From f590efa63e1bedcb39eb135fd2a8e272b0fa320f Mon Sep 17 00:00:00 2001 From: Dan Stillman Date: Mon, 5 Oct 2026 14:14:37 -0400 Subject: [PATCH] Send cookies and Referer from HTTP.download() Since the switch to fetch() in 0fe31b0f04 (Zotero 10.0.0), download requests didn't use cookies, and the Referer header was silently dropped as a forbidden header. Sites that check either -- e.g., IEEE Xplore, which returns a 502 -- failed during Find Full Text. https://forums.zotero.org/discussion/133703/ (cherry picked from commit 364181f4e7fcd43eca7b972862a1c060ef95248d) --- chrome/content/zotero/xpcom/http.js | 9 +++++++ test/tests/httpTest.js | 38 +++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/chrome/content/zotero/xpcom/http.js b/chrome/content/zotero/xpcom/http.js index 978095c6a1..629b04c4de 100644 --- a/chrome/content/zotero/xpcom/http.js +++ b/chrome/content/zotero/xpcom/http.js @@ -707,7 +707,16 @@ Zotero.HTTP = new function () { let encoded = btoa(String.fromCharCode(...bytes)); headers.set('Authorization', `Basic ${encoded}`); } + // fetch() drops Referer as a forbidden header, so pass it as the request referrer + let referrer = headers.get('Referer'); + if (referrer) { + headers.delete('Referer'); + fetchOptions.referrer = referrer; + fetchOptions.referrerPolicy = 'unsafe-url'; + } fetchOptions.headers = headers; + // Send cookies, except for requests with embedded credentials, matching request() + fetchOptions.credentials = ctx.username ? 'omit' : 'include'; // Start the request with a connect timeout let connectTimerID; diff --git a/test/tests/httpTest.js b/test/tests/httpTest.js index 3f9b6a2da5..4c0c00d0d0 100644 --- a/test/tests/httpTest.js +++ b/test/tests/httpTest.js @@ -450,6 +450,20 @@ describe("Zotero.HTTP", function () { } } ); + httpd.registerPathHandler( + '/download/referer', + { + handle: function (request, response) { + response.setStatusLine(null, 200, "OK"); + response.setHeader( + "X-Echo", + request.hasHeader("Referer") ? request.getHeader("Referer") : "", + false + ); + response.write("ok"); + } + } + ); httpd.registerPathHandler( '/download/auth', { @@ -515,6 +529,30 @@ describe("Zotero.HTTP", function () { assert.equal(req.headers.get("X-Echo"), "test-value"); }); + it("should send a Referer header", async function () { + let dest = PathUtils.join(tmpDir, "referer.bin"); + let referrer = baseURL + "article"; + let req = await Zotero.HTTP.download( + baseURL + "download/referer", + dest, + { + headers: { Referer: referrer } + } + ); + assert.equal(req.headers.get("X-Echo"), referrer); + }); + + it("should send cookies", async function () { + Services.cookies.removeAll(); + let url = baseURL + "cookie-check"; + // Set the cookie + await Zotero.HTTP.request('GET', url, { successCodes: false }); + + let dest = PathUtils.join(tmpDir, "cookie.bin"); + let req = await Zotero.HTTP.download(url, dest, { successCodes: false }); + assert.equal(req.status, 200); + }); + it("should throw UnexpectedStatusException for non-success status", async function () { let dest = PathUtils.join(tmpDir, "404.bin"); let e = await getPromiseError(