Source code
Revision control
Copy as Markdown
Other Tools
Test Info:
/* 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,
"use strict";
const { AddonTestUtils } = ChromeUtils.importESModule(
);
AddonTestUtils.maybeInit(this);
const server = AddonTestUtils.createHttpServer({ hosts: ["example.com"] });
const ohttp = Cc["@mozilla.org/network/oblivious-http;1"].getService(
Ci.nsIObliviousHttp
);
const ohttpServer = ohttp.server();
const serverContext = {
statusCode: 0,
configBody: ohttpServer.encodedConfig,
failure: false,
};
server.registerPathHandler(
new URL(API_OHTTP_CONFIG).pathname,
(request, response) => {
const bstream = Cc["@mozilla.org/binaryoutputstream;1"].createInstance(
Ci.nsIBinaryOutputStream
);
bstream.setOutputStream(response.bodyOutputStream);
bstream.writeByteArray(serverContext.configBody);
}
);
let requestPromise, resolveRequest;
let responsePromise, resolveResponse;
server.registerPathHandler(
new URL(API_OHTTP_RELAY).pathname,
async (request, response) => {
const inputStream = Cc["@mozilla.org/binaryinputstream;1"].createInstance(
Ci.nsIBinaryInputStream
);
inputStream.setInputStream(request.bodyInputStream);
const requestBody = inputStream.readByteArray(inputStream.available());
const ohttpRequest = ohttpServer.decapsulate(requestBody);
const bhttp = Cc["@mozilla.org/network/binary-http;1"].getService(
Ci.nsIBinaryHttp
);
const decodedRequest = bhttp.decodeRequest(ohttpRequest.request);
response.processAsync();
if (serverContext.failure) {
response.setStatusLine(request.httpVersion, 500, "Internal Server Error");
response.finish();
resolveRequest(decodedRequest);
return;
}
const BinaryHttpResponse = {
status: serverContext.statusCode,
headerNames: [],
headerValues: [],
content: new TextEncoder().encode(decodedRequest.content),
QueryInterface: ChromeUtils.generateQI(["nsIBinaryHttpResponse"]),
};
const encResponse = ohttpRequest.encapsulate(
bhttp.encodeResponse(BinaryHttpResponse)
);
response.setStatusLine(request.httpVersion, 200, "OK");
response.setHeader("Content-Type", "message/ohttp-res", false);
const bstream = Cc["@mozilla.org/binaryoutputstream;1"].createInstance(
Ci.nsIBinaryOutputStream
);
bstream.setOutputStream(response.bodyOutputStream);
bstream.writeByteArray(encResponse);
response.finish();
resolveRequest(decodedRequest);
}
);
function resetPromises() {
const tmp1 = Promise.withResolvers();
requestPromise = tmp1.promise;
resolveRequest = tmp1.resolve;
const tmp2 = Promise.withResolvers();
responsePromise = tmp2.promise;
resolveResponse = tmp2.resolve;
}
async function test_success() {
resetPromises();
const request = {
method: "POST",
scheme: "https",
authority: "example.com",
path: "/my-path",
headerNames: ["User-Agent"],
headerValues: ["Mozilla/5.0"],
content: "Hello, world!",
};
const expectedResponse = {
url: `${request.scheme}://${request.authority}${request.path}`,
statusCode: 42,
error: "",
};
serverContext.statusCode = expectedResponse.statusCode;
const ohttpClientTester = Cc[
"@mozilla.org/ohttp-client-test;1"
].createInstance(Ci.nsIOhttpClientTest);
ohttpClientTester.fetch(
`${request.scheme}://${request.authority}${request.path}`,
request.method,
request.content,
request.headerNames,
request.headerValues,
(url, statusCode, headerKeys, headerValues, error) => {
resolveResponse({
url,
statusCode,
headerKeys,
headerValues,
error,
});
}
);
const [ohttpRequest, ohttpResponse] = await Promise.all([
requestPromise,
responsePromise,
]);
// Verify request
Assert.equal(ohttpRequest.method, request.method);
Assert.equal(ohttpRequest.scheme, request.scheme);
Assert.equal(ohttpRequest.authority, request.authority);
Assert.equal(ohttpRequest.path, request.path);
Assert.deepEqual(ohttpRequest.headerNames, request.headerNames);
Assert.deepEqual(ohttpRequest.headerValues, request.headerValues);
Assert.deepEqual(
ohttpRequest.content,
request.content.split("").map(s => s.charCodeAt(0))
);
// Verify response
Assert.equal(ohttpResponse.url, expectedResponse.url);
Assert.equal(ohttpResponse.statusCode, expectedResponse.statusCode);
Assert.equal(ohttpResponse.error, expectedResponse.error);
}
async function test_invalid_config() {
resetPromises();
const request = {
method: "POST",
scheme: "https",
authority: "example.com",
path: "/my-path",
headerNames: ["User-Agent"],
headerValues: ["Mozilla/5.0"],
content: "Hello, world!",
};
const expectedResponse = {
url: "",
statusCode: 0,
error: "Request failed, error=0x11, category=0x1",
};
// Provide invalid config
// We still set status code to verify that we don't
// get back 42 as the status.
serverContext.configBody = [0, 0, 0, 0];
serverContext.statusCode = 42;
const ohttpClientTester = Cc[
"@mozilla.org/ohttp-client-test;1"
].createInstance(Ci.nsIOhttpClientTest);
ohttpClientTester.fetch(
`${request.scheme}://${request.authority}${request.path}`,
request.method,
request.content,
request.headerNames,
request.headerValues,
(url, statusCode, headerKeys, headerValues, error) => {
resolveResponse({
url,
statusCode,
headerKeys,
headerValues,
error,
});
}
);
// Request promise never resolves as getting config fails
const ohttpResponse = await responsePromise;
// Verify response
Assert.equal(ohttpResponse.url, expectedResponse.url);
Assert.equal(ohttpResponse.statusCode, expectedResponse.statusCode);
Assert.equal(ohttpResponse.error, expectedResponse.error);
}
async function test_ohttp_failure() {
resetPromises();
const request = {
method: "POST",
scheme: "https",
authority: "example.com",
path: "/my-path",
headerNames: ["User-Agent"],
headerValues: ["Mozilla/5.0"],
content: "Hello, world!",
};
const expectedResponse = {
url: "",
statusCode: 0,
error: "Request failed, error=0x11, category=0x1",
};
// Provide a valid config, but make the server fail.
// We still set status code to verify that we don't
// get back 42 as the status.
serverContext.configBody = ohttpServer.encodedConfig;
serverContext.statusCode = 42;
serverContext.failure = true;
const ohttpClientTester = Cc[
"@mozilla.org/ohttp-client-test;1"
].createInstance(Ci.nsIOhttpClientTest);
ohttpClientTester.fetch(
`${request.scheme}://${request.authority}${request.path}`,
request.method,
request.content,
request.headerNames,
request.headerValues,
(url, statusCode, headerKeys, headerValues, error) => {
resolveResponse({
url,
statusCode,
headerKeys,
headerValues,
error,
});
}
);
const [ohttpRequest, ohttpResponse] = await Promise.all([
requestPromise,
responsePromise,
]);
// Verify request
Assert.equal(ohttpRequest.method, request.method);
Assert.equal(ohttpRequest.scheme, request.scheme);
Assert.equal(ohttpRequest.authority, request.authority);
Assert.equal(ohttpRequest.path, request.path);
Assert.deepEqual(ohttpRequest.headerNames, request.headerNames);
Assert.deepEqual(ohttpRequest.headerValues, request.headerValues);
Assert.deepEqual(
ohttpRequest.content,
request.content.split("").map(s => s.charCodeAt(0))
);
// Verify response
Assert.equal(ohttpResponse.url, expectedResponse.url);
Assert.equal(ohttpResponse.statusCode, expectedResponse.statusCode);
Assert.equal(ohttpResponse.error, expectedResponse.error);
}
add_task(async function run_tests() {
await test_success();
await test_invalid_config();
await test_ohttp_failure();
});