-
Notifications
You must be signed in to change notification settings - Fork 26
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1866 from mozilla/1867821/sendbeacon-fallback-upl…
…oader Bug 1867821 - Implement an uploader that defaults to sendBeacon and falls back to…
- Loading branch information
Showing
5 changed files
with
166 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
42 changes: 42 additions & 0 deletions
42
glean/src/platform/browser/sendbeacon_fallback_uploader.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
/* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | ||
|
||
import type PingRequest from "../../core/upload/ping_request.js"; | ||
|
||
import log, { LoggingLevel } from "../../core/log.js"; | ||
import Uploader from "../../core/upload/uploader.js"; | ||
import BrowserFetchUploader from "./fetch_uploader.js"; | ||
import BrowserSendBeaconUploader from "./sendbeacon_uploader.js"; | ||
import { UploadResultStatus } from "../../core/upload/uploader.js"; | ||
import type { UploadResult } from "../../core/upload/uploader.js"; | ||
|
||
const LOG_TAG = "platform.browser.SendBeaconFallbackUploader"; | ||
|
||
class BrowserSendBeaconFallbackUploader extends Uploader { | ||
fetchUploader = BrowserFetchUploader; | ||
sendBeaconUploader = BrowserSendBeaconUploader; | ||
|
||
// eslint-disable-next-line @typescript-eslint/require-await | ||
async post( | ||
url: string, | ||
pingRequest: PingRequest<string | Uint8Array> | ||
): Promise<UploadResult> { | ||
|
||
// Try `sendBeacon` first, | ||
// fall back to `fetch` if `sendBeacon` reports an error. | ||
const beaconStatus = await this.sendBeaconUploader.post(url, pingRequest, false); | ||
if (beaconStatus.result == UploadResultStatus.Success) { | ||
return beaconStatus; | ||
} | ||
log(LOG_TAG, "The `sendBeacon` call was not serviced by the browser. Falling back to the fetch uploader.", LoggingLevel.Warn); | ||
|
||
return this.fetchUploader.post(url, pingRequest); | ||
} | ||
|
||
supportsCustomHeaders(): boolean { | ||
return false; | ||
} | ||
} | ||
|
||
export default new BrowserSendBeaconFallbackUploader(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
104 changes: 104 additions & 0 deletions
104
glean/tests/unit/platform/browser/sendbeacon_fallback_uploader.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
/* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | ||
|
||
import "jsdom-global/register"; | ||
import assert from "assert"; | ||
import sinon from "sinon"; | ||
import nock from "nock"; | ||
import fetch from "node-fetch"; | ||
|
||
import BrowserSendBeaconFallbackUploader from "../../../../src/platform/browser/sendbeacon_fallback_uploader"; | ||
import { UploadResult, UploadResultStatus } from "../../../../src/core/upload/uploader"; | ||
import PingRequest from "../../../../src/core/upload/ping_request"; | ||
|
||
const sandbox = sinon.createSandbox(); | ||
|
||
const MOCK_ENDPOINT = "http://www.example.com"; | ||
|
||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-ignore | ||
// eslint-disable-next-line jsdoc/require-jsdoc | ||
function setGlobalSendBeacon() { | ||
global.navigator.sendBeacon = (url: string, content: string): boolean => { | ||
void fetch(url, { | ||
body: content, | ||
method: "POST", | ||
}); | ||
|
||
return true; | ||
}; | ||
} | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-ignore | ||
global.fetch = fetch; | ||
|
||
describe("Uploader/BrowserSendBeaconFallback", function () { | ||
beforeEach(function() { | ||
setGlobalSendBeacon(); | ||
}); | ||
|
||
afterEach(function () { | ||
sandbox.restore(); | ||
}); | ||
|
||
it("returns the correct status for successful requests", async function () { | ||
const TEST_PING_CONTENT = {"my-test-value": 40721}; | ||
for (const status of [200, 400, 500]) { | ||
nock(MOCK_ENDPOINT).post(/./i, body => { | ||
return JSON.stringify(body) == JSON.stringify(TEST_PING_CONTENT); | ||
}).reply(status); | ||
|
||
const response = BrowserSendBeaconFallbackUploader.post(MOCK_ENDPOINT, new PingRequest("abc", {}, JSON.stringify(TEST_PING_CONTENT), 1024)); | ||
// When using sendBeacon, we can't really tell if something was correctly uploaded | ||
// or not. All we can know is if the request was enqueued, so we always expect 200. | ||
const expectedResponse = new UploadResult(UploadResultStatus.Success, 200); | ||
assert.deepStrictEqual( | ||
await response, | ||
expectedResponse | ||
); | ||
} | ||
}); | ||
|
||
it("returns the correct status after fallback", async function () { | ||
const TEST_PING_CONTENT = {"my-test-value": 40721}; | ||
nock(MOCK_ENDPOINT).post(/./i).reply(200); | ||
|
||
// Reset `fetch` to a known state. | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-ignore | ||
global.fetch = fetch; | ||
|
||
// Ensure `sendBeacon` fails. | ||
global.navigator.sendBeacon = () => false; | ||
|
||
const response = BrowserSendBeaconFallbackUploader.post(MOCK_ENDPOINT, new PingRequest("abc", {}, JSON.stringify(TEST_PING_CONTENT), 1024)); | ||
const expectedResponse = new UploadResult(UploadResultStatus.Success, 200); | ||
assert.deepStrictEqual( | ||
await response, | ||
expectedResponse | ||
); | ||
}); | ||
|
||
it("returns the correct status when both uploads fail", async function () { | ||
nock(MOCK_ENDPOINT).post(/./i).replyWithError({ | ||
message: "something awful happened", | ||
code: "AWFUL_ERROR", | ||
}); | ||
|
||
// Reset `fetch` to a known state. | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-ignore | ||
global.fetch = fetch; | ||
|
||
// Ensure `sendBeacon` fails. | ||
global.navigator.sendBeacon = () => false; | ||
|
||
const response = BrowserSendBeaconFallbackUploader.post(MOCK_ENDPOINT, new PingRequest("abc", {}, "{}", 1024)); | ||
const expectedResponse = new UploadResult(UploadResultStatus.RecoverableFailure); | ||
assert.deepStrictEqual( | ||
await response, | ||
expectedResponse | ||
); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters