Merge autoland to mozilla-central. a=merge
[gecko.git] / netwerk / test / unit / test_oblivious_http.js
blob4b0af2611b697a2c5fabbac95d971293688abedf
1 /* Any copyright is dedicated to the Public Domain.
2  * https://creativecommons.org/publicdomain/zero/1.0/ */
4 "use strict";
6 const { HttpServer } = ChromeUtils.importESModule(
7   "resource://testing-common/httpd.sys.mjs"
8 );
10 class ObliviousHttpTestRequest {
11   constructor(method, uri, headers, content) {
12     this.method = method;
13     this.uri = uri;
14     this.headers = headers;
15     this.content = content;
16   }
19 class ObliviousHttpTestResponse {
20   constructor(status, headers, content) {
21     this.status = status;
22     this.headers = headers;
23     this.content = content;
24   }
27 class ObliviousHttpTestCase {
28   constructor(request, response) {
29     this.request = request;
30     this.response = response;
31   }
34 add_task(async function test_oblivious_http() {
35   let testcases = [
36     new ObliviousHttpTestCase(
37       new ObliviousHttpTestRequest(
38         "GET",
39         NetUtil.newURI("https://example.com"),
40         { "X-Some-Header": "header value" },
41         ""
42       ),
43       new ObliviousHttpTestResponse(200, {}, "Hello, World!")
44     ),
45     new ObliviousHttpTestCase(
46       new ObliviousHttpTestRequest(
47         "POST",
48         NetUtil.newURI("http://example.test"),
49         { "X-Some-Header": "header value", "X-Some-Other-Header": "25" },
50         "Posting some content..."
51       ),
52       new ObliviousHttpTestResponse(
53         418,
54         { "X-Teapot": "teapot" },
55         "I'm a teapot"
56       )
57     ),
58     new ObliviousHttpTestCase(
59       new ObliviousHttpTestRequest(
60         "PUT",
61         NetUtil.newURI("http://example.test"),
62         { "X-Some-Header": "header value", "X-Some-Other-Header": "25" },
63         "Putting some content..."
64       ),
65       new ObliviousHttpTestResponse(
66         418,
67         { "X-Teapot": "teapot" },
68         "I'm a teapot"
69       )
70     ),
71     new ObliviousHttpTestCase(
72       new ObliviousHttpTestRequest(
73         "GET",
74         NetUtil.newURI("http://example.test/404"),
75         { "X-Some-Header": "header value", "X-Some-Other-Header": "25" },
76         ""
77       ),
78       undefined // 404 relay
79     ),
80   ];
82   for (let testcase of testcases) {
83     await run_one_testcase(testcase);
84   }
85 });
87 async function run_one_testcase(testcase) {
88   let ohttp = Cc["@mozilla.org/network/oblivious-http;1"].getService(
89     Ci.nsIObliviousHttp
90   );
91   let ohttpServer = ohttp.server();
93   let httpServer = new HttpServer();
94   httpServer.registerPathHandler("/", function (request, response) {
95     let inputStream = Cc["@mozilla.org/scriptableinputstream;1"].createInstance(
96       Ci.nsIScriptableInputStream
97     );
98     inputStream.init(request.bodyInputStream);
99     let requestBody = inputStream.readBytes(inputStream.available());
100     let ohttpResponse = ohttpServer.decapsulate(stringToBytes(requestBody));
101     let bhttp = Cc["@mozilla.org/network/binary-http;1"].getService(
102       Ci.nsIBinaryHttp
103     );
104     let decodedRequest = bhttp.decodeRequest(ohttpResponse.request);
105     equal(decodedRequest.method, testcase.request.method);
106     equal(decodedRequest.scheme, testcase.request.uri.scheme);
107     equal(decodedRequest.authority, testcase.request.uri.hostPort);
108     equal(decodedRequest.path, testcase.request.uri.pathQueryRef);
109     for (
110       let i = 0;
111       i < decodedRequest.headerNames.length &&
112       i < decodedRequest.headerValues.length;
113       i++
114     ) {
115       equal(
116         decodedRequest.headerValues[i],
117         testcase.request.headers[decodedRequest.headerNames[i]]
118       );
119     }
120     equal(bytesToString(decodedRequest.content), testcase.request.content);
122     let responseHeaderNames = ["content-type"];
123     let responseHeaderValues = ["text/plain"];
124     for (let headerName of Object.keys(testcase.response.headers)) {
125       responseHeaderNames.push(headerName);
126       responseHeaderValues.push(testcase.response.headers[headerName]);
127     }
128     let binaryResponse = new BinaryHttpResponse(
129       testcase.response.status,
130       responseHeaderNames,
131       responseHeaderValues,
132       stringToBytes(testcase.response.content)
133     );
134     let responseBytes = bhttp.encodeResponse(binaryResponse);
135     let encResponse = ohttpResponse.encapsulate(responseBytes);
136     response.setStatusLine(request.httpVersion, 200, "OK");
137     response.setHeader("Content-Type", "message/ohttp-res", false);
138     response.write(bytesToString(encResponse));
139   });
140   httpServer.start(-1);
142   let ohttpService = Cc[
143     "@mozilla.org/network/oblivious-http-service;1"
144   ].getService(Ci.nsIObliviousHttpService);
145   let relayURI = NetUtil.newURI(
146     `http://localhost:${httpServer.identity.primaryPort}/`
147   );
148   if (!testcase.response) {
149     relayURI = NetUtil.newURI(
150       `http://localhost:${httpServer.identity.primaryPort}/404`
151     );
152   }
153   let obliviousHttpChannel = ohttpService
154     .newChannel(relayURI, testcase.request.uri, ohttpServer.encodedConfig)
155     .QueryInterface(Ci.nsIHttpChannel);
156   for (let headerName of Object.keys(testcase.request.headers)) {
157     obliviousHttpChannel.setRequestHeader(
158       headerName,
159       testcase.request.headers[headerName],
160       false
161     );
162   }
163   if (testcase.request.method == "POST" || testcase.request.method == "PUT") {
164     let uploadChannel = obliviousHttpChannel.QueryInterface(
165       Ci.nsIUploadChannel2
166     );
167     ok(uploadChannel);
168     let bodyStream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(
169       Ci.nsIStringInputStream
170     );
171     bodyStream.setData(
172       testcase.request.content,
173       testcase.request.content.length
174     );
175     uploadChannel.explicitSetUploadStream(
176       bodyStream,
177       null,
178       -1,
179       testcase.request.method,
180       false
181     );
182   }
183   let response = await new Promise(resolve => {
184     NetUtil.asyncFetch(obliviousHttpChannel, function (inputStream) {
185       let scriptableInputStream = Cc[
186         "@mozilla.org/scriptableinputstream;1"
187       ].createInstance(Ci.nsIScriptableInputStream);
188       scriptableInputStream.init(inputStream);
189       try {
190         // If decoding failed just return undefined.
191         inputStream.available();
192       } catch (e) {
193         resolve(undefined);
194         return;
195       }
196       let responseBody = scriptableInputStream.readBytes(
197         inputStream.available()
198       );
199       resolve(responseBody);
200     });
201   });
202   if (testcase.response) {
203     equal(response, testcase.response.content);
204     for (let headerName of Object.keys(testcase.response.headers)) {
205       equal(
206         obliviousHttpChannel.getResponseHeader(headerName),
207         testcase.response.headers[headerName]
208       );
209     }
210   } else {
211     let relayChannel = obliviousHttpChannel.QueryInterface(
212       Ci.nsIObliviousHttpChannel
213     ).relayChannel;
214     equal(relayChannel.responseStatus, 404);
215   }
216   await new Promise(resolve => {
217     httpServer.stop(resolve);
218   });