Skip to content

fix: send browser User-Agent from galaxynovels fetch wrappers (#2442) - #2467

Open
RibatTRW wants to merge 2 commits into
lnreader:masterfrom
RibatTRW:issue-2442
Open

fix: send browser User-Agent from galaxynovels fetch wrappers (#2442)#2467
RibatTRW wants to merge 2 commits into
lnreader:masterfrom
RibatTRW:issue-2442

Conversation

@RibatTRW

@RibatTRW RibatTRW commented Aug 25, 2026

Copy link
Copy Markdown

What

Galaxy Novels (galaxynovels, plugins/arabic/galaxynovels.ts, version
1.1.0) shows the novel listing, but opening any chapter fails with "Could not
reach site (403)". User report: 2026-08-19, LNReader 2.1.0, Android 13.
Bug label, severity Other.

Why: root cause

The plugin does all fetching through the shared fetchApi
(src/lib/fetch.ts). Its makeInit sends: Connection, Accept,
Accept-Language, Sec-Fetch-Mode, Accept-Encoding. It sends no User-Agent.
galaxynovels.com is fronted by Cloudflare with a bot-fight rule that returns
403 on every path except the /novels/ listing when the request has no
User-Agent (curl-like UAs are blocked too). Browser-like UAs return 200
(verified live 2026-08-25, exact makeInit header set).

Failure flow inside the plugin: parseChapter calls the chapter API first, it
403s, the catch swallows it, the HTML fallback 403s, fetchHtml throws "Could
not reach site (403)". That is the exact user-facing error.

Design ruling (spec-2442 D1): fix per-plugin, not in the shared lib. Adding
the UA in makeInit would change behavior for every source, out of this
issue's blast radius.

How

  • plugins/arabic/galaxynovels.ts: add private static readonly BROWSER_UA
    (exact Android Chrome UA verified in the probe matrix) and pass
    { headers: { 'User-Agent': GalaxyNovels.BROWSER_UA } } in both fetch
    wrappers. Every call site flows through one of the two wrappers (D2).
  • Version 1.1.0 -> 1.2.0 (D5) so cached installs refetch.
  • HTML fallback intentionally untouched (D4): chapter HTML pages are
    Cloudflare-blocked even with a browser UA (verified); the API path is the
    only viable read path today.

Tests

Deterministic, no network: tests/regression-2442.mjs +
tests/fixtures/2442/ (captured 2026-08-25). The global fetch stub emulates
the Cloudflare rule: no User-Agent header -> HTTP 403; otherwise it serves
the matching fixture by URL. REQ-1 (every fetch init recorded during the run
carries a User-Agent) is the regression pin.

node tests/regression-2442.mjs
Check Before (e1dcd06) After
REQ-1 every fetch init carries a User-Agent header FAIL (5 of 5 UA-less) PASS
N-1..N-6 parseNovel (name, cover, author, status, genres, 5 chapters) FAIL (403) PASS
C-1 parseChapter content (>= 200, starts with الفصل 1) FAIL (403) PASS
C-2 403 rejects with /Could not reach site (403)/ PASS PASS
C-3 chapter API was fetched (no silent fallback) PASS PASS

Baseline: 8 FAILING (RED, bug reproduced at e1dcd06) -> after fix: 10/10
ALL GREEN.

Live check (node scripts/live-check-plugin.js "plugins/arabic/galaxynovels.ts", 2026-08-25): popularNovels PASS (20),
searchNovels PASS (1), parseNovel PASS (488 chapters), parseChapter PASS
(3743 chars).

Gates

  • npx prettier --check plugins/arabic/galaxynovels.ts tests/regression-2442.mjs: clean. Note: 5 pre-existing prettier drift spots in galaxynovels.ts were normalized (pure formatting, no semantic change; the suite stays 10/10) so the mandatory prettier gate can pass.
  • npx eslint plugins/arabic/galaxynovels.ts: clean.
  • npx tsc --noEmit: 23 pre-existing errors, all in untouched plugins (mvlempyr, mynovels, novelight, and others); zero in galaxynovels.ts and zero in tests/.
  • No lockfile churn. No generated files. Shared lib untouched.

Note for reviewer

Closes #2442


Automated contribution via the LNReader Dev Ops pipeline (builder-bob, Hermes Agent).

…er#2442)

Galaxy Novels chapter reads fail with 403 because Cloudflare blocks the
UA-less requests the shared fetchApi sends. Add a BROWSER_UA constant and
pass it as a header in both fetch wrappers (fetchHtml, fetchJson); bump
plugin version from 1.1.0 to 1.2.0 so cached installs refetch.

Test: tests/regression-2442.mjs runs the real plugin against saved live
fixtures through a Cloudflare-emulating fetch stub (no UA -> 403). RED on
base e1dcd06: 8 failing (REQ-1, N-1..N-6, C-1). GREEN after fix: 10/10.
…ape (F-STD-1/F-STD-2)

try/finally around the regression-bundle.cjs write/require/unlink sequence so a crashed run can no longer leave the untracked artifact behind (.gitignore does not cover .cjs); escape \d as \\d in the N-6 FAIL banner template literal so the escape is lint-clean and the banner prints /chapter-\d+/.

Review nits from t_fcfaa066 (PR lnreader#2467), fix card t_9d621b7c, issue lnreader#2442. Suite unchanged: ALL GREEN 10/10.
@RibatTRW

Copy link
Copy Markdown
Author

Hygiene follow-up from review card t_fcfaa066 pushed as 74b11d7 on issue-2442:

  • F-STD-1 — the regression-bundle.cjs temp-bundle write/require/unlink sequence in tests/regression-2442.mjs is now wrapped in try/finally, so a crashed/killed run can no longer leave the artifact behind (git check-ignore regression-bundle.cjs = NOT ignored: .gitignore's .js entry is a literal basename and this is .cjs). Verified empirically by injecting a throw at the require site in a scratch copy of the suite: process exits non-zero AND the temp file is removed; a normal run leaves git status --porcelain clean.
  • F-STD-2\d\\d in the N-6 FAIL banner template literal (line 135). eslint no-useless-escape at 135:55 cleared; the remaining 12 errors are the known base-config no-undef gap for .mjs, fixed by fix: restore Riwyat covers and chapter content (#2465) #2466 commit 631dca9 as documented.

Gates re-run locally: node tests/regression-2442.mjs → ALL GREEN 10/10, exit 0; prettier clean; eslint shows only the 12 documented no-undef errors.

Consistency note: tests/regression-2465.mjs (#2466) has the identical unguarded temp-file pattern — left untouched here per fix-card scope; flagging for whoever next touches that file.

@RibatTRW
RibatTRW marked this pull request as ready for review August 25, 2026 13:54
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.

Can't read or download chapters in this galaxy novel plugin

1 participant