netwerk/test/unit/test_spdy.js

changeset 1
ca08bd8f51b2
equal deleted inserted replaced
-1:000000000000 0:0c565de47930
1 // test spdy/3
2
3 var Ci = Components.interfaces;
4 var Cc = Components.classes;
5
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 }
14
15 var posts = [];
16 posts.push(generateContent(10));
17 posts.push(generateContent(250000));
18
19 // pre-calculated md5sums (in hex) of the above posts
20 var md5s = ['f1b708bba17f1ce948dc979f4d7092bc',
21 '2ef8d3b6c8f329318eb1a119b12622b6'];
22
23 var bigListenerData = generateContent(128 * 1024);
24 var bigListenerMD5 = '8f607cfdd2c87d6a7eedb657dafbd836';
25
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 }
39
40 var SpdyCheckListener = function() {};
41
42 SpdyCheckListener.prototype = {
43 onStartRequestFired: false,
44 onDataAvailableFired: false,
45 isSpdyConnection: false,
46
47 onStartRequest: function testOnStartRequest(request, ctx) {
48 this.onStartRequestFired = true;
49
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");
54
55 do_check_eq(request.responseStatus, 200);
56 do_check_eq(request.requestSucceeded, true);
57 },
58
59 onDataAvailable: function testOnDataAvailable(request, ctx, stream, off, cnt) {
60 this.onDataAvailableFired = true;
61 this.isSpdyConnection = checkIsSpdy(request);
62
63 read_stream(stream, cnt);
64 },
65
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);
70
71 run_next_test();
72 do_test_finished();
73 }
74 };
75
76 /*
77 * Support for testing valid multiplexing of streams
78 */
79
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 }
90
91 /* Listener class to control the testing of multiplexing */
92 var SpdyMultiplexListener = function() {};
93
94 SpdyMultiplexListener.prototype = new SpdyCheckListener();
95
96 SpdyMultiplexListener.prototype.streamID = 0;
97 SpdyMultiplexListener.prototype.buffer = "";
98
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 };
106
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);
112
113 // This is what does most of the hard work for us
114 register_completed_channel(this);
115 };
116
117 // Does the appropriate checks for header gatewaying
118 var SpdyHeaderListener = function(value) {
119 this.value = value
120 };
121
122 SpdyHeaderListener.prototype = new SpdyCheckListener();
123 SpdyHeaderListener.prototype.value = "";
124
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 };
131
132 var SpdyPushListener = function() {};
133
134 SpdyPushListener.prototype = new SpdyCheckListener();
135
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 };
145
146 // Does the appropriate checks for a large GET response
147 var SpdyBigListener = function() {};
148
149 SpdyBigListener.prototype = new SpdyCheckListener();
150 SpdyBigListener.prototype.buffer = "";
151
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 };
160
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);
165
166 // Don't want to flood output, so don't use do_check_eq
167 do_check_true(this.buffer == bigListenerData);
168
169 run_next_test();
170 do_test_finished();
171 };
172
173 // Does the appropriate checks for POSTs
174 var SpdyPostListener = function(expected_md5) {
175 this.expected_md5 = expected_md5;
176 };
177
178 SpdyPostListener.prototype = new SpdyCheckListener();
179 SpdyPostListener.prototype.expected_md5 = "";
180
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 };
187
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);
191
192 return chan;
193 }
194
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 }
201
202 // Support for making sure XHR works over SPDY
203 function checkXhr(xhr) {
204 if (xhr.readyState != 4) {
205 return;
206 }
207
208 do_check_eq(xhr.status, 200);
209 do_check_eq(checkIsSpdy(xhr), true);
210 run_next_test();
211 do_test_finished();
212 }
213
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 }
223
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 }
233
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 }
242
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 }
249
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 }
256
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 }
263
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 }
270
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 }
277
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;
283
284 var uchan = chan.QueryInterface(Ci.nsIUploadChannel);
285 uchan.setUploadStream(stream, "text/plain", stream.available());
286
287 chan.requestMethod = "POST";
288
289 chan.asyncOpen(listener, null);
290 }
291
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 }
298
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 }
305
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;
324
325 function run_next_test() {
326 if (current_test < tests.length) {
327 tests[current_test]();
328 current_test++;
329 do_test_pending();
330 }
331 }
332
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 };
341
342 CertOverrideListener.prototype = {
343 host: null,
344 port: -1,
345 bits: null,
346
347 getInterface: function(aIID) {
348 return this.QueryInterface(aIID);
349 },
350
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 },
358
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 };
367
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 }
385
386 var prefs;
387 var spdypref;
388 var spdy3pref;
389 var spdypush;
390
391 var loadGroup;
392
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 }
398
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);
405
406 addCertOverride("localhost", 4443,
407 Ci.nsICertOverrideService.ERROR_UNTRUSTED |
408 Ci.nsICertOverrideService.ERROR_MISMATCH |
409 Ci.nsICertOverrideService.ERROR_TIME);
410
411 prefs.setIntPref("network.http.speculative-parallel-limit", oldPref);
412
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);
420
421 loadGroup = Cc["@mozilla.org/network/load-group;1"].createInstance(Ci.nsILoadGroup);
422
423 // And make go!
424 run_next_test();
425 }

mercurial