tor-browser

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

test_resource.js (18408B)


      1 /* Any copyright is dedicated to the Public Domain.
      2 * http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 const { Observers } = ChromeUtils.importESModule(
      5  "resource://services-common/observers.sys.mjs"
      6 );
      7 const { Resource } = ChromeUtils.importESModule(
      8  "resource://services-sync/resource.sys.mjs"
      9 );
     10 const { SyncAuthManager } = ChromeUtils.importESModule(
     11  "resource://services-sync/sync_auth.sys.mjs"
     12 );
     13 
     14 var fetched = false;
     15 function server_open(metadata, response) {
     16  let body;
     17  if (metadata.method == "GET") {
     18    fetched = true;
     19    body = "This path exists";
     20    response.setStatusLine(metadata.httpVersion, 200, "OK");
     21  } else {
     22    body = "Wrong request method";
     23    response.setStatusLine(metadata.httpVersion, 405, "Method Not Allowed");
     24  }
     25  response.bodyOutputStream.write(body, body.length);
     26 }
     27 
     28 function server_protected(metadata, response) {
     29  let body;
     30 
     31  if (has_hawk_header(metadata)) {
     32    body = "This path exists and is protected";
     33    response.setStatusLine(metadata.httpVersion, 200, "OK, authorized");
     34    response.setHeader("WWW-Authenticate", 'Basic realm="secret"', false);
     35  } else {
     36    body = "This path exists and is protected - failed";
     37    response.setStatusLine(metadata.httpVersion, 401, "Unauthorized");
     38    response.setHeader("WWW-Authenticate", 'Basic realm="secret"', false);
     39  }
     40 
     41  response.bodyOutputStream.write(body, body.length);
     42 }
     43 
     44 function server_404(metadata, response) {
     45  let body = "File not found";
     46  response.setStatusLine(metadata.httpVersion, 404, "Not Found");
     47  response.bodyOutputStream.write(body, body.length);
     48 }
     49 
     50 var pacFetched = false;
     51 function server_pac(metadata, response) {
     52  _("Invoked PAC handler.");
     53  pacFetched = true;
     54  let body = 'function FindProxyForURL(url, host) { return "DIRECT"; }';
     55  response.setStatusLine(metadata.httpVersion, 200, "OK");
     56  response.setHeader(
     57    "Content-Type",
     58    "application/x-ns-proxy-autoconfig",
     59    false
     60  );
     61  response.bodyOutputStream.write(body, body.length);
     62 }
     63 
     64 var sample_data = {
     65  some: "sample_data",
     66  injson: "format",
     67  number: 42,
     68 };
     69 
     70 function server_upload(metadata, response) {
     71  let body;
     72 
     73  let input = readBytesFromInputStream(metadata.bodyInputStream);
     74  if (input == JSON.stringify(sample_data)) {
     75    body = "Valid data upload via " + metadata.method;
     76    response.setStatusLine(metadata.httpVersion, 200, "OK");
     77  } else {
     78    body = "Invalid data upload via " + metadata.method + ": " + input;
     79    response.setStatusLine(metadata.httpVersion, 500, "Internal Server Error");
     80  }
     81 
     82  response.bodyOutputStream.write(body, body.length);
     83 }
     84 
     85 function server_delete(metadata, response) {
     86  let body;
     87  if (metadata.method == "DELETE") {
     88    body = "This resource has been deleted";
     89    response.setStatusLine(metadata.httpVersion, 200, "OK");
     90  } else {
     91    body = "Wrong request method";
     92    response.setStatusLine(metadata.httpVersion, 405, "Method Not Allowed");
     93  }
     94  response.bodyOutputStream.write(body, body.length);
     95 }
     96 
     97 function server_json(metadata, response) {
     98  let body = JSON.stringify(sample_data);
     99  response.setStatusLine(metadata.httpVersion, 200, "OK");
    100  response.bodyOutputStream.write(body, body.length);
    101 }
    102 
    103 const TIMESTAMP = 1274380461;
    104 
    105 function server_timestamp(metadata, response) {
    106  let body = "Thank you for your request";
    107  response.setHeader("X-Weave-Timestamp", "" + TIMESTAMP, false);
    108  response.setStatusLine(metadata.httpVersion, 200, "OK");
    109  response.bodyOutputStream.write(body, body.length);
    110 }
    111 
    112 function server_backoff(metadata, response) {
    113  let body = "Hey, back off!";
    114  response.setHeader("X-Weave-Backoff", "600", false);
    115  response.setStatusLine(metadata.httpVersion, 200, "OK");
    116  response.bodyOutputStream.write(body, body.length);
    117 }
    118 
    119 function server_quota_notice(request, response) {
    120  let body = "You're approaching quota.";
    121  response.setHeader("X-Weave-Quota-Remaining", "1048576", false);
    122  response.setStatusLine(request.httpVersion, 200, "OK");
    123  response.bodyOutputStream.write(body, body.length);
    124 }
    125 
    126 function server_quota_error(request, response) {
    127  let body = "14";
    128  response.setHeader("X-Weave-Quota-Remaining", "-1024", false);
    129  response.setStatusLine(request.httpVersion, 400, "OK");
    130  response.bodyOutputStream.write(body, body.length);
    131 }
    132 
    133 function server_headers(metadata, response) {
    134  let ignore_headers = [
    135    "host",
    136    "user-agent",
    137    "accept-language",
    138    "accept-encoding",
    139    "accept-charset",
    140    "keep-alive",
    141    "connection",
    142    "pragma",
    143    "origin",
    144    "cache-control",
    145    "content-length",
    146  ];
    147  let headers = metadata.headers;
    148  let header_names = [];
    149  while (headers.hasMoreElements()) {
    150    let header = headers.getNext().toString();
    151    if (!ignore_headers.includes(header)) {
    152      header_names.push(header);
    153    }
    154  }
    155  header_names = header_names.sort();
    156 
    157  headers = {};
    158  for (let header of header_names) {
    159    headers[header] = metadata.getHeader(header);
    160  }
    161  let body = JSON.stringify(headers);
    162  response.setStatusLine(metadata.httpVersion, 200, "OK");
    163  response.bodyOutputStream.write(body, body.length);
    164 }
    165 
    166 var quotaValue;
    167 Observers.add("weave:service:quota:remaining", function (subject) {
    168  quotaValue = subject;
    169 });
    170 
    171 function run_test() {
    172  Log.repository.rootLogger.addAppender(new Log.DumpAppender());
    173 
    174  Svc.PrefBranch.setIntPref("network.numRetries", 1); // speed up test
    175  run_next_test();
    176 }
    177 
    178 // This apparently has to come first in order for our PAC URL to be hit.
    179 // Don't put any other HTTP requests earlier in the file!
    180 add_task(async function test_proxy_auth_redirect() {
    181  _(
    182    "Ensure that a proxy auth redirect (which switches out our channel) " +
    183      "doesn't break Resource."
    184  );
    185  let server = httpd_setup({
    186    "/open": server_open,
    187    "/pac2": server_pac,
    188  });
    189 
    190  PACSystemSettings.PACURI = server.baseURI + "/pac2";
    191  installFakePAC();
    192  let res = new Resource(server.baseURI + "/open");
    193  let result = await res.get();
    194  Assert.ok(pacFetched);
    195  Assert.ok(fetched);
    196  Assert.equal("This path exists", result.data);
    197  pacFetched = fetched = false;
    198  uninstallFakePAC();
    199  await promiseStopServer(server);
    200 });
    201 
    202 add_task(async function test_new_channel() {
    203  _("Ensure a redirect to a new channel is handled properly.");
    204 
    205  let resourceRequested = false;
    206  function resourceHandler(metadata, response) {
    207    resourceRequested = true;
    208 
    209    let body = "Test";
    210    response.setHeader("Content-Type", "text/plain");
    211    response.bodyOutputStream.write(body, body.length);
    212  }
    213 
    214  let locationURL;
    215  function redirectHandler(metadata, response) {
    216    let body = "Redirecting";
    217    response.setStatusLine(metadata.httpVersion, 307, "TEMPORARY REDIRECT");
    218    response.setHeader("Location", locationURL);
    219    response.bodyOutputStream.write(body, body.length);
    220  }
    221 
    222  let server = httpd_setup({
    223    "/resource": resourceHandler,
    224    "/redirect": redirectHandler,
    225  });
    226  locationURL = server.baseURI + "/resource";
    227 
    228  let request = new Resource(server.baseURI + "/redirect");
    229  let content = await request.get();
    230  Assert.ok(resourceRequested);
    231  Assert.equal(200, content.status);
    232  Assert.ok("content-type" in content.headers);
    233  Assert.equal("text/plain", content.headers["content-type"]);
    234 
    235  await promiseStopServer(server);
    236 });
    237 
    238 var server;
    239 
    240 add_test(function setup() {
    241  server = httpd_setup({
    242    "/open": server_open,
    243    "/protected": server_protected,
    244    "/404": server_404,
    245    "/upload": server_upload,
    246    "/delete": server_delete,
    247    "/json": server_json,
    248    "/timestamp": server_timestamp,
    249    "/headers": server_headers,
    250    "/backoff": server_backoff,
    251    "/pac2": server_pac,
    252    "/quota-notice": server_quota_notice,
    253    "/quota-error": server_quota_error,
    254  });
    255 
    256  run_next_test();
    257 });
    258 
    259 add_test(function test_members() {
    260  _("Resource object members");
    261  let uri = server.baseURI + "/open";
    262  let res = new Resource(uri);
    263  Assert.ok(res.uri instanceof Ci.nsIURI);
    264  Assert.equal(res.uri.spec, uri);
    265  Assert.equal(res.spec, uri);
    266  Assert.equal(typeof res.headers, "object");
    267  Assert.equal(typeof res.authenticator, "object");
    268 
    269  run_next_test();
    270 });
    271 
    272 add_task(async function test_get() {
    273  _("GET a non-password-protected resource");
    274  let res = new Resource(server.baseURI + "/open");
    275  let content = await res.get();
    276  Assert.equal(content.data, "This path exists");
    277  Assert.equal(content.status, 200);
    278  Assert.ok(content.success);
    279 
    280  // Observe logging messages.
    281  let resLogger = res._log;
    282  let dbg = resLogger.debug;
    283  let debugMessages = [];
    284  resLogger.debug = function (msg, extra) {
    285    debugMessages.push(`${msg}: ${JSON.stringify(extra)}`);
    286    dbg.call(this, msg);
    287  };
    288 
    289  // Since we didn't receive proper JSON data, accessing content.obj
    290  // will result in a SyntaxError from JSON.parse
    291  let didThrow = false;
    292  try {
    293    content.obj;
    294  } catch (ex) {
    295    didThrow = true;
    296  }
    297  Assert.ok(didThrow);
    298  Assert.equal(debugMessages.length, 1);
    299  Assert.equal(
    300    debugMessages[0],
    301    'Parse fail: Response body starts: "This path exists"'
    302  );
    303  resLogger.debug = dbg;
    304 });
    305 
    306 add_test(function test_basicauth() {
    307  _("Test that the BasicAuthenticator doesn't screw up header case.");
    308  let res1 = new Resource(server.baseURI + "/foo");
    309  res1.setHeader("Authorization", "Basic foobar");
    310  Assert.equal(res1._headers.authorization, "Basic foobar");
    311  Assert.equal(res1.headers.authorization, "Basic foobar");
    312 
    313  run_next_test();
    314 });
    315 
    316 add_task(async function test_get_protected_fail() {
    317  _(
    318    "GET a password protected resource (test that it'll fail w/o pass, no throw)"
    319  );
    320  let res2 = new Resource(server.baseURI + "/protected");
    321  let content = await res2.get();
    322  Assert.equal(content.data, "This path exists and is protected - failed");
    323  Assert.equal(content.status, 401);
    324  Assert.ok(!content.success);
    325 });
    326 
    327 add_task(async function test_get_protected_success() {
    328  _("GET a password protected resource");
    329  let identityConfig = makeIdentityConfig();
    330  let syncAuthManager = new SyncAuthManager();
    331  configureFxAccountIdentity(syncAuthManager, identityConfig);
    332  let auth = syncAuthManager.getResourceAuthenticator();
    333  let res3 = new Resource(server.baseURI + "/protected");
    334  res3.authenticator = auth;
    335  Assert.equal(res3.authenticator, auth);
    336  let content = await res3.get();
    337  Assert.equal(content.data, "This path exists and is protected");
    338  Assert.equal(content.status, 200);
    339  Assert.ok(content.success);
    340 });
    341 
    342 add_task(async function test_get_404() {
    343  _("GET a non-existent resource (test that it'll fail, but not throw)");
    344  let res4 = new Resource(server.baseURI + "/404");
    345  let content = await res4.get();
    346  Assert.equal(content.data, "File not found");
    347  Assert.equal(content.status, 404);
    348  Assert.ok(!content.success);
    349 
    350  // Check some headers of the 404 response
    351  Assert.equal(content.headers.connection, "close");
    352  Assert.equal(content.headers.server, "httpd.js");
    353  Assert.equal(content.headers["content-length"], 14);
    354 });
    355 
    356 add_task(async function test_put_string() {
    357  _("PUT to a resource (string)");
    358  let res_upload = new Resource(server.baseURI + "/upload");
    359  let content = await res_upload.put(JSON.stringify(sample_data));
    360  Assert.equal(content.data, "Valid data upload via PUT");
    361  Assert.equal(content.status, 200);
    362 });
    363 
    364 add_task(async function test_put_object() {
    365  _("PUT to a resource (object)");
    366  let res_upload = new Resource(server.baseURI + "/upload");
    367  let content = await res_upload.put(sample_data);
    368  Assert.equal(content.data, "Valid data upload via PUT");
    369  Assert.equal(content.status, 200);
    370 });
    371 
    372 add_task(async function test_post_string() {
    373  _("POST to a resource (string)");
    374  let res_upload = new Resource(server.baseURI + "/upload");
    375  let content = await res_upload.post(JSON.stringify(sample_data));
    376  Assert.equal(content.data, "Valid data upload via POST");
    377  Assert.equal(content.status, 200);
    378 });
    379 
    380 add_task(async function test_post_object() {
    381  _("POST to a resource (object)");
    382  let res_upload = new Resource(server.baseURI + "/upload");
    383  let content = await res_upload.post(sample_data);
    384  Assert.equal(content.data, "Valid data upload via POST");
    385  Assert.equal(content.status, 200);
    386 });
    387 
    388 add_task(async function test_delete() {
    389  _("DELETE a resource");
    390  let res6 = new Resource(server.baseURI + "/delete");
    391  let content = await res6.delete();
    392  Assert.equal(content.data, "This resource has been deleted");
    393  Assert.equal(content.status, 200);
    394 });
    395 
    396 add_task(async function test_json_body() {
    397  _("JSON conversion of response body");
    398  let res7 = new Resource(server.baseURI + "/json");
    399  let content = await res7.get();
    400  Assert.equal(content.data, JSON.stringify(sample_data));
    401  Assert.equal(content.status, 200);
    402  Assert.equal(JSON.stringify(content.obj), JSON.stringify(sample_data));
    403 });
    404 
    405 add_task(async function test_weave_timestamp() {
    406  _("X-Weave-Timestamp header updates Resource.serverTime");
    407  // Before having received any response containing the
    408  // X-Weave-Timestamp header, Resource.serverTime is null.
    409  Assert.equal(Resource.serverTime, null);
    410  let res8 = new Resource(server.baseURI + "/timestamp");
    411  await res8.get();
    412  Assert.equal(Resource.serverTime, TIMESTAMP);
    413 });
    414 
    415 add_task(async function test_get_default_headers() {
    416  _("GET: Accept defaults to application/json");
    417  let res_headers = new Resource(server.baseURI + "/headers");
    418  let content = JSON.parse((await res_headers.get()).data);
    419  Assert.equal(content.accept, "application/json;q=0.9,*/*;q=0.2");
    420 });
    421 
    422 add_task(async function test_put_default_headers() {
    423  _(
    424    "PUT: Accept defaults to application/json, Content-Type defaults to text/plain"
    425  );
    426  let res_headers = new Resource(server.baseURI + "/headers");
    427  let content = JSON.parse((await res_headers.put("data")).data);
    428  Assert.equal(content.accept, "application/json;q=0.9,*/*;q=0.2");
    429  Assert.equal(content["content-type"], "text/plain");
    430 });
    431 
    432 add_task(async function test_post_default_headers() {
    433  _(
    434    "POST: Accept defaults to application/json, Content-Type defaults to text/plain"
    435  );
    436  let res_headers = new Resource(server.baseURI + "/headers");
    437  let content = JSON.parse((await res_headers.post("data")).data);
    438  Assert.equal(content.accept, "application/json;q=0.9,*/*;q=0.2");
    439  Assert.equal(content["content-type"], "text/plain");
    440 });
    441 
    442 add_task(async function test_setHeader() {
    443  _("setHeader(): setting simple header");
    444  let res_headers = new Resource(server.baseURI + "/headers");
    445  res_headers.setHeader("X-What-Is-Weave", "awesome");
    446  Assert.equal(res_headers.headers["x-what-is-weave"], "awesome");
    447  let content = JSON.parse((await res_headers.get()).data);
    448  Assert.equal(content["x-what-is-weave"], "awesome");
    449 });
    450 
    451 add_task(async function test_setHeader_overwrite() {
    452  _("setHeader(): setting multiple headers, overwriting existing header");
    453  let res_headers = new Resource(server.baseURI + "/headers");
    454  res_headers.setHeader("X-WHAT-is-Weave", "more awesomer");
    455  res_headers.setHeader("X-Another-Header", "hello world");
    456  Assert.equal(res_headers.headers["x-what-is-weave"], "more awesomer");
    457  Assert.equal(res_headers.headers["x-another-header"], "hello world");
    458  let content = JSON.parse((await res_headers.get()).data);
    459  Assert.equal(content["x-what-is-weave"], "more awesomer");
    460  Assert.equal(content["x-another-header"], "hello world");
    461 });
    462 
    463 add_task(async function test_put_override_content_type() {
    464  _("PUT: override default Content-Type");
    465  let res_headers = new Resource(server.baseURI + "/headers");
    466  res_headers.setHeader("Content-Type", "application/foobar");
    467  Assert.equal(res_headers.headers["content-type"], "application/foobar");
    468  let content = JSON.parse((await res_headers.put("data")).data);
    469  Assert.equal(content["content-type"], "application/foobar");
    470 });
    471 
    472 add_task(async function test_post_override_content_type() {
    473  _("POST: override default Content-Type");
    474  let res_headers = new Resource(server.baseURI + "/headers");
    475  res_headers.setHeader("Content-Type", "application/foobar");
    476  let content = JSON.parse((await res_headers.post("data")).data);
    477  Assert.equal(content["content-type"], "application/foobar");
    478 });
    479 
    480 add_task(async function test_weave_backoff() {
    481  _("X-Weave-Backoff header notifies observer");
    482  let backoffInterval;
    483  function onBackoff(subject) {
    484    backoffInterval = subject;
    485  }
    486  Observers.add("weave:service:backoff:interval", onBackoff);
    487 
    488  let res10 = new Resource(server.baseURI + "/backoff");
    489  await res10.get();
    490  Assert.equal(backoffInterval, 600);
    491 });
    492 
    493 add_task(async function test_quota_error() {
    494  _("X-Weave-Quota-Remaining header notifies observer on successful requests.");
    495  let res10 = new Resource(server.baseURI + "/quota-error");
    496  let content = await res10.get();
    497  Assert.equal(content.status, 400);
    498  Assert.equal(quotaValue, undefined); // HTTP 400, so no observer notification.
    499 });
    500 
    501 add_task(async function test_quota_notice() {
    502  let res10 = new Resource(server.baseURI + "/quota-notice");
    503  let content = await res10.get();
    504  Assert.equal(content.status, 200);
    505  Assert.equal(quotaValue, 1048576);
    506 });
    507 
    508 add_task(async function test_preserve_exceptions() {
    509  _("Error handling preserves exception information");
    510  let res11 = new Resource("http://localhost:12345/does/not/exist");
    511  await Assert.rejects(res11.get(), error => {
    512    Assert.notEqual(error, null);
    513    Assert.equal(error.result, Cr.NS_ERROR_CONNECTION_REFUSED);
    514    Assert.equal(error.name, "NS_ERROR_CONNECTION_REFUSED");
    515    return true;
    516  });
    517 });
    518 
    519 add_task(async function test_timeout() {
    520  _("Ensure channel timeouts are thrown appropriately.");
    521  let res19 = new Resource(server.baseURI + "/json");
    522  res19.ABORT_TIMEOUT = 0;
    523  await Assert.rejects(res19.get(), error => {
    524    Assert.equal(error.result, Cr.NS_ERROR_NET_TIMEOUT);
    525    return true;
    526  });
    527 });
    528 
    529 add_test(function test_uri_construction() {
    530  _("Testing URI construction.");
    531  let args = [];
    532  args.push("newer=" + 1234);
    533  args.push("limit=" + 1234);
    534  args.push("sort=" + 1234);
    535 
    536  let query = "?" + args.join("&");
    537 
    538  let uri1 = CommonUtils.makeURI("http://foo/" + query).QueryInterface(
    539    Ci.nsIURL
    540  );
    541  let uri2 = CommonUtils.makeURI("http://foo/").QueryInterface(Ci.nsIURL);
    542  uri2 = uri2.mutate().setQuery(query).finalize().QueryInterface(Ci.nsIURL);
    543  Assert.equal(uri1.query, uri2.query);
    544 
    545  run_next_test();
    546 });
    547 
    548 /**
    549 * End of tests that rely on a single HTTP server.
    550 * All tests after this point must begin and end their own.
    551 */
    552 add_test(function eliminate_server() {
    553  server.stop(run_next_test);
    554 });