diff --git a/netwerk/test/httpserver/httpd.js b/netwerk/test/httpserver/httpd.js index f483520ea124..d2b97a4702e1 100644 --- a/netwerk/test/httpserver/httpd.js +++ b/netwerk/test/httpserver/httpd.js @@ -2816,7 +2816,9 @@ ServerHandler.prototype = var fieldName = headEnum.getNext() .QueryInterface(Ci.nsISupportsString) .data; - preamble += fieldName + ": " + head.getHeader(fieldName) + "\r\n"; + var values = head.getHeaderValues(fieldName); + for (index in values) + preamble += fieldName + ": " + values[index] + "\r\n"; } // end request-line/headers @@ -3668,10 +3670,28 @@ nsHttpHeaders.prototype = var name = headerUtils.normalizeFieldName(fieldName); var value = headerUtils.normalizeFieldValue(fieldValue); + // The following three headers are stored as arrays because their real-world + // syntax prevents joining individual headers into a single header using + // ",". See also if (merge && name in this._headers) - this._headers[name] = this._headers[name] + "," + value; + { + if (name === "www-authenticate" || + name === "proxy-authenticate" || + name === "set-cookie") + { + this._headers[name].push(value); + } + else + { + this._headers[name][0] += "," + value; + NS_ASSERT(this._headers[name].length === 1, + "how'd a non-special header have multiple values?") + } + } else - this._headers[name] = value; + { + this._headers[name] = [value]; + } }, /** @@ -3684,9 +3704,33 @@ nsHttpHeaders.prototype = * @returns string * the field value for the given header, possibly with non-semantic changes * (i.e., leading/trailing whitespace stripped, whitespace runs replaced - * with spaces, etc.) at the option of the implementation + * with spaces, etc.) at the option of the implementation; multiple + * instances of the header will be combined with a comma, except for + * the three headers noted in the description of getHeaderValues */ getHeader: function(fieldName) + { + return this.getHeaderValues(fieldName).join("\n"); + }, + + /** + * Returns the value for the header specified by fieldName as an array. + * + * @throws NS_ERROR_INVALID_ARG + * if fieldName does not constitute a valid header field name + * @throws NS_ERROR_NOT_AVAILABLE + * if the given header does not exist in this + * @returns [string] + * an array of all the header values in this for the given + * header name. Header values will generally be collapsed + * into a single header by joining all header values together + * with commas, but certain headers (Proxy-Authenticate, + * WWW-Authenticate, and Set-Cookie) violate the HTTP spec + * and cannot be collapsed in this manner. For these headers + * only, the returned array may contain multiple elements if + * that header has been added more than once. + */ + getHeaderValues: function(fieldName) { var name = headerUtils.normalizeFieldName(fieldName); diff --git a/netwerk/test/httpserver/nsIHttpServer.idl b/netwerk/test/httpserver/nsIHttpServer.idl index 660d82e7ccc6..24e6a81a562e 100644 --- a/netwerk/test/httpserver/nsIHttpServer.idl +++ b/netwerk/test/httpserver/nsIHttpServer.idl @@ -349,7 +349,7 @@ interface nsIHttpRequestHandler : nsISupports /** * A representation of the data included in an HTTP request. */ -[scriptable, uuid(ed8bdb6b-83e0-43aB-a412-a9863bd79394)] +[scriptable, uuid(80cbca71-dc51-4fa0-9010-1cec262dbd4a)] interface nsIHttpRequestMetadata : nsIPropertyBag { /** @@ -408,10 +408,11 @@ interface nsIHttpRequestMetadata : nsIPropertyBag * header field names are case-insensitive, this method produces equivalent * results for "HeAdER" and "hEADer" as fieldName * @returns - * the field value for the given header; note that this value may be - * normalized (e.g., leading/trailing whitespace removed from the value [or - * from the values which make this up, if the header is a comma-separated - * list of values], whitespace runs compressed to single spaces, etc.) + * The result is a string containing the individual values of the header, + * usually separated with a comma. The headers WWW-Authenticate, + * Proxy-Authenticate, and Set-Cookie violate the HTTP specification, + * however, and for these headers only the separator string is '\n'. + * * @throws NS_ERROR_INVALID_ARG * if fieldName does not constitute a valid header field name * @throws NS_ERROR_NOT_AVAILABLE diff --git a/netwerk/test/httpserver/test/test_header_array.js b/netwerk/test/httpserver/test/test_header_array.js new file mode 100644 index 000000000000..6b27657238c8 --- /dev/null +++ b/netwerk/test/httpserver/test/test_header_array.js @@ -0,0 +1,102 @@ +/* -*- 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): + * Honza Bambas + * + * 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 ***** */ + +// test that special headers are sent as an array of headers with the same name + +const PORT = 4444; + +function run_test() +{ + var srv; + + srv = createServer(); + srv.registerPathHandler("/path-handler", pathHandler); + srv.start(PORT); + + function done() + { + srv.stop(); + } + + runHttpTests(tests, done); +} + + +/************ + * HANDLERS * + ************/ + +function pathHandler(request, response) +{ + response.setHeader("Cache-Control", "no-cache", false); + + response.setHeader("Proxy-Authenticate", "First line 1", true); + response.setHeader("Proxy-Authenticate", "Second line 1", true); + response.setHeader("Proxy-Authenticate", "Third line 1", true); + + response.setHeader("WWW-Authenticate", "Not merged line 1", false); + response.setHeader("WWW-Authenticate", "Not merged line 2", true); + + response.setHeader("WWW-Authenticate", "First line 2", false); + response.setHeader("WWW-Authenticate", "Second line 2", true); + response.setHeader("WWW-Authenticate", "Third line 2", true); + + response.setHeader("X-Single-Header-Merge", "Single 1", true); + response.setHeader("X-Single-Header-Merge", "Single 2", true); +} + +/*************** + * BEGIN TESTS * + ***************/ + +var tests = [ + new Test("http://localhost:4444/path-handler", + null, check)]; + +function check(ch, cx) +{ + var headerValue; + + headerValue = ch.getResponseHeader("Proxy-Authenticate"); + do_check_eq(headerValue, "First line 1\nSecond line 1\nThird line 1"); + headerValue = ch.getResponseHeader("WWW-Authenticate"); + do_check_eq(headerValue, "First line 2\nSecond line 2\nThird line 2"); + headerValue = ch.getResponseHeader("X-Single-Header-Merge"); + do_check_eq(headerValue, "Single 1,Single 2"); +}