Skip to content

fix(cookie): support Netscape cookies.txt and auto-refresh XSRF token - #88

Open
usmanovbf wants to merge 17 commits into
Sophomoresty:mainfrom
usmanovbf:fix/cookie-file-xsrf-refresh
Open

fix(cookie): support Netscape cookies.txt and auto-refresh XSRF token#88
usmanovbf wants to merge 17 commits into
Sophomoresty:mainfrom
usmanovbf:fix/cookie-file-xsrf-refresh

Conversation

@usmanovbf

@usmanovbf usmanovbf commented Aug 24, 2026

Copy link
Copy Markdown

Summary

  • Parse Netscape-format cookies.txt directly in load_cookie() (single-file and modular) - no manual conversion
  • Fetch the XSRF token (SNlM0e) from the app page at startup and auto-refresh BL + XSRF on HTTP 400/405 retries, so cookie sessions survive token rotation without a restart
  • Detect the new JSPB BardErrorInfo",[code] error envelope (old format still matched) and surface known codes as clear messages instead of silent content: null
  • Drop stale 200/50-char length heuristics that discarded valid short wrb.fr payloads
  • SSE streams end with a finish chunk + [DONE] on upstream failure, so clients never hang
  • Fail fast on hard rejections (1060 IP block, 1037 quota) but keep retries for transient 1013
  • Return 400 (not 500) for invalid JSON request bodies; harden the cookie cache key with file size
  • Port the extra-fields model mechanism (gemini-3.1-pro-enhanced) to the single-file build - both builds now list the same 9 models
  • Add probe_upstream.py (one-shot reachability check) and README/README_CN updates

Problem

Running with a Netscape cookie file failed twice over:

  1. load_cookie() only understood JSON {"cookie": ...} or a raw k=v; k2=v2 string, so the whole tab-separated file was sent upstream as the Cookie header -> Invalid header value.
  2. Authenticated StreamGenerate now requires the XSRF token (at = SNlM0e from the app page). The code could send it but never fetched it -> HTTP 400 with an xsrf error body.
  3. Long-running servers died on token rotation until restart.
  4. Google changed the error envelope to JSPB; the old regex missed it, so upstream rejections (e.g. 1060 IP block) returned HTTP 200 with content: null instead of an error.

Testing

  • 23/23 unit + live-server integration tests (python -m unittest discover -s tests), including new coverage for both error formats, short payloads, SSE error finish chunks and the mocked success path
  • End-to-end against live Gemini with a real cookies.txt: non-streaming and streaming, single-file and modular builds (successful generations verified; later runs hit upstream 1060 from the test machine and were verified to report it correctly - HTTP 502 with a clear message, [error] finish chunk for streams)
  • Simulated token expiry by corrupting xsrf_token mid-session: both builds auto-recovered in ~4s

Bu added 17 commits August 25, 2026 00:21
- Parse Netscape-format cookie files (tab-separated cookies.txt) instead
  of sending raw file contents as the Cookie header
- Extract SNlM0e XSRF token from the app page at startup when cookies
  are configured; required by StreamGenerate for authenticated requests
- On HTTP 400/405, refresh BL and XSRF token mid-retry and rebuild the
  request so long-running servers survive token rotation
- Sync modular package (refresh_bl_and_xsrf) with single-file script
- Google changed the upstream error envelope from 'BardErrorInfo [code]'
  to JSPB 'application.BardErrorInfo",[code]'; the old regex missed it,
  so rejected requests returned HTTP 200 with content:null instead of an
  error
- Map known codes to human-readable hints (1060 = IP temporarily blocked
  / unsupported region, 1037 = usage limit, 1013 = transient)
- Drop the 200-char line-length and 50-char payload heuristics that
  silently skipped valid short wrb.fr payloads
- Streaming SSE responses now end with a finish chunk on error so
  clients do not hang on dropped streams
- Add tests/test_upstream_errors.py covering both error formats and the
  short-line regression
StreamGenerate streaming paths retried BardErrorInfo rejections three
times pointlessly - an IP block (1060) or quota rejection (1037) does
not clear within a retry loop. Raise immediately instead; connection
and transient errors keep their retry behaviour. 1060 responses now
return in ~1s instead of after the full retry cycle.
Malformed JSON fell through the generic handler and produced HTTP 500;
clients cannot distinguish their own bad request from a server fault.
Matches the modular package, which already answers 400 invalid JSON.
Live-server test that a failing generate_stream (BardErrorInfo 1060)
yields the partial content, an [error] finish chunk and [DONE], so
clients terminate instead of hanging on a dropped stream.
Feed server.generate() a Russian answer and assert the full OpenAI
completion shape survives JSON encoding end to end.
mtime-only caching can miss a same-second rewrite on filesystems with
1s timestamp resolution; (mtime, size) catches content changes in one
write.
Anonymous StreamGenerate probe; prints blocked/UNBLOCKED and exits
nonzero while the IP is blocked. Useful to tell 'Google blocked this
IP' apart from a proxy regression before debugging code.
The modular package gained an extra-fields mechanism (inner[31]/inner[80]
payload overrides for the enhanced Pro variant) that the single-file
script never received, leaving the two builds listing different models
(8 vs 9). Thread extra_fields through resolve/generate/stream paths and
widen the payload array to 102 slots, matching the package.
The fail-fast guard stopped retrying every BardErrorInfo, but 1013 is
documented as transient and clears on retry; only hard rejections
(1060 IP block, 1037 quota) skip the retry loop.
Retrying an IP rate limit amplifies it. Surface 429 immediately with
guidance so clients back off, matching the fail-fast behavior used for
hard BardErrorInfo rejections.
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