Bug 468467 - If onInputStreamReady doesn't process all of the Request-Line the first time, httpd.js asserts. r=honzab

This commit is contained in:
Jeff Walden 2008-12-12 11:31:30 -08:00
parent b962771e06
commit 38cf48c5a5
4 changed files with 208 additions and 44 deletions

View File

@ -1118,7 +1118,7 @@ RequestReader.prototype =
// if we don't have a full line, wait until we do
if (!readSuccess)
return true;
return false;
// we have the first non-blank line
try

View File

@ -159,6 +159,40 @@ function expectLines(iter, expectedLines)
}
}
/**
* Spew a bunch of HTTP metadata from request into the body of response.
*
* @param request : nsIHttpRequestMetadata
* the request whose metadata should be output
* @param response : nsIHttpResponse
* the response to which the metadata is written
*/
function writeDetails(request, response)
{
response.write("Method: " + request.method + "\r\n");
response.write("Path: " + request.path + "\r\n");
response.write("Query: " + request.queryString + "\r\n");
response.write("Version: " + request.httpVersion + "\r\n");
response.write("Scheme: " + request.scheme + "\r\n");
response.write("Host: " + request.host + "\r\n");
response.write("Port: " + request.port);
}
/**
* Advances iter past all non-blank lines and a single blank line, after which
* point the body of the response will be returned next from the iterator.
*
* @param iter : Iterator
* an iterator over the CRLF-delimited lines in an HTTP response, currently
* just after the Request-Line
*/
function skipHeaders(iter)
{
var line = iter.next();
while (line !== "")
line = iter.next();
}
/*******************************************************
* SIMPLE SUPPORT FOR LOADING/TESTING A SERIES OF URLS *
@ -296,9 +330,11 @@ function runHttpTests(testArray, done)
* the host to which a connection should be made
* @param port : PRUint16
* the port to use for the connection
* @param data : string
* the raw data to send, as a string of characters with codes in the range
* 0-255
* @param data : string or [string...]
* either:
* - the raw data to send, as a string of characters with codes in the
* range 0-255, or
* - an array of such strings whose concatenation forms the raw data
* @param responseCheck : function(string) : void
* a function which is provided with the data sent by the remote host which
* conducts whatever tests it wants on that data; useful for tweaking the test
@ -308,8 +344,12 @@ function RawTest(host, port, data, responseCheck)
{
if (0 > port || 65535 < port || port % 1 !== 0)
throw "bad port";
if (!/^[\x00-\xff]*$/.test(data))
throw "bad data contains non-byte-valued character";
if (!(data instanceof Array))
data = [data];
if (data.length <= 0)
throw "bad data length";
if (!data.every(function(v) { return /^[\x00-\xff]*$/.test(data); }))
throw "bad data contained non-byte-valued character";
this.host = host;
this.port = port;
@ -372,14 +412,17 @@ function runRawTests(testArray, done)
function waitToWriteOutput(stream)
{
stream.asyncWait(writer, 0, testArray[testIndex].data.length - dataIndex,
stream.asyncWait(writer, 0, testArray[testIndex].data[dataIndex].length,
currentThread);
}
/** Index of the test being run. */
var testIndex = -1;
/** Index of remaining data to be written to the socket in current test. */
/**
* Index of remaining data strings to be written to the socket in current
* test.
*/
var dataIndex = 0;
/** Data received so far from the server. */
@ -428,13 +471,16 @@ function runRawTests(testArray, done)
{
onOutputStreamReady: function(stream)
{
var data = testArray[testIndex].data.substring(dataIndex);
var data = testArray[testIndex].data[dataIndex];
var written = 0;
try
{
written = stream.write(data, data.length);
dataIndex += written;
if (written == data.length)
dataIndex++;
else
testArray[testIndex].data = data.substring(written);
}
catch (e) { /* stream could have been closed, just ignore */ }

View File

@ -252,40 +252,6 @@ function checkPrimariesThrow(id)
do_check_true(threw);
}
/**
* Spew a bunch of HTTP metadata from request into the body of response.
*
* @param request : nsIHttpRequestMetadata
* the request whose metadata should be output
* @param response : nsIHttpResponse
* the response to which the metadata is written
*/
function writeDetails(request, response)
{
response.write("Method: " + request.method + "\r\n");
response.write("Path: " + request.path + "\r\n");
response.write("Query: " + request.queryString + "\r\n");
response.write("Version: " + request.httpVersion + "\r\n");
response.write("Scheme: " + request.scheme + "\r\n");
response.write("Host: " + request.host + "\r\n");
response.write("Port: " + request.port);
}
/**
* Advances iter past all non-blank lines and a single blank line, after which
* point the body of the response will be returned next from the iterator.
*
* @param iter : Iterator
* an iterator over the CRLF-delimited lines in an HTTP response, currently
* just after the Request-Line
*/
function skipHeaders(iter)
{
var line = iter.next();
while (line !== "")
line = iter.next();
}
/**
* Utility function to check for a 400 response.
*/

View File

@ -0,0 +1,152 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is httpd.js code.
*
* The Initial Developer of the Original Code is
* the Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Jeff Walden <jwalden+code@mit.edu> (original author)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/**
* Tests that even when an incoming request's data for the Request-Line doesn't
* all fit in a single onInputStreamReady notification, the request is handled
* properly.
*/
const PORT = 4444;
var srv;
function run_test()
{
srv = createServer();
srv.registerPathHandler("/lots-of-leading-blank-lines",
lotsOfLeadingBlankLines);
srv.registerPathHandler("/very-long-request-line",
veryLongRequestLine);
srv.start(PORT);
runRawTests(tests, function() { srv.stop(); });
}
/***************
* BEGIN TESTS *
***************/
var test, data, str;
var tests = [];
function veryLongRequestLine(request, response)
{
writeDetails(request, response);
response.setStatusLine(request.httpVersion, 200, "TEST PASSED");
}
var path = "/very-long-request-line?";
var gibberish = "dfsasdbfjkbnsldkjnewiunfasjkn";
for (var i = 0; i < 10; i++)
gibberish += gibberish;
str = "GET /very-long-request-line?" + gibberish + " HTTP/1.1\r\n" +
"Host: localhost:4444\r\n" +
"\r\n";
data = [];
for (var i = 0; i < str.length; i += 50)
data.push(str.substr(i, 50));
function checkVeryLongRequestLine(data)
{
var iter = LineIterator(data);
// Status-Line
do_check_eq(iter.next(), "HTTP/1.1 200 TEST PASSED");
skipHeaders(iter);
// Okay, next line must be the data we expected to be written
var body =
[
"Method: GET",
"Path: /very-long-request-line",
"Query: " + gibberish,
"Version: 1.1",
"Scheme: http",
"Host: localhost",
"Port: 4444",
];
expectLines(iter, body);
}
test = new RawTest("localhost", PORT, data, checkVeryLongRequestLine),
tests.push(test);
function lotsOfLeadingBlankLines(request, response)
{
writeDetails(request, response);
response.setStatusLine(request.httpVersion, 200, "TEST PASSED");
}
var blankLines = "\r\n";
for (var i = 0; i < 13; i++)
blankLines += blankLines;
str = blankLines +
"GET /lots-of-leading-blank-lines HTTP/1.1\r\n" +
"Host: localhost:4444\r\n" +
"\r\n";
data = [];
for (var i = 0; i < str.length; i += 100)
data.push(str.substr(i, 100));
function checkLotsOfLeadingBlankLines(data)
{
var iter = LineIterator(data);
// Status-Line
do_check_eq(iter.next(), "HTTP/1.1 200 TEST PASSED");
skipHeaders(iter);
// Okay, next line must be the data we expected to be written
var body =
[
"Method: GET",
"Path: /lots-of-leading-blank-lines",
"Query: ",
"Version: 1.1",
"Scheme: http",
"Host: localhost",
"Port: 4444",
];
expectLines(iter, body);
}
test = new RawTest("localhost", PORT, data, checkLotsOfLeadingBlankLines),
tests.push(test);