/* Any copyright is dedicated to the Public Domain.
* https://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const { HttpServer } = ChromeUtils.importESModule(
"resource://testing-common/httpd.sys.mjs"
);
class ObliviousHttpTestRequest {
constructor(method, uri, headers, content) {
this.method = method;
this.uri = uri;
this.headers = headers;
this.content = content;
}
}
class ObliviousHttpTestResponse {
constructor(status, headers, content) {
this.status = status;
this.headers = headers;
this.content = content;
}
}
class ObliviousHttpTestCase {
constructor(request, response) {
this.request = request;
this.response = response;
}
}
add_task(async
function test_oblivious_http() {
let testcases = [
new ObliviousHttpTestCase(
new ObliviousHttpTestRequest(
"GET",
NetUtil.newURI(
"https://example.com"),
{
"X-Some-Header":
"header value" },
""
),
new ObliviousHttpTestResponse(200, {},
"Hello, World!")
),
new ObliviousHttpTestCase(
new ObliviousHttpTestRequest(
"POST",
NetUtil.newURI(
"http://example.test"),
{
"X-Some-Header":
"header value",
"X-Some-Other-Header":
"25" },
"Posting some content..."
),
new ObliviousHttpTestResponse(
418,
{
"X-Teapot":
"teapot" },
"I'm a teapot"
)
),
new ObliviousHttpTestCase(
new ObliviousHttpTestRequest(
"PUT",
NetUtil.newURI(
"http://example.test"),
{
"X-Some-Header":
"header value",
"X-Some-Other-Header":
"25" },
"Putting some content..."
),
new ObliviousHttpTestResponse(
418,
{
"X-Teapot":
"teapot" },
"I'm a teapot"
)
),
new ObliviousHttpTestCase(
new ObliviousHttpTestRequest(
"GET",
NetUtil.newURI(
"http://example.test/404"),
{
"X-Some-Header":
"header value",
"X-Some-Other-Header":
"25" },
""
),
undefined
// 404 relay
),
];
for (let testcase of testcases) {
await run_one_testcase(testcase);
}
});
async
function run_one_testcase(testcase) {
let ohttp = Cc[
"@mozilla.org/network/oblivious-http;1"].getService(
Ci.nsIObliviousHttp
);
let ohttpServer = ohttp.server();
let httpServer =
new HttpServer();
httpServer.registerPathHandler(
"/",
function (request, response) {
let inputStream = Cc[
"@mozilla.org/scriptableinputstream;1"].createInstance(
Ci.nsIScriptableInputStream
);
inputStream.init(request.bodyInputStream);
let requestBody = inputStream.readBytes(inputStream.available());
let ohttpResponse = ohttpServer.decapsulate(stringToBytes(requestBody));
let bhttp = Cc[
"@mozilla.org/network/binary-http;1"].getService(
Ci.nsIBinaryHttp
);
let decodedRequest = bhttp.decodeRequest(ohttpResponse.request);
equal(decodedRequest.method, testcase.request.method);
equal(decodedRequest.scheme, testcase.request.uri.scheme);
equal(decodedRequest.authority, testcase.request.uri.hostPort);
equal(decodedRequest.path, testcase.request.uri.pathQueryRef);
for (
let i = 0;
i < decodedRequest.headerNames.length &&
i < decodedRequest.headerValues.length;
i++
) {
equal(
decodedRequest.headerValues[i],
testcase.request.headers[decodedRequest.headerNames[i]]
);
}
equal(bytesToString(decodedRequest.content), testcase.request.content);
let responseHeaderNames = [
"content-type"];
let responseHeaderValues = [
"text/plain"];
for (let headerName of Object.keys(testcase.response.headers)) {
responseHeaderNames.push(headerName);
responseHeaderValues.push(testcase.response.headers[headerName]);
}
let binaryResponse =
new BinaryHttpResponse(
testcase.response.status,
responseHeaderNames,
responseHeaderValues,
stringToBytes(testcase.response.content)
);
let responseBytes = bhttp.encodeResponse(binaryResponse);
let encResponse = ohttpResponse.encapsulate(responseBytes);
response.setStatusLine(request.httpVersion, 200,
"OK");
response.setHeader(
"Content-Type",
"message/ohttp-res",
false);
response.write(bytesToString(encResponse));
});
httpServer.start(-1);
let ohttpService = Cc[
"@mozilla.org/network/oblivious-http-service;1"
].getService(Ci.nsIObliviousHttpService);
let relayURI = NetUtil.newURI(
`http:
//localhost:${httpServer.identity.primaryPort}/`
);
if (!testcase.response) {
relayURI = NetUtil.newURI(
`http:
//localhost:${httpServer.identity.primaryPort}/404`
);
}
let obliviousHttpChannel = ohttpService
.newChannel(relayURI, testcase.request.uri, ohttpServer.encodedConfig)
.QueryInterface(Ci.nsIHttpChannel);
for (let headerName of Object.keys(testcase.request.headers)) {
obliviousHttpChannel.setRequestHeader(
headerName,
testcase.request.headers[headerName],
false
);
}
if (testcase.request.method ==
"POST" || testcase.request.method ==
"PUT") {
let uploadChannel = obliviousHttpChannel.QueryInterface(
Ci.nsIUploadChannel2
);
ok(uploadChannel);
let bodyStream = Cc[
"@mozilla.org/io/string-input-stream;1"].createInstance(
Ci.nsIStringInputStream
);
bodyStream.setByteStringData(testcase.request.content);
uploadChannel.explicitSetUploadStream(
bodyStream,
null,
-1,
testcase.request.method,
false
);
}
let response = await
new Promise(resolve => {
NetUtil.asyncFetch(obliviousHttpChannel,
function (inputStream) {
let scriptableInputStream = Cc[
"@mozilla.org/scriptableinputstream;1"
].createInstance(Ci.nsIScriptableInputStream);
scriptableInputStream.init(inputStream);
try {
// If decoding failed just return undefined.
inputStream.available();
}
catch (e) {
resolve(undefined);
return;
}
let responseBody = scriptableInputStream.readBytes(
inputStream.available()
);
resolve(responseBody);
});
});
if (testcase.response) {
equal(response, testcase.response.content);
for (let headerName of Object.keys(testcase.response.headers)) {
equal(
obliviousHttpChannel.getResponseHeader(headerName),
testcase.response.headers[headerName]
);
}
}
else {
let relayChannel = obliviousHttpChannel.QueryInterface(
Ci.nsIObliviousHttpChannel
).relayChannel;
equal(relayChannel.responseStatus, 404);
}
await
new Promise(resolve => {
httpServer.stop(resolve);
});
}