/* Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */

"use strict";

const { RESTRequest } = ChromeUtils.importESModule(
  "resource://services-common/rest.sys.mjs"
);

function run_test() {
  Log.repository.getLogger("Services.Common.RESTRequest").level =
    Log.Level.Trace;
  initTestLogging("Trace");

  run_next_test();
}

/**
 * Initializing a RESTRequest with an invalid URI throws
 * NS_ERROR_MALFORMED_URI.
 */
add_test(function test_invalid_uri() {
  do_check_throws(function () {
    new RESTRequest("an invalid URI");
  }, Cr.NS_ERROR_MALFORMED_URI);
  run_next_test();
});

/**
 * Verify initial values for attributes.
 */
add_test(function test_attributes() {
  let uri = "http://foo.com/bar/baz";
  let request = new RESTRequest(uri);

  Assert.ok(request.uri instanceof Ci.nsIURI);
  Assert.equal(request.uri.spec, uri);
  Assert.equal(request.response, null);
  Assert.equal(request.status, request.NOT_SENT);
  let expectedLoadFlags =
    Ci.nsIRequest.LOAD_BYPASS_CACHE |
    Ci.nsIRequest.INHIBIT_CACHING |
    Ci.nsIRequest.LOAD_ANONYMOUS;
  Assert.equal(request.loadFlags, expectedLoadFlags);

  run_next_test();
});

/**
 * Verify that a proxy auth redirect doesn't break us. This has to be the first
 * request made in the file!
 */
add_task(async function test_proxy_auth_redirect() {
  let pacFetched = false;
  function pacHandler(metadata, response) {
    pacFetched = true;
    let body = 'function FindProxyForURL(url, host) { return "DIRECT"; }';
    response.setStatusLine(metadata.httpVersion, 200, "OK");
    response.setHeader(
      "Content-Type",
      "application/x-ns-proxy-autoconfig",
      false
    );
    response.bodyOutputStream.write(body, body.length);
  }

  let fetched = false;
  function original(metadata, response) {
    fetched = true;
    let body = "TADA!";
    response.setStatusLine(metadata.httpVersion, 200, "OK");
    response.bodyOutputStream.write(body, body.length);
  }

  let server = httpd_setup({
    "/original": original,
    "/pac3": pacHandler,
  });
  PACSystemSettings.PACURI = server.baseURI + "/pac3";
  installFakePAC();

  let req = new RESTRequest(server.baseURI + "/original");
  await req.get();

  Assert.ok(pacFetched);
  Assert.ok(fetched);

  Assert.ok(req.response.success);
  Assert.equal("TADA!", req.response.body);
  uninstallFakePAC();
  await promiseStopServer(server);
});

/**
 * Ensure that failures that cause asyncOpen to throw
 * result in callbacks being invoked.
 * Bug 826086.
 */
add_task(async function test_forbidden_port() {
  let request = new RESTRequest("http://localhost:6000/");

  await Assert.rejects(
    request.get(),
    error => error.result == Cr.NS_ERROR_PORT_ACCESS_NOT_ALLOWED
  );
});

/**
 * Demonstrate API short-hand: create a request and dispatch it immediately.
 */
add_task(async function test_simple_get() {
  let handler = httpd_handler(200, "OK", "Huzzah!");
  let server = httpd_setup({ "/resource": handler });
  let request = new RESTRequest(server.baseURI + "/resource");
  let promiseResponse = request.get();

  Assert.equal(request.status, request.SENT);
  Assert.equal(request.method, "GET");

  let response = await promiseResponse;
  Assert.equal(response, request.response);

  Assert.equal(request.status, request.COMPLETED);
  Assert.ok(response.success);
  Assert.equal(response.status, 200);
  Assert.equal(response.body, "Huzzah!");
  await promiseStopServer(server);
});

/**
 * Test HTTP GET with all bells and whistles.
 */
add_task(async function test_get() {
  let handler = httpd_handler(200, "OK", "Huzzah!");
  let server = httpd_setup({ "/resource": handler });

  let request = new RESTRequest(server.baseURI + "/resource");
  Assert.equal(request.status, request.NOT_SENT);

  let promiseResponse = request.get();

  Assert.equal(request.status, request.SENT);
  Assert.equal(request.method, "GET");

  Assert.ok(!!(request.channel.loadFlags & Ci.nsIRequest.LOAD_BYPASS_CACHE));
  Assert.ok(!!(request.channel.loadFlags & Ci.nsIRequest.INHIBIT_CACHING));

  let response = await promiseResponse;

  Assert.equal(response, request.response);
  Assert.equal(request.status, request.COMPLETED);
  Assert.ok(request.response.success);
  Assert.equal(request.response.status, 200);
  Assert.equal(request.response.body, "Huzzah!");
  Assert.equal(handler.request.method, "GET");

  await Assert.rejects(request.get(), /Request has already been sent/);

  await promiseStopServer(server);
});

/**
 * Test HTTP GET with UTF-8 content, and custom Content-Type.
 */
add_task(async function test_get_utf8() {
  let response = "Hello World or Καλημέρα κόσμε or こんにちは 世界 😺";

  let contentType = "text/plain";
  let charset = true;
  let charsetSuffix = "; charset=UTF-8";

  let server = httpd_setup({
    "/resource": function (req, res) {
      res.setStatusLine(req.httpVersion, 200, "OK");
      res.setHeader(
        "Content-Type",
        contentType + (charset ? charsetSuffix : "")
      );

      let converter = Cc[
        "@mozilla.org/intl/converter-output-stream;1"
      ].createInstance(Ci.nsIConverterOutputStream);
      converter.init(res.bodyOutputStream, "UTF-8");
      converter.writeString(response);
      converter.close();
    },
  });

  // Check if charset in Content-Type is propertly interpreted.
  let request1 = new RESTRequest(server.baseURI + "/resource");
  await request1.get();

  Assert.equal(request1.response.status, 200);
  Assert.equal(request1.response.body, response);
  Assert.equal(
    request1.response.headers["content-type"],
    contentType + charsetSuffix
  );

  // Check that we default to UTF-8 if Content-Type doesn't have a charset
  charset = false;
  let request2 = new RESTRequest(server.baseURI + "/resource");
  await request2.get();
  Assert.equal(request2.response.status, 200);
  Assert.equal(request2.response.body, response);
  Assert.equal(request2.response.headers["content-type"], contentType);
  Assert.equal(request2.response.charset, "utf-8");

  let request3 = new RESTRequest(server.baseURI + "/resource");

  // With the test server we tend to get onDataAvailable in chunks of 8192 (in
  // real network requests there doesn't appear to be any pattern to the size of
  // the data `onDataAvailable` is called with), the smiling cat emoji encodes as
  // 4 bytes, and so when utf8 encoded, the `"a" + "😺".repeat(2048)` will not be
  // aligned onto a codepoint.
  //
  // Since 8192 isn't guaranteed and could easily change, the following string is
  // a) very long, and b) misaligned on roughly 3/4 of the bytes, as a safety
  // measure.
  response = ("a" + "😺".repeat(2048)).repeat(10);

  await request3.get();

  Assert.equal(request3.response.status, 200);

  // Make sure it came through ok, despite the misalignment.
  Assert.equal(request3.response.body, response);

  await promiseStopServer(server);
});

/**
 * Test HTTP POST data is encoded as UTF-8 by default.
 */
add_task(async function test_post_utf8() {
  // We setup a handler that responds with exactly what it received.
  // Given we've already tested above that responses are correctly utf-8
  // decoded we can surmise that the correct response coming back means the
  // input must also have been encoded.
  let server = httpd_setup({
    "/echo": function (req, res) {
      res.setStatusLine(req.httpVersion, 200, "OK");
      res.setHeader("Content-Type", req.getHeader("content-type"));
      // Get the body as bytes and write them back without touching them
      let sis = Cc["@mozilla.org/scriptableinputstream;1"].createInstance(
        Ci.nsIScriptableInputStream
      );
      sis.init(req.bodyInputStream);
      let body = sis.read(sis.available());
      sis.close();
      res.write(body);
    },
  });

  let data = {
    copyright: "©",
    // See the comment in test_get_utf8 about this string.
    long: ("a" + "😺".repeat(2048)).repeat(10),
  };
  let request1 = new RESTRequest(server.baseURI + "/echo");
  await request1.post(data);

  Assert.equal(request1.response.status, 200);
  deepEqual(JSON.parse(request1.response.body), data);
  Assert.equal(
    request1.response.headers["content-type"],
    "application/json; charset=utf-8"
  );

  await promiseStopServer(server);
});

/**
 * Test more variations of charset handling.
 */
add_task(async function test_charsets() {
  let response = "Hello World, I can't speak Russian";

  let contentType = "text/plain";
  let charset = true;
  let charsetSuffix = "; charset=us-ascii";

  let server = httpd_setup({
    "/resource": function (req, res) {
      res.setStatusLine(req.httpVersion, 200, "OK");
      res.setHeader(
        "Content-Type",
        contentType + (charset ? charsetSuffix : "")
      );

      let converter = Cc[
        "@mozilla.org/intl/converter-output-stream;1"
      ].createInstance(Ci.nsIConverterOutputStream);
      converter.init(res.bodyOutputStream, "us-ascii");
      converter.writeString(response);
      converter.close();
    },
  });

  // Check that provided charset overrides hint.
  let request1 = new RESTRequest(server.baseURI + "/resource");
  request1.charset = "not-a-charset";
  await request1.get();
  Assert.equal(request1.response.status, 200);
  Assert.equal(request1.response.body, response);
  Assert.equal(
    request1.response.headers["content-type"],
    contentType + charsetSuffix
  );
  Assert.equal(request1.response.charset, "us-ascii");

  // Check that hint is used if Content-Type doesn't have a charset.
  charset = false;
  let request2 = new RESTRequest(server.baseURI + "/resource");
  request2.charset = "us-ascii";
  await request2.get();

  Assert.equal(request2.response.status, 200);
  Assert.equal(request2.response.body, response);
  Assert.equal(request2.response.headers["content-type"], contentType);
  Assert.equal(request2.response.charset, "us-ascii");

  await promiseStopServer(server);
});

/**
 * Used for testing PATCH/PUT/POST methods.
 */
async function check_posting_data(method) {
  let funcName = method.toLowerCase();
  let handler = httpd_handler(200, "OK", "Got it!");
  let server = httpd_setup({ "/resource": handler });

  let request = new RESTRequest(server.baseURI + "/resource");
  Assert.equal(request.status, request.NOT_SENT);
  let responsePromise = request[funcName]("Hullo?");
  Assert.equal(request.status, request.SENT);
  Assert.equal(request.method, method);

  let response = await responsePromise;

  Assert.equal(response, request.response);

  Assert.equal(request.status, request.COMPLETED);
  Assert.ok(request.response.success);
  Assert.equal(request.response.status, 200);
  Assert.equal(request.response.body, "Got it!");

  Assert.equal(handler.request.method, method);
  Assert.equal(handler.request.body, "Hullo?");
  Assert.equal(handler.request.getHeader("Content-Type"), "text/plain");

  await Assert.rejects(
    request[funcName]("Hai!"),
    /Request has already been sent/
  );

  await promiseStopServer(server);
}

/**
 * Test HTTP PATCH with a simple string argument and default Content-Type.
 */
add_task(async function test_patch() {
  await check_posting_data("PATCH");
});

/**
 * Test HTTP PUT with a simple string argument and default Content-Type.
 */
add_task(async function test_put() {
  await check_posting_data("PUT");
});

/**
 * Test HTTP POST with a simple string argument and default Content-Type.
 */
add_task(async function test_post() {
  await check_posting_data("POST");
});

/**
 * Test HTTP DELETE.
 */
add_task(async function test_delete() {
  let handler = httpd_handler(200, "OK", "Got it!");
  let server = httpd_setup({ "/resource": handler });

  let request = new RESTRequest(server.baseURI + "/resource");
  Assert.equal(request.status, request.NOT_SENT);
  let responsePromise = request.delete();
  Assert.equal(request.status, request.SENT);
  Assert.equal(request.method, "DELETE");

  let response = await responsePromise;
  Assert.equal(response, request.response);

  Assert.equal(request.status, request.COMPLETED);
  Assert.ok(request.response.success);
  Assert.equal(request.response.status, 200);
  Assert.equal(request.response.body, "Got it!");
  Assert.equal(handler.request.method, "DELETE");

  await Assert.rejects(request.delete(), /Request has already been sent/);

  await promiseStopServer(server);
});

/**
 * Test an HTTP response with a non-200 status code.
 */
add_task(async function test_get_404() {
  let handler = httpd_handler(404, "Not Found", "Cannae find it!");
  let server = httpd_setup({ "/resource": handler });

  let request = new RESTRequest(server.baseURI + "/resource");
  await request.get();

  Assert.equal(request.status, request.COMPLETED);
  Assert.ok(!request.response.success);
  Assert.equal(request.response.status, 404);
  Assert.equal(request.response.body, "Cannae find it!");

  await promiseStopServer(server);
});

/**
 * The 'data' argument to PUT, if not a string already, is automatically
 * stringified as JSON.
 */
add_task(async function test_put_json() {
  let handler = httpd_handler(200, "OK");
  let server = httpd_setup({ "/resource": handler });

  let sample_data = {
    some: "sample_data",
    injson: "format",
    number: 42,
  };
  let request = new RESTRequest(server.baseURI + "/resource");
  await request.put(sample_data);

  Assert.equal(request.status, request.COMPLETED);
  Assert.ok(request.response.success);
  Assert.equal(request.response.status, 200);
  Assert.equal(request.response.body, "");

  Assert.equal(handler.request.method, "PUT");
  Assert.equal(handler.request.body, JSON.stringify(sample_data));
  Assert.equal(
    handler.request.getHeader("Content-Type"),
    "application/json; charset=utf-8"
  );

  await promiseStopServer(server);
});

/**
 * The 'data' argument to POST, if not a string already, is automatically
 * stringified as JSON.
 */
add_task(async function test_post_json() {
  let handler = httpd_handler(200, "OK");
  let server = httpd_setup({ "/resource": handler });

  let sample_data = {
    some: "sample_data",
    injson: "format",
    number: 42,
  };
  let request = new RESTRequest(server.baseURI + "/resource");
  await request.post(sample_data);

  Assert.equal(request.status, request.COMPLETED);
  Assert.ok(request.response.success);
  Assert.equal(request.response.status, 200);
  Assert.equal(request.response.body, "");

  Assert.equal(handler.request.method, "POST");
  Assert.equal(handler.request.body, JSON.stringify(sample_data));
  Assert.equal(
    handler.request.getHeader("Content-Type"),
    "application/json; charset=utf-8"
  );

  await promiseStopServer(server);
});

/**
 * The content-type will be text/plain without a charset if the 'data' argument
 * to POST is already a string.
 */
add_task(async function test_post_json() {
  let handler = httpd_handler(200, "OK");
  let server = httpd_setup({ "/resource": handler });

  let sample_data = "hello";
  let request = new RESTRequest(server.baseURI + "/resource");
  await request.post(sample_data);
  Assert.equal(request.status, request.COMPLETED);
  Assert.ok(request.response.success);
  Assert.equal(request.response.status, 200);
  Assert.equal(request.response.body, "");

  Assert.equal(handler.request.method, "POST");
  Assert.equal(handler.request.body, sample_data);
  Assert.equal(handler.request.getHeader("Content-Type"), "text/plain");

  await promiseStopServer(server);
});

/**
 * HTTP PUT with a custom Content-Type header.
 */
add_task(async function test_put_override_content_type() {
  let handler = httpd_handler(200, "OK");
  let server = httpd_setup({ "/resource": handler });

  let request = new RESTRequest(server.baseURI + "/resource");
  request.setHeader("Content-Type", "application/lolcat");
  await request.put("O HAI!!1!");

  Assert.equal(request.status, request.COMPLETED);
  Assert.ok(request.response.success);
  Assert.equal(request.response.status, 200);
  Assert.equal(request.response.body, "");

  Assert.equal(handler.request.method, "PUT");
  Assert.equal(handler.request.body, "O HAI!!1!");
  Assert.equal(handler.request.getHeader("Content-Type"), "application/lolcat");

  await promiseStopServer(server);
});

/**
 * HTTP POST with a custom Content-Type header.
 */
add_task(async function test_post_override_content_type() {
  let handler = httpd_handler(200, "OK");
  let server = httpd_setup({ "/resource": handler });

  let request = new RESTRequest(server.baseURI + "/resource");
  request.setHeader("Content-Type", "application/lolcat");
  await request.post("O HAI!!1!");

  Assert.equal(request.status, request.COMPLETED);
  Assert.ok(request.response.success);
  Assert.equal(request.response.status, 200);
  Assert.equal(request.response.body, "");

  Assert.equal(handler.request.method, "POST");
  Assert.equal(handler.request.body, "O HAI!!1!");
  Assert.equal(handler.request.getHeader("Content-Type"), "application/lolcat");

  await promiseStopServer(server);
});

/**
 * No special headers are sent by default on a GET request.
 */
add_task(async function test_get_no_headers() {
  let handler = httpd_handler(200, "OK");
  let server = httpd_setup({ "/resource": handler });

  let ignore_headers = [
    "host",
    "user-agent",
    "accept",
    "accept-language",
    "accept-encoding",
    "accept-charset",
    "keep-alive",
    "connection",
    "pragma",
    "cache-control",
    "content-length",
    "sec-fetch-dest",
    "sec-fetch-mode",
    "sec-fetch-site",
    "sec-fetch-user",
  ];
  let request = new RESTRequest(server.baseURI + "/resource");
  await request.get();

  Assert.equal(request.response.status, 200);
  Assert.equal(request.response.body, "");

  let server_headers = handler.request.headers;
  while (server_headers.hasMoreElements()) {
    let header = server_headers.getNext().toString();
    if (!ignore_headers.includes(header)) {
      do_throw("Got unexpected header!");
    }
  }

  await promiseStopServer(server);
});

/**
 * Client includes default Accept header in API requests
 */
add_task(async function test_default_accept_headers() {
  let handler = httpd_handler(200, "OK");
  let server = httpd_setup({ "/resource": handler });

  let request = new RESTRequest(server.baseURI + "/resource");
  await request.get();

  Assert.equal(request.response.status, 200);
  Assert.equal(request.response.body, "");

  let accept_header = handler.request.getHeader("accept");

  Assert.ok(!accept_header.includes("text/html"));
  Assert.ok(!accept_header.includes("application/xhtml+xml"));
  Assert.ok(!accept_header.includes("applcation/xml"));

  Assert.ok(
    accept_header.includes("application/json") ||
      accept_header.includes("application/newlines")
  );

  await promiseStopServer(server);
});

/**
 * Test changing the URI after having created the request.
 */
add_task(async function test_changing_uri() {
  let handler = httpd_handler(200, "OK");
  let server = httpd_setup({ "/resource": handler });

  let request = new RESTRequest("http://localhost:1234/the-wrong-resource");
  request.uri = CommonUtils.makeURI(server.baseURI + "/resource");
  let response = await request.get();
  Assert.equal(response.status, 200);
  await promiseStopServer(server);
});

/**
 * Test setting HTTP request headers.
 */
add_task(async function test_request_setHeader() {
  let handler = httpd_handler(200, "OK");
  let server = httpd_setup({ "/resource": handler });

  let request = new RESTRequest(server.baseURI + "/resource");

  request.setHeader("X-What-Is-Weave", "awesome");
  request.setHeader("X-WHAT-is-Weave", "more awesomer");
  request.setHeader("Another-Header", "Hello World");
  await request.get();

  Assert.equal(request.response.status, 200);
  Assert.equal(request.response.body, "");

  Assert.equal(handler.request.getHeader("X-What-Is-Weave"), "more awesomer");
  Assert.equal(handler.request.getHeader("another-header"), "Hello World");

  await promiseStopServer(server);
});

/**
 * Test receiving HTTP response headers.
 */
add_task(async function test_response_headers() {
  function handler(request, response) {
    response.setHeader("X-What-Is-Weave", "awesome");
    response.setHeader("Another-Header", "Hello World");
    response.setStatusLine(request.httpVersion, 200, "OK");
  }
  let server = httpd_setup({ "/resource": handler });
  let request = new RESTRequest(server.baseURI + "/resource");
  await request.get();

  Assert.equal(request.response.status, 200);
  Assert.equal(request.response.body, "");

  Assert.equal(request.response.headers["x-what-is-weave"], "awesome");
  Assert.equal(request.response.headers["another-header"], "Hello World");

  await promiseStopServer(server);
});

/**
 * The onComplete() handler gets called in case of any network errors
 * (e.g. NS_ERROR_CONNECTION_REFUSED).
 */
add_task(async function test_connection_refused() {
  let request = new RESTRequest("http://localhost:1234/resource");

  // Fail the test if we resolve, return the error if we reject
  await Assert.rejects(
    request.get(),
    error =>
      error.result == Cr.NS_ERROR_CONNECTION_REFUSED &&
      error.message == "NS_ERROR_CONNECTION_REFUSED"
  );

  Assert.equal(request.status, request.COMPLETED);
});

/**
 * Abort a request that just sent off.
 */
add_task(async function test_abort() {
  function handler() {
    do_throw("Shouldn't have gotten here!");
  }
  let server = httpd_setup({ "/resource": handler });

  let request = new RESTRequest(server.baseURI + "/resource");

  // Aborting a request that hasn't been sent yet is pointless and will throw.
  do_check_throws(function () {
    request.abort();
  });

  let responsePromise = request.get();
  request.abort();

  // Aborting an already aborted request is pointless and will throw.
  do_check_throws(function () {
    request.abort();
  });

  Assert.equal(request.status, request.ABORTED);

  await Assert.rejects(responsePromise, /NS_BINDING_ABORTED/);

  await promiseStopServer(server);
});

/**
 * A non-zero 'timeout' property specifies the amount of seconds to wait after
 * channel activity until the request is automatically canceled.
 */
add_task(async function test_timeout() {
  let server = new HttpServer();
  let server_connection;
  server._handler.handleResponse = function (connection) {
    // This is a handler that doesn't do anything, just keeps the connection
    // open, thereby mimicking a timing out connection. We keep a reference to
    // the open connection for later so it can be properly disposed of. That's
    // why you really only want to make one HTTP request to this server ever.
    server_connection = connection;
  };
  server.start();
  let identity = server.identity;
  let uri =
    identity.primaryScheme +
    "://" +
    identity.primaryHost +
    ":" +
    identity.primaryPort;

  let request = new RESTRequest(uri + "/resource");
  request.timeout = 0.1; // 100 milliseconds

  await Assert.rejects(
    request.get(),
    error => error.result == Cr.NS_ERROR_NET_TIMEOUT
  );

  Assert.equal(request.status, request.ABORTED);

  // server_connection is undefined on the Android emulator for reasons
  // unknown. Yet, we still get here. If this test is refactored, we should
  // investigate the reason why the above callback is behaving differently.
  if (server_connection) {
    _("Closing connection.");
    server_connection.close();
  }
  await promiseStopServer(server);
});

add_task(async function test_new_channel() {
  _("Ensure a redirect to a new channel is handled properly.");

  function checkUA(metadata) {
    let ua = metadata.getHeader("User-Agent");
    _("User-Agent is " + ua);
    Assert.equal("foo bar", ua);
  }

  let redirectRequested = false;
  let redirectURL;
  function redirectHandler(metadata, response) {
    checkUA(metadata);
    redirectRequested = true;

    let body = "Redirecting";
    response.setStatusLine(metadata.httpVersion, 307, "TEMPORARY REDIRECT");
    response.setHeader("Location", redirectURL);
    response.bodyOutputStream.write(body, body.length);
  }

  let resourceRequested = false;
  function resourceHandler(metadata, response) {
    checkUA(metadata);
    resourceRequested = true;

    let body = "Test";
    response.setHeader("Content-Type", "text/plain");
    response.bodyOutputStream.write(body, body.length);
  }

  let server1 = httpd_setup({ "/redirect": redirectHandler });
  let server2 = httpd_setup({ "/resource": resourceHandler });
  redirectURL = server2.baseURI + "/resource";

  let request = new RESTRequest(server1.baseURI + "/redirect");
  request.setHeader("User-Agent", "foo bar");

  // Swizzle in our own fakery, because this redirect is neither
  // internal nor URI-preserving. RESTRequest's policy is to only
  // copy headers under certain circumstances.
  let protoMethod = request.shouldCopyOnRedirect;
  request.shouldCopyOnRedirect = function wrapped(o, n, f) {
    // Check the default policy.
    Assert.ok(!protoMethod.call(this, o, n, f));
    return true;
  };

  let response = await request.get();

  Assert.equal(200, response.status);
  Assert.equal("Test", response.body);
  Assert.ok(redirectRequested);
  Assert.ok(resourceRequested);

  await promiseStopServer(server1);
  await promiseStopServer(server2);
});

add_task(async function test_not_sending_cookie() {
  function handler(metadata, response) {
    let body = "COOKIE!";
    response.setStatusLine(metadata.httpVersion, 200, "OK");
    response.bodyOutputStream.write(body, body.length);
    Assert.ok(!metadata.hasHeader("Cookie"));
  }
  let server = httpd_setup({ "/test": handler });

  let uri = CommonUtils.makeURI(server.baseURI);
  let channel = NetUtil.newChannel({
    uri,
    loadUsingSystemPrincipal: true,
    contentPolicyType: Ci.nsIContentPolicy.TYPE_DOCUMENT,
  });
  Services.cookies.setCookieStringFromHttp(uri, "test=test; path=/;", channel);

  let res = new RESTRequest(server.baseURI + "/test");
  let response = await res.get();

  Assert.ok(response.success);
  Assert.equal("COOKIE!", response.body);

  await promiseStopServer(server);
});
