Skip to content

fix: resolve translation keys, and fall back to http-post when https-post is rejected - #8

Merged
andrewshell merged 3 commits into
mainfrom
fix/i18n-keys-and-protocol-fallback
Aug 6, 2026
Merged

fix: resolve translation keys, and fall back to http-post when https-post is rejected#8
andrewshell merged 3 commits into
mainfrom
fix/i18n-keys-and-protocol-fallback

Conversation

@andrewshell

@andrewshell andrewshell commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Two independent defects that surfaced in the same screenshot. Split into a commit each, since the
i18n one predates the status table entirely.

1. Translation keys never resolved (e21b6cc)

Minz_Translate::resolveKey() splits a key on every dot and descends one array level per
segment, so a literal 'callback.help' key is unreachable — the lookup enters 'callback', finds a
string, and gives up. Keys that are also parents need their value under '_'. This is why core
spells one of its own keys token_help with an underscore.

26 of the 35 keys here were unreachable and rendered as their raw key: every .help string,
plus callback.invalid and callback.private. All of that predates the status table by a long way;
the table only made it visible, because there you read the value rather than a label you can infer
from context (ext.rsscloud.status.error where "Failed" belonged).

Rewritten in core's nested form. Verified by replicating resolveKey()'s algorithm against the
file: all 35 keys the configuration screen asks for now resolve, including the parent-and-child
pairs status ("Subscriptions"), status.kind ("Type") and status.kind.opml.

2. pleaseNotify protocol fallback (a4293d1)

The protocol parameter names the notification methodhttp-post for REST, as against
xml-rpc or soap — not the scheme of the callback, which is carried by port. We were sending
https-post purely because the instance is TLS. Two real endpoints reject that outright:

https://www.valedalama.net/wp-json/rsscloud/v1/please-notify
  -> Only the http-post (REST) protocol is supported by this endpoint.
http://refarmer.blog/?rsscloud=notify
  -> Only http-post notifications are supported at this time.

Both feeds advertise port="443"/port="80" with protocol="http-post" — the same convention this
extension already applies when reading a <cloud> element. We consumed it one way and produced
another.

The preferred value is now tried first with plain http-post as a fallback, and whichever the
server accepts is recorded so later renewals go straight to it. https-post remains the preference
because it is the path confirmed working against rpc.rsscloud.io; flipping the default would need
evidence about that server this change does not have.

Only a server that actually answered can be objecting to the protocol, so a negative status —
FreshRSS-internal, meaning the request never arrived — does not trigger the fallback. A first
attempt that will be retried logs at debug rather than warning, so a subscription that recovers
on the fallback does not look broken in the log.

One thing a reviewer should know

This does not fix refarmer.blog on its own. Its endpoint is the WordPress rssCloud plugin
(v0.5.0, unmaintained since 2022), which cannot speak HTTPS to a subscriber at all:
notification-request.php builds the callback as domain:port/path and unconditionally prepends
http://, with no code path producing https://. It then challenges http://host:443/…
plaintext against a TLS port — and gets a 400. Any HTTPS FreshRSS is unreachable from that plugin
until it is fixed upstream. This PR removes one of the two blockers.

Verification

php -l over php+phtml clean · PHPUnit 21 tests / 56 assertions · PHPStan level 10 [OK] No errors
· phpcs 13/13 clean. All re-run after reverting an earlier speculative change, not carried over.

RssCloud_Subscriber::protocolCandidates() is a pure static, so the candidate logic is covered
directly without HTTP — including the invariant that http-post stays reachable for every
combination of remembered and callback protocol.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Improved RSS Cloud subscription compatibility by trying supported protocols and remembering the protocol accepted by the server.
    • Added fallback handling for HTTPS callbacks and clearer subscription status updates.
  • Bug Fixes

    • Reset stored protocol details when the RSS Cloud endpoint changes.
    • Improved handling of missing or invalid protocol information.
  • Documentation

    • Updated RSS Cloud status text to indicate when renewal has been attempted.

andrewshell and others added 2 commits August 5, 2026 21:40
Minz_Translate::resolveKey() splits a key on every dot and descends one array
level per segment, so a literal 'callback.help' key can never be reached: the
lookup enters 'callback', finds a string, and gives up. Keys that are also
parents need their own value under '_', which is how core's i18n files are
written -- and why core spells one of its own keys token_help with an
underscore.

26 of the 35 keys here were unreachable and rendered as their raw key. That
included every .help string and callback.invalid / callback.private, all of
which predate the status table by a long way; the table only made it obvious,
because there you read the value rather than a label you can infer from
context.

Verified by replicating resolveKey()'s algorithm against the file: all 35 keys
the configuration screen asks for now resolve, including the parent-and-child
pairs status ("Subscriptions"), status.kind ("Type") and status.kind.opml.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The protocol parameter of pleaseNotify names the notification method --
http-post for REST, as against xml-rpc or soap -- not the scheme of the
callback, which is carried by port. Servers disagree: some accept https-post as
a TLS-flavoured spelling, others take only the value the specification lists.
An HTTPS instance therefore cannot assume either.

Registering with https://www.valedalama.net/feed/ was answered with "Only the
http-post (REST) protocol is supported by this endpoint." That feed advertises
port="443" protocol="http-post", which is the convention this extension already
applies when reading a <cloud> element -- we consumed it that way and produced
something else.

The preferred value is now tried first and plain http-post kept as a fallback,
and the value a server accepts is recorded so later renewals go straight to it
rather than failing the same way every time. https-post stays the preference:
it is the path confirmed working against rpc.rsscloud.io, and flipping the
default would need evidence about that server this change does not have.

Only a server that actually answered can be objecting to the protocol, so a
negative status -- FreshRSS-internal, meaning the request never arrived -- does
not trigger the fallback, and a first attempt that will be retried logs at debug
rather than warning, so a subscription that recovers does not look broken.

remember() forgets the recorded value when the endpoint moves, since a different
server need not accept what the old one did. Existing state files simply have no
recorded value and rediscover it on the next renewal, so nothing needs migrating.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@andrewshell, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 34 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 12375b59-8e50-4913-8315-81870cda019d

📥 Commits

Reviewing files that changed from the base of the PR and between a4293d1 and 1a73ee9.

📒 Files selected for processing (2)
  • RssCloud/Registry.php
  • tests/RssCloud/RegistryTest.php
📝 Walkthrough

Walkthrough

The registry now stores protocol state. The subscriber negotiates protocol candidates, retries failed notifications, persists outcomes, and uses nested RSS Cloud translations. Tests cover state and candidate selection.

Changes

RSS Cloud protocol negotiation

Layer / File(s) Summary
Protocol state persistence
RssCloud/Registry.php, tests/RssCloud/RegistryTest.php
RssCloudState includes protocol. State loading defaults invalid or missing values to ''. Endpoint changes clear the remembered protocol. Tests cover preservation and reset behavior.
Protocol candidate negotiation
RssCloud/Subscriber.php, i18n/en/ext.php, tests/RssCloud/SubscriberTest.php, tests/bootstrap.php
subscribe() tries remembered or callback protocols and uses http-post as a fallback when applicable. Successful and failed attempts persist state and use different log levels. Translations use nested keys, and tests cover candidate selection and fallback behavior.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant RssCloud_Subscriber
  participant RssCloud_Registry
  participant rssCloud_server
  RssCloud_Subscriber->>RssCloud_Registry: Load remembered protocol
  RssCloud_Subscriber->>RssCloud_Subscriber: Build protocolCandidates()
  RssCloud_Subscriber->>rssCloud_server: pleaseNotify with candidate protocol
  rssCloud_server-->>RssCloud_Subscriber: Accept or reject request
  RssCloud_Subscriber->>RssCloud_Registry: Persist accepted protocol or final error
Loading

Poem

A rabbit hops through protocols bright,
Tries remembered paths, then HTTP-post light.
The registry keeps what servers agree,
And clears old paths when endpoints flee.
Tests watch each candidate bound—
Success is saved, failure is found.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes both primary changes: nested translation keys and http-post fallback when https-post is rejected.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/i18n-keys-and-protocol-fallback

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@RssCloud/Registry.php`:
- Around line 85-86: Update the protocol normalization in the Registry
state-loading logic to preserve only the recognized values “http-post” and
“https-post”; map missing, non-string, or any other stored value to an empty
string so unknown protocols cannot become the first pleaseNotify candidate.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 1bd673a0-710b-437c-9c88-1ff6a53b97f4

📥 Commits

Reviewing files that changed from the base of the PR and between 7d3d9fd and a4293d1.

📒 Files selected for processing (6)
  • RssCloud/Registry.php
  • RssCloud/Subscriber.php
  • i18n/en/ext.php
  • tests/RssCloud/RegistryTest.php
  • tests/RssCloud/SubscriberTest.php
  • tests/bootstrap.php

Comment thread RssCloud/Registry.php Outdated
normalise() treats the state file as entirely untrusted, and every other field
is bounded accordingly -- `kind` was tightened to its two known values for this
same reason. `protocol` was the one left accepting any string.

Whatever is stored there is tried first, so a hand-edited or corrupted file
could put an arbitrary value at the head of the candidate list and advertise it
to a cloud server ahead of one known to work. In-process writers were already
safe, since protocolCandidates() only ever returns the two constants; this bounds
the path that does not come from us.

Raised by CodeRabbit on #8.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@andrewshell
andrewshell merged commit 0754b7a into main Aug 6, 2026
4 checks passed
@andrewshell
andrewshell deleted the fix/i18n-keys-and-protocol-fallback branch August 6, 2026 13:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant