diff --git a/mozilla/netwerk/test/httpserver/httpd.js b/mozilla/netwerk/test/httpserver/httpd.js index 5257a84c1b0..d7cdb59113b 100644 --- a/mozilla/netwerk/test/httpserver/httpd.js +++ b/mozilla/netwerk/test/httpserver/httpd.js @@ -1,3 +1,5 @@ +/* -*- 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 * @@ -22,6 +24,7 @@ * Darin Fisher (v1, netwerk/test/TestServ.js) * Christian Biesinger (v2, netwerk/test/unit/head_http_server.js) * Jeff Walden (v3, netwerk/test/httpserver/httpd.js) + * Robert Sayre * * 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 @@ -396,6 +399,14 @@ nsHttpServer.prototype = this._handler.registerErrorHandler(code, handler); }, + // + // see nsIHttpServer.setIndexHandler + // + setIndexHandler: function(handler) + { + this._handler.setIndexHandler(handler); + }, + // NSISUPPORTS // @@ -538,6 +549,16 @@ function createHandlerFunc(handler) return function(metadata, response) { handler.handle(metadata, response); }; } +/** + * The default handler for directories. + */ +function defaultIndexHandler(metadata, response) +{ + var file = metadata.getProperty("directory"); + NS_ASSERT(file); + + throw HTTP_501; // need directory listings ftw! +} /** * An object which handles requests for a server, executing default and @@ -592,6 +613,12 @@ function ServerHandler(srv) * @see also ServerHandler.prototype._defaultErrors */ this._overrideErrors = {}; + + /** + * Init our default handler for directories. The handler used to + * serve directories when no index file is present. + */ + this._indexHandler = defaultIndexHandler; } ServerHandler.prototype = { @@ -711,13 +738,7 @@ ServerHandler.prototype = if (path.charAt(0) != "/") throw Cr.NS_ERROR_INVALID_ARG; - // for convenience, handler can be a function if this is run from xpcshell - if (typeof(handler) == "function") - this._overridePaths[path] = handler; - else if (handler) - this._overridePaths[path] = createHandlerFunc(handler); - else - delete this._overridePaths[path]; + this._handlerToField(handler, this._overridePaths, path); }, // @@ -752,13 +773,41 @@ ServerHandler.prototype = dumpn("*** WARNING: registering non-HTTP/1.1 error code " + "(" + err + ") handler -- was this intentional?"); + this._handlerToField(handler, this._overrideErrors, err); + }, + + // + // see nsIHttpServer.setIndexHandler + // + setIndexHandler: function(handler) + { + if (!handler) + handler = defaultIndexHandler; + else if (typeof(handler) != "function") + handler = createHandlerFunc(handler); + + this._indexHandler = handler; + }, + + /** + * Set or remove a handler in an ojbect with a key. + * If handler is null, the key will be deleted. + * + * @param handler + * A function or an nsIHttpRequestHandler object. + * @param dict + * The object to attach the handler to. + * @param key + * The field name of the handler. + */ + _handlerToField: function(handler, dict, key) { // for convenience, handler can be a function if this is run from xpcshell if (typeof(handler) == "function") - this._overrideErrors[err] = handler; + dict[key] = handler; else if (handler) - this._overrideErrors[err] = createHandlerFunc(handler); + dict[key] = createHandlerFunc(handler); else - delete this._overrideErrors[err]; + delete dict[key]; }, /** @@ -800,13 +849,17 @@ ServerHandler.prototype = // path-to-directory mapping in the requested URL var file = this._getFileForPath(path); - // the "file" might be a directory -- deal + // the "file" might be a directory, in which case we either serve the + // contained index.html or make the index handler write the response if (file.exists() && file.isDirectory()) { - file.append("index.html"); // make configurable? - if (!file.exists() || - file.isDirectory()) - throw HTTP_501; // need directory listings ftw! + file.append("index.html"); // make configurable? + if (!file.exists() || file.isDirectory()) + { + metadata._bag.setPropertyAsInterface("directory", file.parent); + this._indexHandler(metadata, response); + return; + } } // alternately, the file might not exist @@ -1457,6 +1510,15 @@ Response.prototype = return this._bodyOutputStream; }, + // + // see nsIHttpResponse.write + // + write: function(data) + { + var dataAsString = String(data); + this.bodyOutputStream.write(dataAsString, dataAsString.length); + }, + // // see nsIHttpResponse.setStatusLine // @@ -2013,6 +2075,13 @@ function RequestMetadata(port) */ this._headers = new nsHttpHeaders(); + /** + * For the addition of ad-hoc properties and new functionality + * without having to tweak nsIHttpRequestMetadata every time. + */ + this._bag = Cc["@mozilla.org/hash-property-bag;1"] + .createInstance(Ci.nsIWritablePropertyBag2); + /** * The numeric HTTP error, if any, associated with this request. This value * may be set by the constructor but is usually only set by the handler after @@ -2101,6 +2170,22 @@ RequestMetadata.prototype = return this._headers.enumerator; }, + // + // see nsIPropertyBag.enumerator + // + get enumerator() + { + return this._bag.enumerator; + }, + + // + // see nsIPropertyBag.getProperty + // + getProperty: function(name) + { + return this._bag.getProperty(name); + }, + // ENTITY /** diff --git a/mozilla/netwerk/test/httpserver/nsIHttpServer.idl b/mozilla/netwerk/test/httpserver/nsIHttpServer.idl index 043a0d2e1fb..e4ccbaf771f 100644 --- a/mozilla/netwerk/test/httpserver/nsIHttpServer.idl +++ b/mozilla/netwerk/test/httpserver/nsIHttpServer.idl @@ -20,6 +20,7 @@ * * Contributor(s): * Jeff Walden (original author) + * Robert Sayre * * 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 @@ -36,6 +37,7 @@ * ***** END LICENSE BLOCK ***** */ #include "nsIServerSocket.idl" +#include "nsIPropertyBag.idl" interface nsILocalFile; interface nsISimpleEnumerator; @@ -145,8 +147,22 @@ interface nsIHttpServer : nsIServerSocketListener * does not begin with and end with a forward slash */ void registerDirectory(in string path, in nsILocalFile dir); -}; + /** + * Sets the handler used to display the contents of a directory if + * the directory contains no index page. + * + * @param handler + * an object which will handle any requests for directories which + * do not contain index pages, or null to reset to the default + * index handler; if while the server is running the handler + * throws an exception while responding to a request, an HTTP 500 + * response will be returned. An nsIFile corresponding to the + * directory is available from the metadata object passed to the + * handler, under the key "directory". + */ + void setIndexHandler(in nsIHttpRequestHandler handler); +}; /** * A representation of a handler for HTTP requests. The handler is used by @@ -183,8 +199,8 @@ interface nsIHttpRequestHandler : nsISupports /** * A representation of the data included in an HTTP request. */ -[scriptable, uuid(acbb5ea6-58b3-4750-bcc7-618910ebca9d)] -interface nsIHttpRequestMetadata : nsISupports +[scriptable, uuid(3a899b17-b6eb-4333-8ef4-912df454a551)] +interface nsIHttpRequestMetadata : nsIPropertyBag { /** * The request type for this request (see RFC 2616, section 5.1.1). @@ -269,7 +285,7 @@ interface nsIHttpRequestMetadata : nsISupports /** * Represents an HTTP response, as described in RFC 2616, section 6. */ -[scriptable, uuid(017f962b-137e-4911-887c-61808e89fa4f)] +[scriptable, uuid(a2aaaff7-03bd-43b6-b460-94671e288093)] interface nsIHttpResponse : nsISupports { /** @@ -317,4 +333,12 @@ interface nsIHttpResponse : nsISupports * written. */ readonly attribute nsIOutputStream bodyOutputStream; + + /** + * Write a string to the response's output stream. + * + * @note + * This method is only guaranteed to work with ASCII data. + */ + void write(in string data); }; diff --git a/mozilla/netwerk/test/httpserver/test/test_response_write.js b/mozilla/netwerk/test/httpserver/test/test_response_write.js new file mode 100644 index 00000000000..10ad5a166c3 --- /dev/null +++ b/mozilla/netwerk/test/httpserver/test/test_response_write.js @@ -0,0 +1,123 @@ +/* -*- 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 MozJSHTTP code. + * + * The Initial Developer of the Original Code is + * Jeff Walden . + * Portions created by the Initial Developer are Copyright (C) 2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * 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 ***** */ + +// make sure response.write works for strings, and coerces other args to strings + +var paths = + [ + "http://localhost:4444/writeString", + "http://localhost:4444/writeInt" + ]; +var currPathIndex = 0; + +var listener = + { + // NSISTREAMLISTENER + onDataAvailable: function(request, cx, inputStream, offset, count) + { + makeBIS(inputStream).readByteArray(count); // required by API + }, + // NSIREQUESTOBSERVER + onStartRequest: function(request, cx) + { + var ch = request.QueryInterface(Ci.nsIHttpChannel) + .QueryInterface(Ci.nsIHttpChannelInternal); + + switch (currPathIndex) + { + case 0: + do_check_eq(ch.getResponseHeader("Content-Length"), "4"); + break; + + case 1: + do_check_eq(ch.getResponseHeader("Content-Length"), "4"); + break; + } + }, + onStopRequest: function(request, cx, status) + { + do_check_true(Components.isSuccessCode(status)); + if (++currPathIndex == paths.length) + srv.stop(); + else + performNextTest(); + do_test_finished(); + }, + // NSISUPPORTS + QueryInterface: function(aIID) + { + if (aIID.equals(Ci.nsIStreamListener) || + aIID.equals(Ci.nsIRequestObserver) || + aIID.equals(Ci.nsISupports)) + return this; + throw Cr.NS_ERROR_NO_INTERFACE; + } + }; + +function performNextTest() +{ + do_test_pending(); + + var ch = makeChannel(paths[currPathIndex]); + ch.asyncOpen(listener, null); +} + +var srv; + +function run_test() +{ + srv = createServer(); + + srv.registerPathHandler("/writeString", writeString); + srv.registerPathHandler("/writeInt", writeInt); + srv.start(4444); + + performNextTest(); +} + +// PATH HANDLERS + +function writeString(metadata, response) +{ + response.write("1234"); +} + +function writeInt(metadata, response) +{ + response.write(1234); +} diff --git a/mozilla/netwerk/test/httpserver/test/test_setindexhandler.js b/mozilla/netwerk/test/httpserver/test/test_setindexhandler.js new file mode 100644 index 00000000000..bb11e612c04 --- /dev/null +++ b/mozilla/netwerk/test/httpserver/test/test_setindexhandler.js @@ -0,0 +1,123 @@ +/* -*- 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 MozJSHTTP code. + * + * The Initial Developer of the Original Code is + * Jeff Walden . + * Portions created by the Initial Developer are Copyright (C) 2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * 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 ***** */ + +// Make sure setIndexHandler works as expected + +var paths = + [ + "http://localhost:4444/", + "http://localhost:4444/" + ]; +var currPathIndex = 0; + +var listener = + { + // NSISTREAMLISTENER + onDataAvailable: function(request, cx, inputStream, offset, count) + { + makeBIS(inputStream).readByteArray(count); // required by API + }, + // NSIREQUESTOBSERVER + onStartRequest: function(request, cx) + { + var ch = request.QueryInterface(Ci.nsIHttpChannel) + .QueryInterface(Ci.nsIHttpChannelInternal); + + switch (currPathIndex) + { + case 0: + do_check_eq(ch.getResponseHeader("Content-Length"), "10"); + srv.setIndexHandler(null); + break; + + case 1: + do_check_eq(ch.responseStatus, 500); + break; + } + }, + onStopRequest: function(request, cx, status) + { + do_check_true(Components.isSuccessCode(status)); + if (++currPathIndex == paths.length) + srv.stop(); + else + performNextTest(); + do_test_finished(); + }, + // NSISUPPORTS + QueryInterface: function(aIID) + { + if (aIID.equals(Ci.nsIStreamListener) || + aIID.equals(Ci.nsIRequestObserver) || + aIID.equals(Ci.nsISupports)) + return this; + throw Cr.NS_ERROR_NO_INTERFACE; + } + }; + +function performNextTest() +{ + do_test_pending(); + + var ch = makeChannel(paths[currPathIndex]); + ch.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE; // important! + ch.asyncOpen(listener, null); +} + +var srv; + +function run_test() +{ + srv = createServer(); + dirServ = Cc["@mozilla.org/file/directory_service;1"] + .getService(Ci.nsIProperties); + serverBasePath = dirServ.get("CurProcD", Ci.nsILocalFile); + serverBasePath.append("httpserver_tests"); + srv.registerDirectory("/", serverBasePath); + srv.setIndexHandler(myIndexHandler); + srv.start(4444); + + performNextTest(); +} + +// PATH HANDLERS + +function myIndexHandler(metadata, response) +{ + response.write("directory!"); +}