test_oblivious_http.js (7089B)
1 /* Any copyright is dedicated to the Public Domain. 2 * https://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 const { HttpServer } = ChromeUtils.importESModule( 7 "resource://testing-common/httpd.sys.mjs" 8 ); 9 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 } 17 } 18 19 class ObliviousHttpTestResponse { 20 constructor(status, headers, content) { 21 this.status = status; 22 this.headers = headers; 23 this.content = content; 24 } 25 } 26 27 class ObliviousHttpTestCase { 28 constructor(request, response) { 29 this.request = request; 30 this.response = response; 31 } 32 } 33 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 "DELETE", 74 NetUtil.newURI("http://example.test"), 75 { "X-Some-Header": "header value", "X-Some-Other-Header": "25" }, 76 "Putting some content..." 77 ), 78 new ObliviousHttpTestResponse( 79 418, 80 { "X-Teapot": "teapot" }, 81 "I'm a teapot" 82 ) 83 ), 84 new ObliviousHttpTestCase( 85 new ObliviousHttpTestRequest( 86 "GET", 87 NetUtil.newURI("http://example.test/404"), 88 { "X-Some-Header": "header value", "X-Some-Other-Header": "25" }, 89 "" 90 ), 91 undefined // 404 relay 92 ), 93 ]; 94 95 for (let testcase of testcases) { 96 await run_one_testcase(testcase); 97 } 98 }); 99 100 async function run_one_testcase(testcase) { 101 let ohttp = Cc["@mozilla.org/network/oblivious-http;1"].getService( 102 Ci.nsIObliviousHttp 103 ); 104 let ohttpServer = ohttp.server(); 105 106 let httpServer = new HttpServer(); 107 httpServer.registerPathHandler("/", function (request, response) { 108 let inputStream = Cc["@mozilla.org/scriptableinputstream;1"].createInstance( 109 Ci.nsIScriptableInputStream 110 ); 111 inputStream.init(request.bodyInputStream); 112 let requestBody = inputStream.readBytes(inputStream.available()); 113 let ohttpResponse = ohttpServer.decapsulate(stringToBytes(requestBody)); 114 let bhttp = Cc["@mozilla.org/network/binary-http;1"].getService( 115 Ci.nsIBinaryHttp 116 ); 117 let decodedRequest = bhttp.decodeRequest(ohttpResponse.request); 118 equal(decodedRequest.method, testcase.request.method); 119 equal(decodedRequest.scheme, testcase.request.uri.scheme); 120 equal(decodedRequest.authority, testcase.request.uri.hostPort); 121 equal(decodedRequest.path, testcase.request.uri.pathQueryRef); 122 for ( 123 let i = 0; 124 i < decodedRequest.headerNames.length && 125 i < decodedRequest.headerValues.length; 126 i++ 127 ) { 128 equal( 129 decodedRequest.headerValues[i], 130 testcase.request.headers[decodedRequest.headerNames[i]] 131 ); 132 } 133 equal(bytesToString(decodedRequest.content), testcase.request.content); 134 135 let responseHeaderNames = ["content-type"]; 136 let responseHeaderValues = ["text/plain"]; 137 for (let headerName of Object.keys(testcase.response.headers)) { 138 responseHeaderNames.push(headerName); 139 responseHeaderValues.push(testcase.response.headers[headerName]); 140 } 141 let binaryResponse = new BinaryHttpResponse( 142 testcase.response.status, 143 responseHeaderNames, 144 responseHeaderValues, 145 stringToBytes(testcase.response.content) 146 ); 147 let responseBytes = bhttp.encodeResponse(binaryResponse); 148 let encResponse = ohttpResponse.encapsulate(responseBytes); 149 response.setStatusLine(request.httpVersion, 200, "OK"); 150 response.setHeader("Content-Type", "message/ohttp-res", false); 151 response.write(bytesToString(encResponse)); 152 }); 153 httpServer.start(-1); 154 155 let ohttpService = Cc[ 156 "@mozilla.org/network/oblivious-http-service;1" 157 ].getService(Ci.nsIObliviousHttpService); 158 let relayURI = NetUtil.newURI( 159 `http://localhost:${httpServer.identity.primaryPort}/` 160 ); 161 if (!testcase.response) { 162 relayURI = NetUtil.newURI( 163 `http://localhost:${httpServer.identity.primaryPort}/404` 164 ); 165 } 166 let obliviousHttpChannel = ohttpService 167 .newChannel(relayURI, testcase.request.uri, ohttpServer.encodedConfig) 168 .QueryInterface(Ci.nsIHttpChannel); 169 for (let headerName of Object.keys(testcase.request.headers)) { 170 obliviousHttpChannel.setRequestHeader( 171 headerName, 172 testcase.request.headers[headerName], 173 false 174 ); 175 } 176 if ( 177 testcase.request.method == "POST" || 178 testcase.request.method == "PUT" || 179 testcase.request.method == "DELETE" 180 ) { 181 let uploadChannel = obliviousHttpChannel.QueryInterface( 182 Ci.nsIUploadChannel2 183 ); 184 ok(uploadChannel); 185 let bodyStream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance( 186 Ci.nsIStringInputStream 187 ); 188 bodyStream.setByteStringData(testcase.request.content); 189 uploadChannel.explicitSetUploadStream( 190 bodyStream, 191 null, 192 -1, 193 testcase.request.method, 194 false 195 ); 196 } 197 let response = await new Promise(resolve => { 198 NetUtil.asyncFetch(obliviousHttpChannel, function (inputStream) { 199 let scriptableInputStream = Cc[ 200 "@mozilla.org/scriptableinputstream;1" 201 ].createInstance(Ci.nsIScriptableInputStream); 202 scriptableInputStream.init(inputStream); 203 try { 204 // If decoding failed just return undefined. 205 inputStream.available(); 206 } catch (e) { 207 resolve(undefined); 208 return; 209 } 210 let responseBody = scriptableInputStream.readBytes( 211 inputStream.available() 212 ); 213 resolve(responseBody); 214 }); 215 }); 216 if (testcase.response) { 217 equal(response, testcase.response.content); 218 for (let headerName of Object.keys(testcase.response.headers)) { 219 equal( 220 obliviousHttpChannel.getResponseHeader(headerName), 221 testcase.response.headers[headerName] 222 ); 223 } 224 } else { 225 let relayChannel = obliviousHttpChannel.QueryInterface( 226 Ci.nsIObliviousHttpChannel 227 ).relayChannel; 228 equal(relayChannel.responseStatus, 404); 229 } 230 await new Promise(resolve => { 231 httpServer.stop(resolve); 232 }); 233 }