tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 6225c88e92cc2f2d71265600329aa1191aa76290
parent 2f7bfd630e4eb2ce47db838f6c9fc2b166db4231
Author: Mike Conley <mconley@mozilla.com>
Date:   Wed, 10 Dec 2025 19:43:49 +0000

Bug 2005277 - Convert Headers object into a JS key-value object when calling into ObliviousHTTP from DiscoveryStreamFeed. r=home-newtab-reviewers,nbarrett

Differential Revision: https://phabricator.services.mozilla.com/D275832

Diffstat:
Mbrowser/extensions/newtab/lib/DiscoveryStreamFeed.sys.mjs | 11+++++++++++
Mbrowser/extensions/newtab/test/unit/lib/DiscoveryStreamFeed.test.js | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 65 insertions(+), 0 deletions(-)

diff --git a/browser/extensions/newtab/lib/DiscoveryStreamFeed.sys.mjs b/browser/extensions/newtab/lib/DiscoveryStreamFeed.sys.mjs @@ -452,6 +452,17 @@ export class DiscoveryStreamFeed { ); return null; } + + // ObliviousHTTP.ohttpRequest only accepts a key/value object, and not + // a Headers instance. We normalize any headers to a key/value object. + // + // We use instanceof here since isInstance isn't available for node + // tests like DiscoveryStreamFeed.test.js. + // eslint-disable-next-line mozilla/use-isInstance + if (options.headers && options.headers instanceof Headers) { + options.headers = Object.fromEntries(options.headers); + } + fetchPromise = lazy.ObliviousHTTP.ohttpRequest( ohttpRelayURL, config, diff --git a/browser/extensions/newtab/test/unit/lib/DiscoveryStreamFeed.test.js b/browser/extensions/newtab/test/unit/lib/DiscoveryStreamFeed.test.js @@ -300,6 +300,60 @@ describe("DiscoveryStreamFeed", () => { DUMMY_ENDPOINT ); }); + + it("should cast headers from a Headers object to JS object when using OHTTP", async () => { + sandbox + .stub(global.Services.prefs, "getStringPref") + .withArgs( + "browser.newtabpage.activity-stream.discoverystream.ohttp.relayURL" + ) + .returns("https://relay.url") + .withArgs( + "browser.newtabpage.activity-stream.discoverystream.ohttp.configURL" + ) + .returns("https://config.url"); + + const fakeOhttpConfig = { config: "config" }; + sandbox + .stub(global.ObliviousHTTP, "getOHTTPConfig") + .resolves(fakeOhttpConfig); + + const ohttpResponse = { + json: () => Promise.resolve("ohttp response"), + ok: true, + }; + const ohttpRequestStub = sandbox + .stub(global.ObliviousHTTP, "ohttpRequest") + .resolves(ohttpResponse); + + // Allow the endpoint + feed.store.getState = () => ({ + Prefs: { + values: { + [ENDPOINTS_PREF_NAME]: DUMMY_ENDPOINT, + }, + }, + }); + + const headers = new Headers(); + headers.set("headername", "headervalue"); + + const result = await feed.fetchFromEndpoint( + DUMMY_ENDPOINT, + { headers }, + true + ); + + assert.equal(result, "ohttp response"); + assert.calledOnce(ohttpRequestStub); + assert.calledWithMatch( + ohttpRequestStub, + "https://relay.url", + fakeOhttpConfig, + DUMMY_ENDPOINT, + { headers: Object.fromEntries(headers), credentials: "omit" } + ); + }); }); describe("#getOrCreateImpressionId", () => {