tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }