Bug 1274556 - Add a channelId attribute to nsIHttpChannel (unit test) r=hurley
authorJarda Snajdr <jsnajdr@gmail.com>
Wed, 01 Jun 2016 12:30:27 -0700
changeset 339002 16042a3a145c271f6937cb25ca53f65614a61543
parent 339001 dc171b5fafc0742057f24d66dd76818f95c86656
child 339003 4b44dee24a773b23b7214107ddd39556fbe3f071
push id6249
push userjlund@mozilla.com
push dateMon, 01 Aug 2016 13:59:36 +0000
treeherdermozilla-beta@bad9d4f5bf7e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewershurley
bugs1274556
milestone49.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1274556 - Add a channelId attribute to nsIHttpChannel (unit test) r=hurley
netwerk/test/unit_ipc/child_channel_id.js
netwerk/test/unit_ipc/test_channel_id.js
netwerk/test/unit_ipc/xpcshell.ini
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit_ipc/child_channel_id.js
@@ -0,0 +1,45 @@
+/**
+ * Send HTTP requests and notify the parent about their channelId
+ */
+
+Cu.import("resource://gre/modules/NetUtil.jsm");
+
+let shouldQuit = false;
+
+function run_test() {
+  // keep the event loop busy and the test alive until a "finish" command
+  // is issued by parent
+  do_timeout(100, function keepAlive() {
+    if (!shouldQuit) {
+      do_timeout(100, keepAlive);
+    }
+  });
+}
+
+function makeRequest(uri) {
+  let requestChannel = NetUtil.newChannel({uri, loadUsingSystemPrincipal: true});
+  requestChannel.asyncOpen2(new ChannelListener(checkResponse, requestChannel));
+  requestChannel.QueryInterface(Ci.nsIHttpChannel);
+  dump(`Child opened request: ${uri}, channelId=${requestChannel.channelId}\n`);
+}
+
+function checkResponse(request, buffer, requestChannel) {
+  // notify the parent process about the original request channel
+  requestChannel.QueryInterface(Ci.nsIHttpChannel);
+  do_send_remote_message(`request:${requestChannel.channelId}`);
+
+  // the response channel can be different (if it was redirected)
+  let responseChannel = request.QueryInterface(Ci.nsIHttpChannel);
+
+  let uri = responseChannel.URI.spec;
+  let origUri = responseChannel.originalURI.spec;
+  let id = responseChannel.channelId;
+  dump(`Child got response to: ${uri} (orig=${origUri}), channelId=${id}\n`);
+
+  // notify the parent process about this channel's ID
+  do_send_remote_message(`response:${id}`);
+}
+
+function finish() {
+  shouldQuit = true;
+}
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit_ipc/test_channel_id.js
@@ -0,0 +1,109 @@
+Cu.import("resource://testing-common/httpd.js");
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/Promise.jsm");
+
+/*
+ * Test that when doing HTTP requests, the nsIHttpChannel is detected in
+ * both parent and child and shares the same channelId across processes.
+ */
+
+let httpserver;
+let port;
+
+function startHttpServer() {
+  httpserver = new HttpServer();
+
+  httpserver.registerPathHandler("/resource", (metadata, response) => {
+    response.setStatusLine(metadata.httpVersion, 200, "OK");
+    response.setHeader("Content-Type", "text/plain", false);
+    response.setHeader("Cache-Control", "no-cache", false);
+    response.bodyOutputStream.write("data", 4);
+ });
+
+  httpserver.registerPathHandler("/redirect", (metadata, response) => {
+    response.setStatusLine(metadata.httpVersion, 302, "Redirect");
+    response.setHeader("Location", "/resource", false);
+    response.setHeader("Cache-Control", "no-cache", false);
+  });
+
+  httpserver.start(-1);
+  port = httpserver.identity.primaryPort;
+}
+
+function stopHttpServer(next) {
+ httpserver.stop(next);
+}
+
+let expectedParentChannels = [];
+let expectedChildMessages = [];
+
+let maybeFinishWaitForParentChannels;
+let parentChannelsDone = new Promise(resolve => {
+  maybeFinishWaitForParentChannels = () => {
+    if (expectedParentChannels.length == 0) {
+      dump("All expected parent channels were detected\n");
+      resolve();
+    }
+  };
+});
+
+function observer(subject, topic, data) {
+  let channel = subject.QueryInterface(Ci.nsIHttpChannel);
+
+  let uri = channel.URI.spec;
+  let origUri = channel.originalURI.spec;
+  let id = channel.channelId;
+  dump(`Parent detected channel: ${uri} (orig=${origUri}): channelId=${id}\n`);
+
+  // did we expect a new channel?
+  let expected = expectedParentChannels.shift();
+  do_check_true(!!expected);
+
+  // Start waiting for the messages about request/response from child
+  for (let event of expected) {
+    let message = `${event}:${id}`;
+    dump(`Expecting message from child: ${message}\n`);
+
+    let messagePromise = do_await_remote_message(message).then(() => {
+      dump(`Expected message from child arrived: ${message}\n`);
+    });
+    expectedChildMessages.push(messagePromise);
+  }
+
+  // If we don't expect any further parent channels, finish the parent wait
+  maybeFinishWaitForParentChannels();
+}
+
+function run_test() {
+  startHttpServer();
+  Services.obs.addObserver(observer, "http-on-modify-request", false);
+  run_test_in_child("child_channel_id.js", makeRequests);
+}
+
+function makeRequests() {
+  // First, a normal request without any redirect. Expect one channel detected
+  // in parent, used by both request and response.
+  expectedParentChannels.push(["request", "response"]);
+  sendCommand(`makeRequest("http://localhost:${port}/resource");`);
+
+  // Second request will be redirected. Expect two channels, one with the
+  // original request, then the redirected one which gets the final response.
+  expectedParentChannels.push(["request"], ["response"]);
+  sendCommand(`makeRequest("http://localhost:${port}/redirect");`);
+
+  waitForParentChannels();
+}
+
+function waitForParentChannels() {
+  parentChannelsDone.then(waitForChildMessages);
+}
+
+function waitForChildMessages() {
+  dump(`Waiting for ${expectedChildMessages.length} child messages\n`);
+  Promise.all(expectedChildMessages).then(finish);
+}
+
+function finish() {
+  Services.obs.removeObserver(observer, "http-on-modify-request");
+  sendCommand("finish();", () => stopHttpServer(do_test_finished));
+}
--- a/netwerk/test/unit_ipc/xpcshell.ini
+++ b/netwerk/test/unit_ipc/xpcshell.ini
@@ -1,14 +1,15 @@
 [DEFAULT]
 head = head_channels_clone.js head_cc.js
 tail =
 skip-if = toolkit == 'android' || toolkit == 'gonk'
 support-files = child_app_offline.js
   child_app_offline_notifications.js
+  child_channel_id.js
   !/netwerk/test/unit/test_XHR_redirects.js
   !/netwerk/test/unit/test_bug248970_cookie.js
   !/netwerk/test/unit/test_bug528292.js
   !/netwerk/test/unit/test_cache_jar.js
   !/netwerk/test/unit/test_cacheflags.js
   !/netwerk/test/unit/test_channel_close.js
   !/netwerk/test/unit/test_cookie_header.js
   !/netwerk/test/unit/test_cookiejars.js
@@ -89,8 +90,9 @@ skip-if = true
 [test_xmlhttprequest_wrap.js]
 [test_XHR_redirects.js]
 [test_redirect_history_wrap.js]
 [test_reply_without_content_type_wrap.js]
 [test_app_offline_http.js]
 [test_getHost_wrap.js]
 [test_app_offline_notifications.js]
 [test_original_sent_received_head_wrap.js]
+[test_channel_id.js]