Bug 1283319 - Tests for connection info's hash key. r=mayhemer
authorJonathan Hao <jhao@mozilla.com>
Wed, 17 Aug 2016 19:57:00 -0400
changeset 310587 ac8be09dc64970021e8e04ba2a20564cae8e81dc
parent 310586 e2abf925226d246e93235bfdd5b8dc31d64dfa04
child 310588 7437baa4d4598eae5372508a00a4be6fc9a8a335
push id80896
push userryanvm@gmail.com
push dateMon, 22 Aug 2016 13:54:17 +0000
treeherdermozilla-inbound@ac8be09dc649 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmayhemer
bugs1283319
milestone51.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 1283319 - Tests for connection info's hash key. r=mayhemer
netwerk/protocol/http/HttpBaseChannel.cpp
netwerk/protocol/http/HttpBaseChannel.h
netwerk/protocol/http/nsIHttpChannelInternal.idl
netwerk/test/unit/test_separate_connections.js
netwerk/test/unit/xpcshell.ini
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -3513,10 +3513,20 @@ NS_IMETHODIMP
 HttpBaseChannel::SetBlockAuthPrompt(bool aValue)
 {
   ENSURE_CALLED_BEFORE_CONNECT();
 
   mBlockAuthPrompt = aValue;
   return NS_OK;
 }
 
+NS_IMETHODIMP
+HttpBaseChannel::GetConnectionInfoHashKey(nsACString& aConnectionInfoHashKey)
+{
+  if (!mConnectionInfo) {
+    return NS_ERROR_FAILURE;
+  }
+  aConnectionInfoHashKey.Assign(mConnectionInfo->HashKey());
+  return NS_OK;
+}
+
 } // namespace net
 } // namespace mozilla
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -225,16 +225,17 @@ public:
   NS_IMETHOD SetCorsMode(uint32_t aCorsMode) override;
   NS_IMETHOD GetRedirectMode(uint32_t* aRedirectMode) override;
   NS_IMETHOD SetRedirectMode(uint32_t aRedirectMode) override;
   NS_IMETHOD GetFetchCacheMode(uint32_t* aFetchCacheMode) override;
   NS_IMETHOD SetFetchCacheMode(uint32_t aFetchCacheMode) override;
   NS_IMETHOD GetTopWindowURI(nsIURI **aTopWindowURI) override;
   NS_IMETHOD GetProxyURI(nsIURI **proxyURI) override;
   virtual void SetCorsPreflightParameters(const nsTArray<nsCString>& unsafeHeaders) override;
+  NS_IMETHOD GetConnectionInfoHashKey(nsACString& aConnectionInfoHashKey) override;
 
   inline void CleanRedirectCacheChainIfNecessary()
   {
       mRedirectedCachekeys = nullptr;
   }
   NS_IMETHOD HTTPUpgrade(const nsACString & aProtocolName,
                          nsIHttpUpgradeListener *aListener) override;
 
--- a/netwerk/protocol/http/nsIHttpChannelInternal.idl
+++ b/netwerk/protocol/http/nsIHttpChannelInternal.idl
@@ -281,9 +281,14 @@ interface nsIHttpChannelInternal : nsISu
     /**
      * When set to true, the channel will not pop any authentication prompts up
      * to the user.  When provided or cached credentials lead to an
      * authentication failure, that failure will be propagated to the channel
      * listener.  Must be called before opening the channel, otherwise throws.
      */
     [infallible]
     attribute boolean blockAuthPrompt;
+
+    /**
+     * The connection info's hash key. We use it to test connection separation.
+     */
+    readonly attribute ACString connectionInfoHashKey;
 };
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit/test_separate_connections.js
@@ -0,0 +1,99 @@
+Cu.import("resource://testing-common/httpd.js");
+Cu.import("resource://gre/modules/NetUtil.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyGetter(this, "URL", function() {
+  return "http://localhost:" + httpserv.identity.primaryPort;
+});
+
+// This unit test ensures each container has its own connection pool.
+// We verify this behavior by opening channels with different userContextId,
+// and their connection info's hash keys should be different.
+
+// In the first round of this test, we record the hash key in each container.
+// In the second round, we check if each container's hash key is consistent
+// and different from other container's hash key.
+
+let httpserv = null;
+let gSecondRoundStarted = false;
+
+function handler(metadata, response) {
+  response.setHeader("Content-Type", "text/plain", false);
+  response.setHeader("Cache-Control", "no-cache", false);
+  response.setStatusLine(metadata.httpVersion, 200, "OK");
+  let body = "0123456789";
+  response.bodyOutputStream.write(body, body.length);
+}
+
+function makeChan(url, userContextId) {
+  let chan = NetUtil.newChannel({ uri: url, loadUsingSystemPrincipal: true });
+  chan.loadInfo.originAttributes = { userContextId: userContextId };
+  return chan;
+}
+
+let previousHashKeys = [];
+
+function Listener(userContextId) {
+  this.userContextId = userContextId;
+}
+
+let gTestsRun = 0;
+Listener.prototype = {
+  onStartRequest: function(request, context) {
+    request.QueryInterface(Ci.nsIHttpChannel)
+           .QueryInterface(Ci.nsIHttpChannelInternal);
+
+    do_check_eq(request.loadInfo.originAttributes.userContextId, this.userContextId);
+
+    let hashKey = request.connectionInfoHashKey;
+    if (gSecondRoundStarted) {
+      // Compare the hash keys with the previous set ones.
+      // Hash keys should match if and only if their userContextId are the same.
+      for (let userContextId = 0; userContextId < 3; userContextId++) {
+        if (userContextId == this.userContextId) {
+          do_check_eq(hashKey, previousHashKeys[userContextId]);
+        } else {
+          do_check_neq(hashKey, previousHashKeys[userContextId]);
+        }
+      }
+    } else {
+      // Set the hash keys in the first round.
+      previousHashKeys[this.userContextId] = hashKey;
+    }
+  },
+  onDataAvailable: function(request, ctx, stream, off, cnt) {
+    read_stream(stream, cnt);
+  },
+  onStopRequest: function() {
+    gTestsRun++;
+    if (gTestsRun == 3) {
+      gTestsRun = 0;
+      if (gSecondRoundStarted) {
+        // The second round finishes.
+        httpserv.stop(do_test_finished);
+      } else {
+        // The first round finishes. Do the second round.
+        gSecondRoundStarted = true;
+        doTest();
+      }
+    }
+  },
+};
+
+function doTest() {
+  for (let userContextId = 0; userContextId < 3; userContextId++) {
+    let chan = makeChan(URL, userContextId);
+    let listener = new Listener(userContextId);
+    chan.asyncOpen2(listener);
+  }
+}
+
+function run_test() {
+  do_test_pending();
+  httpserv = new HttpServer();
+  httpserv.registerPathHandler("/", handler);
+  httpserv.start(-1);
+
+  doTest();
+}
+
--- a/netwerk/test/unit/xpcshell.ini
+++ b/netwerk/test/unit/xpcshell.ini
@@ -355,8 +355,9 @@ skip-if = os == "android"
 [test_bug1195415.js]
 [test_cookie_blacklist.js]
 [test_getHost.js]
 [test_packaged_app_bug1214079.js]
 [test_bug412457.js]
 [test_bug464591.js]
 [test_cache-control_request.js]
 [test_bug1279246.js]
+[test_separate_connections.js]