Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

* [#696](https://github.com/mozilla/glean.js/pull/696): Expose Node.js entry point `@mozilla/glean/node`.
* [#695](https://github.com/mozilla/glean.js/pull/695): Implement PlatformInfo module for the Node.js platform.
* [#695](https://github.com/mozilla/glean.js/pull/730): Implement Uploader module for the Node.js platform.


# v0.19.0 (2021-09-03)

Expand Down
82 changes: 55 additions & 27 deletions glean/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions glean/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@
"jsonschema": "^1.4.0",
"madge": "^5.0.1",
"mocha": "^9.0.2",
"nock": "^13.1.3",
"npm-run-all": "^4.1.5",
"request": "^2.88.2",
"request-promise-native": "^1.0.9",
Expand Down
6 changes: 4 additions & 2 deletions glean/src/platform/node/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@
* 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 PlatformInfo from "./platform_info.js";
import type Platform from "../index.js";
import PlatformInfo from "./platform_info.js";
import Uploader from "./uploader.js";

// We will still use the TestPlatform as a placeholder
// for the other Node.js modules that have not been implemented yet.
// See Bug 1728810 (Uploader) and Bug 1728807 (Storage).
// See Bug 1728807 (Storage).
import TestPlatform from "../test/index.js";

const NodePlatform: Platform = {
...TestPlatform,
uploader: Uploader,
info: PlatformInfo,
name: "node"
};
Expand Down
63 changes: 63 additions & 0 deletions glean/src/platform/node/uploader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/* 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 https from "https";
import http from "http";

import log, { LoggingLevel } from "../../core/log.js";
import Uploader, {
UploadResult,
UploadResultStatus,
DEFAULT_UPLOAD_TIMEOUT_MS
} from "../../core/upload/uploader.js";

const LOG_TAG = "platform.node.Uploader";

class NodeUploader extends Uploader {
post(url: string, body: string | Uint8Array, headers?: Record<string, string>): Promise<UploadResult> {
return new Promise(resolve => {
// We can trust that the URL is valid,
// since it is validated upon Glean initialzation
// TODO: Bug 1730496
const parsedURL = new URL(url);
const mod = parsedURL.protocol === "http:" ? http : https;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess there's an interesting discussion to be had about allowing un-TLS'd connections in Glean to non-local hosts, but this isn't the time or place.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There will be some validation soon https://bugzilla.mozilla.org/show_bug.cgi?id=1730496#c1

const request = mod.request(
{
hostname: parsedURL.hostname,
path: parsedURL.pathname,
port: parsedURL.port,
headers,
method: "POST",
timeout: DEFAULT_UPLOAD_TIMEOUT_MS
},
response => {
response.resume();
response.once("end", () => {
resolve(new UploadResult(UploadResultStatus.Success, response.statusCode));
});
}
);

request.on("timeout", () => {
log(LOG_TAG, "Timeout while attempting to upload ping.", LoggingLevel.Error);
request.destroy();
resolve(new UploadResult(UploadResultStatus.RecoverableFailure));
});

request.on("error", error => {
log(
LOG_TAG,
[ "Network error while attempting to upload ping.\n", error.message ],
LoggingLevel.Error
);
resolve(new UploadResult(UploadResultStatus.RecoverableFailure));
});

// Finish sending the request.
request.end(body);
});
}
}

export default new NodeUploader();
2 changes: 1 addition & 1 deletion glean/src/platform/qt/uploader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class QtUploader extends Uploader {
};

xhr.onerror = function(e) {
log(LOG_TAG, ["Network rror while attempting to upload ping.\n", e.type], LoggingLevel.Error);
log(LOG_TAG, ["Network error while attempting to upload ping.\n", e.type], LoggingLevel.Error);
resolve(new UploadResult(UploadResultStatus.RecoverableFailure));
};

Expand Down
55 changes: 55 additions & 0 deletions glean/tests/unit/platform/node/uploader.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/* 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 assert from "assert";
import nock from "nock";

import NodeUploader from "../../../../src/platform/node/uploader";
import { DEFAULT_UPLOAD_TIMEOUT_MS, UploadResult, UploadResultStatus } from "../../../../src/core/upload/uploader";

const MOCK_ENDPOINT = "http://www.example.com";

describe("Uploader/Node", function () {
afterEach(function () {
nock.cleanAll();
});

it("returns the correct status for successful requests", async function () {
for (const status of [200, 400, 500]) {
nock(MOCK_ENDPOINT).post(/./i).reply(status);

const response = NodeUploader.post(MOCK_ENDPOINT, "");
const expectedResponse = new UploadResult(UploadResultStatus.Success, status);
assert.deepStrictEqual(
await response,
expectedResponse
);
}
});

it("returns the correct status for timeout requests", async function () {
nock(MOCK_ENDPOINT).post(/./i).delay(DEFAULT_UPLOAD_TIMEOUT_MS + 1).reply(500);

const response = NodeUploader.post(MOCK_ENDPOINT, "");
const expectedResponse = new UploadResult(UploadResultStatus.RecoverableFailure);
assert.deepStrictEqual(
await response,
expectedResponse
);
});

it("returns the correct status for request errors", async function () {
nock(MOCK_ENDPOINT).post(/./i).replyWithError({
message: "something awful happened",
code: "AWFUL_ERROR",
});

const response = NodeUploader.post(MOCK_ENDPOINT, "");
const expectedResponse = new UploadResult(UploadResultStatus.RecoverableFailure);
assert.deepStrictEqual(
await response,
expectedResponse
);
});
});