Skip to content

fix(server): cache hits preserve harvester user-agent - #5

Merged
KeyCode17 merged 1 commit into
mainfrom
fix/cache-preserve-harvester-ua
May 18, 2026
Merged

fix(server): cache hits preserve harvester user-agent#5
KeyCode17 merged 1 commit into
mainfrom
fix/cache-preserve-harvester-ua

Conversation

@KeyCode17

Copy link
Copy Markdown
Owner

Summary

The cookie bundle stored in the cache had its user_agent field hard-coded to the literal string "px-harvester". On a cache hit, the response body's user_agent field was sourced from bundle.user_agent and therefore returned "px-harvester" instead of the real browser UA that the harvester actually used.

Fix: derive user_agent from outcome.user_agent once in both PxSolveDispatcher::solve and RoutingDispatcher::solve, then thread the same value into both the bundle and the SolveOutput.

Why

Downstream clients that use the returned UA to replay requests (so the TLS ClientHello and the User-Agent header look consistent to PX/CF) were getting silently mis-routed on every cache-hit response: TLS impersonation said Firefox, header said px-harvester, server flagged the mismatch. Concretely this surfaced as a 403 from pedidosya.com.ar/v4/shoplist/vendors on the second and subsequent solve calls while the first call worked.

The first call worked because that path read outcome.user_agent directly into the response body and only masked the issue at the cache boundary.

Test plan

  • cargo fmt --all -- --check
  • cargo clippy --workspace --all-targets --all-features against the lefthook rule set
  • cargo test -p px-server --lib routing — 6/6 passing, including the new bundle_user_agent_matches_harvester regression test
  • Lefthook pre-commit + pre-push hooks pass locally (fmt, clippy, loc≤200, forbidden-patterns, tests)
  • Manual: against a running px-server, repeated curl /v1/solve for the same target — every response now returns the same Firefox UA the Camoufox harvester used, not "px-harvester"

Notes for the reviewer

  • Pure server-side change; no px-cli, px-camoufox, or px-cloudflare touched.
  • The regression test lives next to the existing routing tests so it shares the StaticHandler fixture.
  • This unblocks the consumer pattern of using wreq / curl-impersonate to replay the bundle on the same UA — without this fix, the cached UA always trips the server's UA/JA3 consistency check.

🤖 Generated with Claude Code

`PxSolveDispatcher::solve` and `RoutingDispatcher::solve` were
constructing the `PxCookieBundle` with the literal string
`"px-harvester"` as its `user_agent` field. The non-cached response
path masked this because the body field was sourced from
`outcome.user_agent` (the real browser UA), but the cache stored the
bundle with the placeholder. On the next request for the same target
the cache-hit branch in `solve.rs` reads `bundle.user_agent.clone()`
into `SolveOutput.user_agent`, so the JSON body returned
`"user_agent": "px-harvester"` instead of the harvester's real UA.

Downstream clients that use the returned UA to replay requests (so the
TLS ClientHello and the `User-Agent` header look consistent to PX/CF)
were getting silently mis-routed: TLS said Firefox, header said
`px-harvester`, server flagged it. Concretely this surfaced as a 403
from `pedidosya.com.ar/v4/shoplist/vendors` on the second and later
solve calls while the first call worked.

Fix: derive `user_agent` from `outcome.user_agent` once, then thread
the same value into both the bundle and the `SolveOutput`.

Adds a regression test (`bundle_user_agent_matches_harvester`) that
exercises the dispatcher and asserts both the response UA and the
bundle UA equal the harvester's UA.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@KeyCode17
KeyCode17 merged commit 7b4f879 into main May 18, 2026
6 checks passed
@KeyCode17
KeyCode17 deleted the fix/cache-preserve-harvester-ua branch May 18, 2026 18:51
KeyCode17 added a commit that referenced this pull request May 18, 2026
Lifts workspace.package.version 1.3.0 -> 1.4.0 and aligns the 16
internal workspace.dependencies pins in lockstep.

Changes since 1.3.0:
- fix(server): cache hits preserve harvester user-agent (#5)
- feat(server): /v1/fetch endpoint backed by handler-owned browsers (#6)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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