head_utils.js
Go to the documentation of this file.
1 /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et: */
3 /* ***** BEGIN LICENSE BLOCK *****
4  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5  *
6  * The contents of this file are subject to the Mozilla Public License Version
7  * 1.1 (the "License"); you may not use this file except in compliance with
8  * the License. You may obtain a copy of the License at
9  * http://www.mozilla.org/MPL/
10  *
11  * Software distributed under the License is distributed on an "AS IS" basis,
12  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13  * for the specific language governing rights and limitations under the
14  * License.
15  *
16  * The Original Code is httpd.js code.
17  *
18  * The Initial Developer of the Original Code is
19  * Jeff Walden <jwalden+code@mit.edu>.
20  * Portions created by the Initial Developer are Copyright (C) 2006
21  * the Initial Developer. All Rights Reserved.
22  *
23  * Contributor(s):
24  *
25  * Alternatively, the contents of this file may be used under the terms of
26  * either the GNU General Public License Version 2 or later (the "GPL"), or
27  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28  * in which case the provisions of the GPL or the LGPL are applicable instead
29  * of those above. If you wish to allow use of your version of this file only
30  * under the terms of either the GPL or the LGPL, and not to allow others to
31  * use your version of this file under the terms of the MPL, indicate your
32  * decision by deleting the provisions above and replace them with the notice
33  * and other provisions required by the GPL or the LGPL. If you do not delete
34  * the provisions above, a recipient may use your version of this file under
35  * the terms of any one of the MPL, the GPL or the LGPL.
36  *
37  * ***** END LICENSE BLOCK ***** */
38 
40 
41 // if these tests fail, we'll want the debug output
42 DEBUG = true;
43 
44 const Timer = CC("@mozilla.org/timer;1", "nsITimer", "initWithCallback");
45 
46 
53 function createServer()
54 {
55  return new nsHttpServer();
56 }
57 
64 function makeChannel(url)
65 {
66  var ios = Cc["@mozilla.org/network/io-service;1"]
67  .getService(Ci.nsIIOService);
68  var chan = ios.newChannel(url, null, null)
69  .QueryInterface(Ci.nsIHttpChannel);
70 
71  return chan;
72 }
73 
80 function makeBIS(stream)
81 {
82  return new BinaryInputStream(stream);
83 }
84 
85 
94 function fileContents(file)
95 {
96  const PR_RDONLY = 0x01;
97  var fis = new FileInputStream(file, PR_RDONLY, 0444,
98  Ci.nsIFileInputStream.CLOSE_ON_EOF);
99  var sis = new ScriptableInputStream(fis);
100  var contents = sis.read(file.fileSize);
101  sis.close();
102  return contents;
103 }
104 
116 {
117  var start = 0, index = 0;
118  do
119  {
120  index = data.indexOf("\r\n");
121  if (index >= 0)
122  yield data.substring(0, index);
123  else
124  yield data;
125 
126  data = data.substring(index + 2);
127  }
128  while (index >= 0);
129 }
130 
142 function expectLines(iter, expectedLines)
143 {
144  var index = 0;
145  for (var line in iter)
146  {
147  if (expectedLines.length == index)
148  throw "Error: got more than " + expectedLines.length + " expected lines!";
149 
150  var expected = expectedLines[index++];
151  if (expected !== line)
152  throw "Error on line " + index + "!\n" +
153  " actual: '" + line + "',\n" +
154  " expect: '" + expected + "'";
155  }
156 
157  if (expectedLines.length !== index)
158  {
159  throw "Expected more lines! Got " + index +
160  ", expected " + expectedLines.length;
161  }
162 }
163 
172 function writeDetails(request, response)
173 {
174  response.write("Method: " + request.method + "\r\n");
175  response.write("Path: " + request.path + "\r\n");
176  response.write("Query: " + request.queryString + "\r\n");
177  response.write("Version: " + request.httpVersion + "\r\n");
178  response.write("Scheme: " + request.scheme + "\r\n");
179  response.write("Host: " + request.host + "\r\n");
180  response.write("Port: " + request.port);
181 }
182 
191 function skipHeaders(iter)
192 {
193  var line = iter.next();
194  while (line !== "")
195  line = iter.next();
196 }
197 
207 function isException(e, code)
208 {
209  if (e !== code && e.result !== code)
210  do_throw("unexpected error: " + e);
211 }
212 
218 
225 const __timerFuzz = 15;
226 
237 function callLater(msecs, callback)
238 {
239  do_check_true(msecs >= 0);
240 
241  var start = Date.now();
242 
243  function checkTime()
244  {
245  var index = __pendingTimers.indexOf(timer);
246  do_check_true(index >= 0); // sanity
247  __pendingTimers.splice(index, 1);
248  do_check_eq(__pendingTimers.indexOf(timer), -1);
249 
250  // The current nsITimer implementation can undershoot, but even if it
251  // couldn't, paranoia is probably a virtue here given the potential for
252  // random orange on tinderboxen.
253  var end = Date.now();
254  var elapsed = end - start;
255  if (elapsed >= msecs)
256  {
257  dumpn("*** TIMER FIRE " + elapsed + "ms (" + msecs + "ms requested)");
258  try
259  {
260  callback();
261  }
262  catch (e)
263  {
264  do_throw("exception thrown from callLater callback: " + e);
265  }
266  return;
267  }
268 
269  // Timer undershot, retry with a little overshoot to try to avoid more
270  // undershoots.
271  var newDelay = msecs - elapsed;
272  dumpn("*** TIMER UNDERSHOOT " + newDelay + "ms " +
273  "(" + msecs + "ms requested, delaying)");
274 
275  callLater(newDelay, callback);
276  }
277 
278  var timer =
279  new Timer(checkTime, msecs + __timerFuzz, Ci.nsITimer.TYPE_ONE_SHOT);
280  __pendingTimers.push(timer);
281 }
282 
283 
284 /*******************************************************
285  * SIMPLE SUPPORT FOR LOADING/TESTING A SERIES OF URLS *
286  *******************************************************/
287 
292 function testComplete(srv)
293 {
294  return function complete()
295  {
296  do_test_pending();
297  srv.stop(function quit() { do_test_finished(); });
298  };
299 }
300 
322 function Test(path, initChannel, onStartRequest, onStopRequest)
323 {
324  function nil() { }
325 
326  this.path = path;
327  this.initChannel = initChannel || nil;
328  this.onStartRequest = onStartRequest || nil;
329  this.onStopRequest = onStopRequest || nil;
330 }
331 
340 function runHttpTests(testArray, done)
341 {
343  function performNextTest()
344  {
345  if (++testIndex == testArray.length)
346  {
347  try
348  {
349  done();
350  }
351  catch (e)
352  {
353  do_throw("error running test-completion callback: " + e);
354  }
355  return;
356  }
357 
358  do_test_pending();
359 
360  var test = testArray[testIndex];
361  var ch = makeChannel(test.path);
362  try
363  {
364  test.initChannel(ch);
365  }
366  catch (e)
367  {
368  try
369  {
370  do_throw("testArray[" + testIndex + "].initChannel(ch) failed: " + e);
371  }
372  catch (e) { /* swallow and let tests continue */ }
373  }
374 
375  ch.asyncOpen(listener, null);
376  }
377 
379  var testIndex = -1;
380 
382  var listener =
383  {
385  _data: [],
386 
387  onStartRequest: function(request, cx)
388  {
389  var ch = request.QueryInterface(Ci.nsIHttpChannel)
390  .QueryInterface(Ci.nsIHttpChannelInternal);
391 
392  this._data.length = 0;
393  try
394  {
395  try
396  {
397  testArray[testIndex].onStartRequest(ch, cx);
398  }
399  catch (e)
400  {
401  do_throw("testArray[" + testIndex + "].onStartRequest: " + e);
402  }
403  }
404  catch (e)
405  {
406  dumpn("!!! swallowing onStartRequest exception so onStopRequest is " +
407  "called...");
408  }
409  },
410  onDataAvailable: function(request, cx, inputStream, offset, count)
411  {
412  Array.prototype.push.apply(this._data,
413  makeBIS(inputStream).readByteArray(count));
414  },
415  onStopRequest: function(request, cx, status)
416  {
417  var ch = request.QueryInterface(Ci.nsIHttpChannel)
418  .QueryInterface(Ci.nsIHttpChannelInternal);
419 
420  // NB: The onStopRequest callback must run before performNextTest here,
421  // because the latter runs the next test's initChannel callback, and
422  // we want one test to be sequentially processed before the next
423  // one.
424  try
425  {
426  testArray[testIndex].onStopRequest(ch, cx, status, this._data);
427  }
428  finally
429  {
430  try
431  {
432  performNextTest();
433  }
434  finally
435  {
436  do_test_finished();
437  }
438  }
439  },
440  QueryInterface: function(aIID)
441  {
442  if (aIID.equals(Ci.nsIStreamListener) ||
443  aIID.equals(Ci.nsIRequestObserver) ||
444  aIID.equals(Ci.nsISupports))
445  return this;
446  throw Cr.NS_ERROR_NO_INTERFACE;
447  }
448  };
449 
450  performNextTest();
451 }
452 
453 
454 /****************************************
455  * RAW REQUEST FORMAT TESTING FUNCTIONS *
456  ****************************************/
457 
476 function RawTest(host, port, data, responseCheck)
477 {
478  if (0 > port || 65535 < port || port % 1 !== 0)
479  throw "bad port";
480  if (!(data instanceof Array))
481  data = [data];
482  if (data.length <= 0)
483  throw "bad data length";
484  if (!data.every(function(v) { return /^[\x00-\xff]*$/.test(data); }))
485  throw "bad data contained non-byte-valued character";
486 
487  this.host = host;
488  this.port = port;
489  this.data = data;
490  this.responseCheck = responseCheck;
491 }
492 
501 function runRawTests(testArray, done)
502 {
503  do_test_pending();
504 
505  var sts = Cc["@mozilla.org/network/socket-transport-service;1"]
506  .getService(Ci.nsISocketTransportService);
507 
508  var currentThread = Cc["@mozilla.org/thread-manager;1"]
509  .getService()
510  .currentThread;
511 
513  function performNextTest()
514  {
515  if (++testIndex == testArray.length)
516  {
517  do_test_finished();
518  try
519  {
520  done();
521  }
522  catch (e)
523  {
524  do_throw("error running test-completion callback: " + e);
525  }
526  return;
527  }
528 
529 
530  var rawTest = testArray[testIndex];
531 
532  var transport =
533  sts.createTransport(null, 0, rawTest.host, rawTest.port, null);
534 
535  var inStream = transport.openInputStream(0, 0, 0)
536  .QueryInterface(Ci.nsIAsyncInputStream);
537  var outStream = transport.openOutputStream(0, 0, 0)
538  .QueryInterface(Ci.nsIAsyncOutputStream);
539 
540  // reset
541  dataIndex = 0;
542  received = "";
543 
544  waitForMoreInput(inStream);
545  waitToWriteOutput(outStream);
546  }
547 
548  function waitForMoreInput(stream)
549  {
550  stream.asyncWait(reader, 0, 0, currentThread);
551  }
552 
553  function waitToWriteOutput(stream)
554  {
555  stream.asyncWait(writer, 0, testArray[testIndex].data[dataIndex].length,
556  currentThread);
557  }
558 
560  var testIndex = -1;
561 
566  var dataIndex = 0;
567 
569  var received = "";
570 
572  var reader =
573  {
574  onInputStreamReady: function(stream)
575  {
576  var bis = new BinaryInputStream(stream);
577 
578  var av = 0;
579  try
580  {
581  av = bis.available();
582  }
583  catch (e) { /* default to 0 */ }
584 
585  if (av > 0)
586  {
587  received += String.fromCharCode.apply(null, bis.readByteArray(av));
588  waitForMoreInput(stream);
589  return;
590  }
591 
592  var rawTest = testArray[testIndex];
593  try
594  {
595  rawTest.responseCheck(received);
596  }
597  catch (e)
598  {
599  do_throw("error thrown by responseCheck: " + e);
600  }
601  finally
602  {
603  stream.close();
604  performNextTest();
605  }
606  }
607  };
608 
610  var writer =
611  {
612  onOutputStreamReady: function(stream)
613  {
614  var data = testArray[testIndex].data[dataIndex];
615 
616  var written = 0;
617  try
618  {
619  written = stream.write(data, data.length);
620  if (written == data.length)
621  dataIndex++;
622  else
623  testArray[testIndex].data = data.substring(written);
624  }
625  catch (e) { /* stream could have been closed, just ignore */ }
626 
627  // Keep reading data until there's no more data to read
628  if (written != 0)
629  waitToWriteOutput(stream);
630  else
631  stream.close();
632  }
633  };
634 
635  performNextTest();
636 }
function start(ch)
GeneratorThread currentThread
const BinaryInputStream
Definition: httpd.js:210
const Cc
do_check_eq(typeof PlacesUtils,"object")
DEBUG
Definition: head_utils.js:42
const PR_RDONLY
function makeBIS(stream)
Definition: head_utils.js:80
function runHttpTests(testArray, done)
Definition: head_utils.js:340
function skipHeaders(iter)
Definition: head_utils.js:191
sbOSDControlService prototype QueryInterface
var __pendingTimers
Definition: head_utils.js:217
function dumpn(str)
Definition: httpd.js:172
function expectLines(iter, expectedLines)
Definition: head_utils.js:142
function runRawTests(testArray, done)
Definition: head_utils.js:501
PRUint32 & offset
function createServer()
Definition: head_utils.js:53
var count
Definition: test_bug7406.js:32
const FileInputStream
Definition: httpd.js:222
function fileContents(file)
Definition: head_utils.js:94
unique done
function writeDetails(request, response)
Definition: head_utils.js:172
grep callback
function makeChannel(url)
Definition: head_utils.js:64
return null
Definition: FeedWriter.js:1143
var expected
function RawTest(host, port, data, responseCheck)
Definition: head_utils.js:476
function url(spec)
function testComplete(srv)
Definition: head_utils.js:292
function Test(path, initChannel, onStartRequest, onStopRequest)
Definition: head_utils.js:322
const Timer
Definition: head_utils.js:44
const Cr
const Ci
const __timerFuzz
Definition: head_utils.js:225
var ios
Definition: head_feeds.js:5
const ScriptableInputStream
Definition: httpd.js:216
observe data
Definition: FeedWriter.js:1329
function callLater(msecs, callback)
Definition: head_utils.js:237
function LineIterator(data)
Definition: head_utils.js:115
function nsHttpServer()
Definition: httpd.js:339
do_load_httpd_js()
function isException(e, code)
Definition: head_utils.js:207
var file
var srv