1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/test/unit/test_spdy.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,425 @@ 1.4 +// test spdy/3 1.5 + 1.6 +var Ci = Components.interfaces; 1.7 +var Cc = Components.classes; 1.8 + 1.9 +// Generate a small and a large post with known pre-calculated md5 sums 1.10 +function generateContent(size) { 1.11 + var content = ""; 1.12 + for (var i = 0; i < size; i++) { 1.13 + content += "0"; 1.14 + } 1.15 + return content; 1.16 +} 1.17 + 1.18 +var posts = []; 1.19 +posts.push(generateContent(10)); 1.20 +posts.push(generateContent(250000)); 1.21 + 1.22 +// pre-calculated md5sums (in hex) of the above posts 1.23 +var md5s = ['f1b708bba17f1ce948dc979f4d7092bc', 1.24 + '2ef8d3b6c8f329318eb1a119b12622b6']; 1.25 + 1.26 +var bigListenerData = generateContent(128 * 1024); 1.27 +var bigListenerMD5 = '8f607cfdd2c87d6a7eedb657dafbd836'; 1.28 + 1.29 +function checkIsSpdy(request) { 1.30 + try { 1.31 + if (request.getResponseHeader("X-Firefox-Spdy") == "3") { 1.32 + if (request.getResponseHeader("X-Connection-Spdy") == "yes") { 1.33 + return true; 1.34 + } 1.35 + return false; // Weird case, but the server disagrees with us 1.36 + } 1.37 + } catch (e) { 1.38 + // Nothing to do here 1.39 + } 1.40 + return false; 1.41 +} 1.42 + 1.43 +var SpdyCheckListener = function() {}; 1.44 + 1.45 +SpdyCheckListener.prototype = { 1.46 + onStartRequestFired: false, 1.47 + onDataAvailableFired: false, 1.48 + isSpdyConnection: false, 1.49 + 1.50 + onStartRequest: function testOnStartRequest(request, ctx) { 1.51 + this.onStartRequestFired = true; 1.52 + 1.53 + if (!Components.isSuccessCode(request.status)) 1.54 + do_throw("Channel should have a success code! (" + request.status + ")"); 1.55 + if (!(request instanceof Components.interfaces.nsIHttpChannel)) 1.56 + do_throw("Expecting an HTTP channel"); 1.57 + 1.58 + do_check_eq(request.responseStatus, 200); 1.59 + do_check_eq(request.requestSucceeded, true); 1.60 + }, 1.61 + 1.62 + onDataAvailable: function testOnDataAvailable(request, ctx, stream, off, cnt) { 1.63 + this.onDataAvailableFired = true; 1.64 + this.isSpdyConnection = checkIsSpdy(request); 1.65 + 1.66 + read_stream(stream, cnt); 1.67 + }, 1.68 + 1.69 + onStopRequest: function testOnStopRequest(request, ctx, status) { 1.70 + do_check_true(this.onStartRequestFired); 1.71 + do_check_true(this.onDataAvailableFired); 1.72 + do_check_true(this.isSpdyConnection); 1.73 + 1.74 + run_next_test(); 1.75 + do_test_finished(); 1.76 + } 1.77 +}; 1.78 + 1.79 +/* 1.80 + * Support for testing valid multiplexing of streams 1.81 + */ 1.82 + 1.83 +var multiplexContent = generateContent(30*1024); 1.84 +var completed_channels = []; 1.85 +function register_completed_channel(listener) { 1.86 + completed_channels.push(listener); 1.87 + if (completed_channels.length == 2) { 1.88 + do_check_neq(completed_channels[0].streamID, completed_channels[1].streamID); 1.89 + run_next_test(); 1.90 + do_test_finished(); 1.91 + } 1.92 +} 1.93 + 1.94 +/* Listener class to control the testing of multiplexing */ 1.95 +var SpdyMultiplexListener = function() {}; 1.96 + 1.97 +SpdyMultiplexListener.prototype = new SpdyCheckListener(); 1.98 + 1.99 +SpdyMultiplexListener.prototype.streamID = 0; 1.100 +SpdyMultiplexListener.prototype.buffer = ""; 1.101 + 1.102 +SpdyMultiplexListener.prototype.onDataAvailable = function(request, ctx, stream, off, cnt) { 1.103 + this.onDataAvailableFired = true; 1.104 + this.isSpdyConnection = checkIsSpdy(request); 1.105 + this.streamID = parseInt(request.getResponseHeader("X-Spdy-StreamID")); 1.106 + var data = read_stream(stream, cnt); 1.107 + this.buffer = this.buffer.concat(data); 1.108 +}; 1.109 + 1.110 +SpdyMultiplexListener.prototype.onStopRequest = function(request, ctx, status) { 1.111 + do_check_true(this.onStartRequestFired); 1.112 + do_check_true(this.onDataAvailableFired); 1.113 + do_check_true(this.isSpdyConnection); 1.114 + do_check_true(this.buffer == multiplexContent); 1.115 + 1.116 + // This is what does most of the hard work for us 1.117 + register_completed_channel(this); 1.118 +}; 1.119 + 1.120 +// Does the appropriate checks for header gatewaying 1.121 +var SpdyHeaderListener = function(value) { 1.122 + this.value = value 1.123 +}; 1.124 + 1.125 +SpdyHeaderListener.prototype = new SpdyCheckListener(); 1.126 +SpdyHeaderListener.prototype.value = ""; 1.127 + 1.128 +SpdyHeaderListener.prototype.onDataAvailable = function(request, ctx, stream, off, cnt) { 1.129 + this.onDataAvailableFired = true; 1.130 + this.isSpdyConnection = checkIsSpdy(request); 1.131 + do_check_eq(request.getResponseHeader("X-Received-Test-Header"), this.value); 1.132 + read_stream(stream, cnt); 1.133 +}; 1.134 + 1.135 +var SpdyPushListener = function() {}; 1.136 + 1.137 +SpdyPushListener.prototype = new SpdyCheckListener(); 1.138 + 1.139 +SpdyPushListener.prototype.onDataAvailable = function(request, ctx, stream, off, cnt) { 1.140 + this.onDataAvailableFired = true; 1.141 + this.isSpdyConnection = checkIsSpdy(request); 1.142 + if (ctx.originalURI.spec == "https://localhost:4443/push.js" || 1.143 + ctx.originalURI.spec == "https://localhost:4443/push2.js") { 1.144 + do_check_eq(request.getResponseHeader("pushed"), "yes"); 1.145 + } 1.146 + read_stream(stream, cnt); 1.147 +}; 1.148 + 1.149 +// Does the appropriate checks for a large GET response 1.150 +var SpdyBigListener = function() {}; 1.151 + 1.152 +SpdyBigListener.prototype = new SpdyCheckListener(); 1.153 +SpdyBigListener.prototype.buffer = ""; 1.154 + 1.155 +SpdyBigListener.prototype.onDataAvailable = function(request, ctx, stream, off, cnt) { 1.156 + this.onDataAvailableFired = true; 1.157 + this.isSpdyConnection = checkIsSpdy(request); 1.158 + this.buffer = this.buffer.concat(read_stream(stream, cnt)); 1.159 + // We know the server should send us the same data as our big post will be, 1.160 + // so the md5 should be the same 1.161 + do_check_eq(bigListenerMD5, request.getResponseHeader("X-Expected-MD5")); 1.162 +}; 1.163 + 1.164 +SpdyBigListener.prototype.onStopRequest = function(request, ctx, status) { 1.165 + do_check_true(this.onStartRequestFired); 1.166 + do_check_true(this.onDataAvailableFired); 1.167 + do_check_true(this.isSpdyConnection); 1.168 + 1.169 + // Don't want to flood output, so don't use do_check_eq 1.170 + do_check_true(this.buffer == bigListenerData); 1.171 + 1.172 + run_next_test(); 1.173 + do_test_finished(); 1.174 +}; 1.175 + 1.176 +// Does the appropriate checks for POSTs 1.177 +var SpdyPostListener = function(expected_md5) { 1.178 + this.expected_md5 = expected_md5; 1.179 +}; 1.180 + 1.181 +SpdyPostListener.prototype = new SpdyCheckListener(); 1.182 +SpdyPostListener.prototype.expected_md5 = ""; 1.183 + 1.184 +SpdyPostListener.prototype.onDataAvailable = function(request, ctx, stream, off, cnt) { 1.185 + this.onDataAvailableFired = true; 1.186 + this.isSpdyConnection = checkIsSpdy(request); 1.187 + read_stream(stream, cnt); 1.188 + do_check_eq(this.expected_md5, request.getResponseHeader("X-Calculated-MD5")); 1.189 +}; 1.190 + 1.191 +function makeChan(url) { 1.192 + var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); 1.193 + var chan = ios.newChannel(url, null, null).QueryInterface(Ci.nsIHttpChannel); 1.194 + 1.195 + return chan; 1.196 +} 1.197 + 1.198 +// Make sure we make a spdy connection and both us and the server mark it as such 1.199 +function test_spdy_basic() { 1.200 + var chan = makeChan("https://localhost:4443/"); 1.201 + var listener = new SpdyCheckListener(); 1.202 + chan.asyncOpen(listener, null); 1.203 +} 1.204 + 1.205 +// Support for making sure XHR works over SPDY 1.206 +function checkXhr(xhr) { 1.207 + if (xhr.readyState != 4) { 1.208 + return; 1.209 + } 1.210 + 1.211 + do_check_eq(xhr.status, 200); 1.212 + do_check_eq(checkIsSpdy(xhr), true); 1.213 + run_next_test(); 1.214 + do_test_finished(); 1.215 +} 1.216 + 1.217 +// Fires off an XHR request over SPDY 1.218 +function test_spdy_xhr() { 1.219 + var req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"] 1.220 + .createInstance(Ci.nsIXMLHttpRequest); 1.221 + req.open("GET", "https://localhost:4443/", true); 1.222 + req.addEventListener("readystatechange", function (evt) { checkXhr(req); }, 1.223 + false); 1.224 + req.send(null); 1.225 +} 1.226 + 1.227 +// Test to make sure we get multiplexing right 1.228 +function test_spdy_multiplex() { 1.229 + var chan1 = makeChan("https://localhost:4443/multiplex1"); 1.230 + var chan2 = makeChan("https://localhost:4443/multiplex2"); 1.231 + var listener1 = new SpdyMultiplexListener(); 1.232 + var listener2 = new SpdyMultiplexListener(); 1.233 + chan1.asyncOpen(listener1, null); 1.234 + chan2.asyncOpen(listener2, null); 1.235 +} 1.236 + 1.237 +// Test to make sure we gateway non-standard headers properly 1.238 +function test_spdy_header() { 1.239 + var chan = makeChan("https://localhost:4443/header"); 1.240 + var hvalue = "Headers are fun"; 1.241 + var listener = new SpdyHeaderListener(hvalue); 1.242 + chan.setRequestHeader("X-Test-Header", hvalue, false); 1.243 + chan.asyncOpen(listener, null); 1.244 +} 1.245 + 1.246 +function test_spdy_push1() { 1.247 + var chan = makeChan("https://localhost:4443/push"); 1.248 + chan.loadGroup = loadGroup; 1.249 + var listener = new SpdyPushListener(); 1.250 + chan.asyncOpen(listener, chan); 1.251 +} 1.252 + 1.253 +function test_spdy_push2() { 1.254 + var chan = makeChan("https://localhost:4443/push.js"); 1.255 + chan.loadGroup = loadGroup; 1.256 + var listener = new SpdyPushListener(); 1.257 + chan.asyncOpen(listener, chan); 1.258 +} 1.259 + 1.260 +function test_spdy_push3() { 1.261 + var chan = makeChan("https://localhost:4443/push2"); 1.262 + chan.loadGroup = loadGroup; 1.263 + var listener = new SpdyPushListener(); 1.264 + chan.asyncOpen(listener, chan); 1.265 +} 1.266 + 1.267 +function test_spdy_push4() { 1.268 + var chan = makeChan("https://localhost:4443/push2.js"); 1.269 + chan.loadGroup = loadGroup; 1.270 + var listener = new SpdyPushListener(); 1.271 + chan.asyncOpen(listener, chan); 1.272 +} 1.273 + 1.274 +// Make sure we handle GETs that cover more than 2 frames properly 1.275 +function test_spdy_big() { 1.276 + var chan = makeChan("https://localhost:4443/big"); 1.277 + var listener = new SpdyBigListener(); 1.278 + chan.asyncOpen(listener, null); 1.279 +} 1.280 + 1.281 +// Support for doing a POST 1.282 +function do_post(content, chan, listener) { 1.283 + var stream = Cc["@mozilla.org/io/string-input-stream;1"] 1.284 + .createInstance(Ci.nsIStringInputStream); 1.285 + stream.data = content; 1.286 + 1.287 + var uchan = chan.QueryInterface(Ci.nsIUploadChannel); 1.288 + uchan.setUploadStream(stream, "text/plain", stream.available()); 1.289 + 1.290 + chan.requestMethod = "POST"; 1.291 + 1.292 + chan.asyncOpen(listener, null); 1.293 +} 1.294 + 1.295 +// Make sure we can do a simple POST 1.296 +function test_spdy_post() { 1.297 + var chan = makeChan("https://localhost:4443/post"); 1.298 + var listener = new SpdyPostListener(md5s[0]); 1.299 + do_post(posts[0], chan, listener); 1.300 +} 1.301 + 1.302 +// Make sure we can do a POST that covers more than 2 frames 1.303 +function test_spdy_post_big() { 1.304 + var chan = makeChan("https://localhost:4443/post"); 1.305 + var listener = new SpdyPostListener(md5s[1]); 1.306 + do_post(posts[1], chan, listener); 1.307 +} 1.308 + 1.309 +// hack - the header test resets the multiplex object on the server, 1.310 +// so make sure header is always run before the multiplex test. 1.311 +// 1.312 +// make sure post_big runs first to test race condition in restarting 1.313 +// a stalled stream when a SETTINGS frame arrives 1.314 +var tests = [ test_spdy_post_big 1.315 + , test_spdy_basic 1.316 + , test_spdy_push1 1.317 + , test_spdy_push2 1.318 + , test_spdy_push3 1.319 + , test_spdy_push4 1.320 + , test_spdy_xhr 1.321 + , test_spdy_header 1.322 + , test_spdy_multiplex 1.323 + , test_spdy_big 1.324 + , test_spdy_post 1.325 + ]; 1.326 +var current_test = 0; 1.327 + 1.328 +function run_next_test() { 1.329 + if (current_test < tests.length) { 1.330 + tests[current_test](); 1.331 + current_test++; 1.332 + do_test_pending(); 1.333 + } 1.334 +} 1.335 + 1.336 +// Support for making sure we can talk to the invalid cert the server presents 1.337 +var CertOverrideListener = function(host, port, bits) { 1.338 + this.host = host; 1.339 + if (port) { 1.340 + this.port = port; 1.341 + } 1.342 + this.bits = bits; 1.343 +}; 1.344 + 1.345 +CertOverrideListener.prototype = { 1.346 + host: null, 1.347 + port: -1, 1.348 + bits: null, 1.349 + 1.350 + getInterface: function(aIID) { 1.351 + return this.QueryInterface(aIID); 1.352 + }, 1.353 + 1.354 + QueryInterface: function(aIID) { 1.355 + if (aIID.equals(Ci.nsIBadCertListener2) || 1.356 + aIID.equals(Ci.nsIInterfaceRequestor) || 1.357 + aIID.equals(Ci.nsISupports)) 1.358 + return this; 1.359 + throw Components.results.NS_ERROR_NO_INTERFACE; 1.360 + }, 1.361 + 1.362 + notifyCertProblem: function(socketInfo, sslStatus, targetHost) { 1.363 + var cert = sslStatus.QueryInterface(Ci.nsISSLStatus).serverCert; 1.364 + var cos = Cc["@mozilla.org/security/certoverride;1"]. 1.365 + getService(Ci.nsICertOverrideService); 1.366 + cos.rememberValidityOverride(this.host, this.port, cert, this.bits, false); 1.367 + return true; 1.368 + }, 1.369 +}; 1.370 + 1.371 +function addCertOverride(host, port, bits) { 1.372 + var req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"] 1.373 + .createInstance(Ci.nsIXMLHttpRequest); 1.374 + try { 1.375 + var url; 1.376 + if (port) { 1.377 + url = "https://" + host + ":" + port + "/"; 1.378 + } else { 1.379 + url = "https://" + host + "/"; 1.380 + } 1.381 + req.open("GET", url, false); 1.382 + req.channel.notificationCallbacks = new CertOverrideListener(host, port, bits); 1.383 + req.send(null); 1.384 + } catch (e) { 1.385 + // This will fail since the server is not trusted yet 1.386 + } 1.387 +} 1.388 + 1.389 +var prefs; 1.390 +var spdypref; 1.391 +var spdy3pref; 1.392 +var spdypush; 1.393 + 1.394 +var loadGroup; 1.395 + 1.396 +function resetPrefs() { 1.397 + prefs.setBoolPref("network.http.spdy.enabled", spdypref); 1.398 + prefs.setBoolPref("network.http.spdy.enabled.v3", spdy3pref); 1.399 + prefs.setBoolPref("network.http.spdy.allow-push", spdypush); 1.400 +} 1.401 + 1.402 +function run_test() { 1.403 + // Set to allow the cert presented by our SPDY server 1.404 + do_get_profile(); 1.405 + var prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch); 1.406 + var oldPref = prefs.getIntPref("network.http.speculative-parallel-limit"); 1.407 + prefs.setIntPref("network.http.speculative-parallel-limit", 0); 1.408 + 1.409 + addCertOverride("localhost", 4443, 1.410 + Ci.nsICertOverrideService.ERROR_UNTRUSTED | 1.411 + Ci.nsICertOverrideService.ERROR_MISMATCH | 1.412 + Ci.nsICertOverrideService.ERROR_TIME); 1.413 + 1.414 + prefs.setIntPref("network.http.speculative-parallel-limit", oldPref); 1.415 + 1.416 + // Enable all versions of spdy to see that we auto negotiate spdy/3 1.417 + spdypref = prefs.getBoolPref("network.http.spdy.enabled"); 1.418 + spdy3pref = prefs.getBoolPref("network.http.spdy.enabled.v3"); 1.419 + spdypush = prefs.getBoolPref("network.http.spdy.allow-push"); 1.420 + prefs.setBoolPref("network.http.spdy.enabled", true); 1.421 + prefs.setBoolPref("network.http.spdy.enabled.v3", true); 1.422 + prefs.setBoolPref("network.http.spdy.allow-push", true); 1.423 + 1.424 + loadGroup = Cc["@mozilla.org/network/load-group;1"].createInstance(Ci.nsILoadGroup); 1.425 + 1.426 + // And make go! 1.427 + run_next_test(); 1.428 +}