netwerk/test/unit/test_spdy.js

Wed, 31 Dec 2014 13:27:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 13:27:57 +0100
branch
TOR_BUG_3246
changeset 6
8bccb770b82d
permissions
-rw-r--r--

Ignore runtime configuration files generated during quality assurance.

     1 // test spdy/3
     3 var Ci = Components.interfaces;
     4 var Cc = Components.classes;
     6 // Generate a small and a large post with known pre-calculated md5 sums
     7 function generateContent(size) {
     8   var content = "";
     9   for (var i = 0; i < size; i++) {
    10     content += "0";
    11   }
    12   return content;
    13 }
    15 var posts = [];
    16 posts.push(generateContent(10));
    17 posts.push(generateContent(250000));
    19 // pre-calculated md5sums (in hex) of the above posts
    20 var md5s = ['f1b708bba17f1ce948dc979f4d7092bc',
    21             '2ef8d3b6c8f329318eb1a119b12622b6'];
    23 var bigListenerData = generateContent(128 * 1024);
    24 var bigListenerMD5 = '8f607cfdd2c87d6a7eedb657dafbd836';
    26 function checkIsSpdy(request) {
    27   try {
    28     if (request.getResponseHeader("X-Firefox-Spdy") == "3") {
    29       if (request.getResponseHeader("X-Connection-Spdy") == "yes") {
    30         return true;
    31       }
    32       return false; // Weird case, but the server disagrees with us
    33     }
    34   } catch (e) {
    35     // Nothing to do here
    36   }
    37   return false;
    38 }
    40 var SpdyCheckListener = function() {};
    42 SpdyCheckListener.prototype = {
    43   onStartRequestFired: false,
    44   onDataAvailableFired: false,
    45   isSpdyConnection: false,
    47   onStartRequest: function testOnStartRequest(request, ctx) {
    48     this.onStartRequestFired = true;
    50     if (!Components.isSuccessCode(request.status))
    51       do_throw("Channel should have a success code! (" + request.status + ")");
    52     if (!(request instanceof Components.interfaces.nsIHttpChannel))
    53       do_throw("Expecting an HTTP channel");
    55     do_check_eq(request.responseStatus, 200);
    56     do_check_eq(request.requestSucceeded, true);
    57   },
    59   onDataAvailable: function testOnDataAvailable(request, ctx, stream, off, cnt) {
    60     this.onDataAvailableFired = true;
    61     this.isSpdyConnection = checkIsSpdy(request);
    63     read_stream(stream, cnt);
    64   },
    66   onStopRequest: function testOnStopRequest(request, ctx, status) {
    67     do_check_true(this.onStartRequestFired);
    68     do_check_true(this.onDataAvailableFired);
    69     do_check_true(this.isSpdyConnection);
    71     run_next_test();
    72     do_test_finished();
    73   }
    74 };
    76 /*
    77  * Support for testing valid multiplexing of streams
    78  */
    80 var multiplexContent = generateContent(30*1024);
    81 var completed_channels = [];
    82 function register_completed_channel(listener) {
    83   completed_channels.push(listener);
    84   if (completed_channels.length == 2) {
    85     do_check_neq(completed_channels[0].streamID, completed_channels[1].streamID);
    86     run_next_test();
    87     do_test_finished();
    88   }
    89 }
    91 /* Listener class to control the testing of multiplexing */
    92 var SpdyMultiplexListener = function() {};
    94 SpdyMultiplexListener.prototype = new SpdyCheckListener();
    96 SpdyMultiplexListener.prototype.streamID = 0;
    97 SpdyMultiplexListener.prototype.buffer = "";
    99 SpdyMultiplexListener.prototype.onDataAvailable = function(request, ctx, stream, off, cnt) {
   100   this.onDataAvailableFired = true;
   101   this.isSpdyConnection = checkIsSpdy(request);
   102   this.streamID = parseInt(request.getResponseHeader("X-Spdy-StreamID"));
   103   var data = read_stream(stream, cnt);
   104   this.buffer = this.buffer.concat(data);
   105 };
   107 SpdyMultiplexListener.prototype.onStopRequest = function(request, ctx, status) {
   108   do_check_true(this.onStartRequestFired);
   109   do_check_true(this.onDataAvailableFired);
   110   do_check_true(this.isSpdyConnection);
   111   do_check_true(this.buffer == multiplexContent);
   113   // This is what does most of the hard work for us
   114   register_completed_channel(this);
   115 };
   117 // Does the appropriate checks for header gatewaying
   118 var SpdyHeaderListener = function(value) {
   119   this.value = value
   120 };
   122 SpdyHeaderListener.prototype = new SpdyCheckListener();
   123 SpdyHeaderListener.prototype.value = "";
   125 SpdyHeaderListener.prototype.onDataAvailable = function(request, ctx, stream, off, cnt) {
   126   this.onDataAvailableFired = true;
   127   this.isSpdyConnection = checkIsSpdy(request);
   128   do_check_eq(request.getResponseHeader("X-Received-Test-Header"), this.value);
   129   read_stream(stream, cnt);
   130 };
   132 var SpdyPushListener = function() {};
   134 SpdyPushListener.prototype = new SpdyCheckListener();
   136 SpdyPushListener.prototype.onDataAvailable = function(request, ctx, stream, off, cnt) {
   137   this.onDataAvailableFired = true;
   138   this.isSpdyConnection = checkIsSpdy(request);
   139   if (ctx.originalURI.spec == "https://localhost:4443/push.js" ||
   140       ctx.originalURI.spec == "https://localhost:4443/push2.js") {
   141     do_check_eq(request.getResponseHeader("pushed"), "yes");
   142   }
   143   read_stream(stream, cnt);
   144 };
   146 // Does the appropriate checks for a large GET response
   147 var SpdyBigListener = function() {};
   149 SpdyBigListener.prototype = new SpdyCheckListener();
   150 SpdyBigListener.prototype.buffer = "";
   152 SpdyBigListener.prototype.onDataAvailable = function(request, ctx, stream, off, cnt) {
   153   this.onDataAvailableFired = true;
   154   this.isSpdyConnection = checkIsSpdy(request);
   155   this.buffer = this.buffer.concat(read_stream(stream, cnt));
   156   // We know the server should send us the same data as our big post will be,
   157   // so the md5 should be the same
   158   do_check_eq(bigListenerMD5, request.getResponseHeader("X-Expected-MD5"));
   159 };
   161 SpdyBigListener.prototype.onStopRequest = function(request, ctx, status) {
   162   do_check_true(this.onStartRequestFired);
   163   do_check_true(this.onDataAvailableFired);
   164   do_check_true(this.isSpdyConnection);
   166   // Don't want to flood output, so don't use do_check_eq
   167   do_check_true(this.buffer == bigListenerData);
   169   run_next_test();
   170   do_test_finished();
   171 };
   173 // Does the appropriate checks for POSTs
   174 var SpdyPostListener = function(expected_md5) {
   175   this.expected_md5 = expected_md5;
   176 };
   178 SpdyPostListener.prototype = new SpdyCheckListener();
   179 SpdyPostListener.prototype.expected_md5 = "";
   181 SpdyPostListener.prototype.onDataAvailable = function(request, ctx, stream, off, cnt) {
   182   this.onDataAvailableFired = true;
   183   this.isSpdyConnection = checkIsSpdy(request);
   184   read_stream(stream, cnt);
   185   do_check_eq(this.expected_md5, request.getResponseHeader("X-Calculated-MD5"));
   186 };
   188 function makeChan(url) {
   189   var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
   190   var chan = ios.newChannel(url, null, null).QueryInterface(Ci.nsIHttpChannel);
   192   return chan;
   193 }
   195 // Make sure we make a spdy connection and both us and the server mark it as such
   196 function test_spdy_basic() {
   197   var chan = makeChan("https://localhost:4443/");
   198   var listener = new SpdyCheckListener();
   199   chan.asyncOpen(listener, null);
   200 }
   202 // Support for making sure XHR works over SPDY
   203 function checkXhr(xhr) {
   204   if (xhr.readyState != 4) {
   205     return;
   206   }
   208   do_check_eq(xhr.status, 200);
   209   do_check_eq(checkIsSpdy(xhr), true);
   210   run_next_test();
   211   do_test_finished();
   212 }
   214 // Fires off an XHR request over SPDY
   215 function test_spdy_xhr() {
   216   var req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
   217             .createInstance(Ci.nsIXMLHttpRequest);
   218   req.open("GET", "https://localhost:4443/", true);
   219   req.addEventListener("readystatechange", function (evt) { checkXhr(req); },
   220                        false);
   221   req.send(null);
   222 }
   224 // Test to make sure we get multiplexing right
   225 function test_spdy_multiplex() {
   226   var chan1 = makeChan("https://localhost:4443/multiplex1");
   227   var chan2 = makeChan("https://localhost:4443/multiplex2");
   228   var listener1 = new SpdyMultiplexListener();
   229   var listener2 = new SpdyMultiplexListener();
   230   chan1.asyncOpen(listener1, null);
   231   chan2.asyncOpen(listener2, null);
   232 }
   234 // Test to make sure we gateway non-standard headers properly
   235 function test_spdy_header() {
   236   var chan = makeChan("https://localhost:4443/header");
   237   var hvalue = "Headers are fun";
   238   var listener = new SpdyHeaderListener(hvalue);
   239   chan.setRequestHeader("X-Test-Header", hvalue, false);
   240   chan.asyncOpen(listener, null);
   241 }
   243 function test_spdy_push1() {
   244   var chan = makeChan("https://localhost:4443/push");
   245   chan.loadGroup = loadGroup;
   246   var listener = new SpdyPushListener();
   247   chan.asyncOpen(listener, chan);
   248 }
   250 function test_spdy_push2() {
   251   var chan = makeChan("https://localhost:4443/push.js");
   252   chan.loadGroup = loadGroup;
   253   var listener = new SpdyPushListener();
   254   chan.asyncOpen(listener, chan);
   255 }
   257 function test_spdy_push3() {
   258   var chan = makeChan("https://localhost:4443/push2");
   259   chan.loadGroup = loadGroup;
   260   var listener = new SpdyPushListener();
   261   chan.asyncOpen(listener, chan);
   262 }
   264 function test_spdy_push4() {
   265   var chan = makeChan("https://localhost:4443/push2.js");
   266   chan.loadGroup = loadGroup;
   267   var listener = new SpdyPushListener();
   268   chan.asyncOpen(listener, chan);
   269 }
   271 // Make sure we handle GETs that cover more than 2 frames properly
   272 function test_spdy_big() {
   273   var chan = makeChan("https://localhost:4443/big");
   274   var listener = new SpdyBigListener();
   275   chan.asyncOpen(listener, null);
   276 }
   278 // Support for doing a POST
   279 function do_post(content, chan, listener) {
   280   var stream = Cc["@mozilla.org/io/string-input-stream;1"]
   281                .createInstance(Ci.nsIStringInputStream);
   282   stream.data = content;
   284   var uchan = chan.QueryInterface(Ci.nsIUploadChannel);
   285   uchan.setUploadStream(stream, "text/plain", stream.available());
   287   chan.requestMethod = "POST";
   289   chan.asyncOpen(listener, null);
   290 }
   292 // Make sure we can do a simple POST
   293 function test_spdy_post() {
   294   var chan = makeChan("https://localhost:4443/post");
   295   var listener = new SpdyPostListener(md5s[0]);
   296   do_post(posts[0], chan, listener);
   297 }
   299 // Make sure we can do a POST that covers more than 2 frames
   300 function test_spdy_post_big() {
   301   var chan = makeChan("https://localhost:4443/post");
   302   var listener = new SpdyPostListener(md5s[1]);
   303   do_post(posts[1], chan, listener);
   304 }
   306 // hack - the header test resets the multiplex object on the server,
   307 // so make sure header is always run before the multiplex test.
   308 //
   309 // make sure post_big runs first to test race condition in restarting
   310 // a stalled stream when a SETTINGS frame arrives
   311 var tests = [ test_spdy_post_big
   312             , test_spdy_basic
   313             , test_spdy_push1
   314             , test_spdy_push2
   315             , test_spdy_push3
   316             , test_spdy_push4
   317             , test_spdy_xhr
   318             , test_spdy_header
   319             , test_spdy_multiplex
   320             , test_spdy_big
   321             , test_spdy_post
   322             ];
   323 var current_test = 0;
   325 function run_next_test() {
   326   if (current_test < tests.length) {
   327     tests[current_test]();
   328     current_test++;
   329     do_test_pending();
   330   }
   331 }
   333 // Support for making sure we can talk to the invalid cert the server presents
   334 var CertOverrideListener = function(host, port, bits) {
   335   this.host = host;
   336   if (port) {
   337     this.port = port;
   338   }
   339   this.bits = bits;
   340 };
   342 CertOverrideListener.prototype = {
   343   host: null,
   344   port: -1,
   345   bits: null,
   347   getInterface: function(aIID) {
   348     return this.QueryInterface(aIID);
   349   },
   351   QueryInterface: function(aIID) {
   352     if (aIID.equals(Ci.nsIBadCertListener2) ||
   353         aIID.equals(Ci.nsIInterfaceRequestor) ||
   354         aIID.equals(Ci.nsISupports))
   355       return this;
   356     throw Components.results.NS_ERROR_NO_INTERFACE;
   357   },
   359   notifyCertProblem: function(socketInfo, sslStatus, targetHost) {
   360     var cert = sslStatus.QueryInterface(Ci.nsISSLStatus).serverCert;
   361     var cos = Cc["@mozilla.org/security/certoverride;1"].
   362               getService(Ci.nsICertOverrideService);
   363     cos.rememberValidityOverride(this.host, this.port, cert, this.bits, false);
   364     return true;
   365   },
   366 };
   368 function addCertOverride(host, port, bits) {
   369   var req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
   370             .createInstance(Ci.nsIXMLHttpRequest);
   371   try {
   372     var url;
   373     if (port) {
   374       url = "https://" + host + ":" + port + "/";
   375     } else {
   376       url = "https://" + host + "/";
   377     }
   378     req.open("GET", url, false);
   379     req.channel.notificationCallbacks = new CertOverrideListener(host, port, bits);
   380     req.send(null);
   381   } catch (e) {
   382     // This will fail since the server is not trusted yet
   383   }
   384 }
   386 var prefs;
   387 var spdypref;
   388 var spdy3pref;
   389 var spdypush;
   391 var loadGroup;
   393 function resetPrefs() {
   394   prefs.setBoolPref("network.http.spdy.enabled", spdypref);
   395   prefs.setBoolPref("network.http.spdy.enabled.v3", spdy3pref);
   396   prefs.setBoolPref("network.http.spdy.allow-push", spdypush);
   397 }
   399 function run_test() {
   400   // Set to allow the cert presented by our SPDY server
   401   do_get_profile();
   402   var prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
   403   var oldPref = prefs.getIntPref("network.http.speculative-parallel-limit");
   404   prefs.setIntPref("network.http.speculative-parallel-limit", 0);
   406   addCertOverride("localhost", 4443,
   407                   Ci.nsICertOverrideService.ERROR_UNTRUSTED |
   408                   Ci.nsICertOverrideService.ERROR_MISMATCH |
   409                   Ci.nsICertOverrideService.ERROR_TIME);
   411   prefs.setIntPref("network.http.speculative-parallel-limit", oldPref);
   413   // Enable all versions of spdy to see that we auto negotiate spdy/3
   414   spdypref = prefs.getBoolPref("network.http.spdy.enabled");
   415   spdy3pref = prefs.getBoolPref("network.http.spdy.enabled.v3");
   416   spdypush = prefs.getBoolPref("network.http.spdy.allow-push");
   417   prefs.setBoolPref("network.http.spdy.enabled", true);
   418   prefs.setBoolPref("network.http.spdy.enabled.v3", true);
   419   prefs.setBoolPref("network.http.spdy.allow-push", true);
   421   loadGroup = Cc["@mozilla.org/network/load-group;1"].createInstance(Ci.nsILoadGroup);
   423   // And make go!
   424   run_next_test();
   425 }

mercurial