bug 367537. enhancements to httpd.js. add an index handler and respose.write. r=jwalden

This commit is contained in:
sayrer%gmail.com 2007-01-24 16:57:03 +00:00
Родитель 178fbd42c6
Коммит 23223c2562
4 изменённых файлов: 374 добавлений и 19 удалений

Просмотреть файл

@ -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 <jwalden+code@mit.edu> (v3, netwerk/test/httpserver/httpd.js)
* Robert Sayre <sayrer@gmail.com>
*
* 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
/**

Просмотреть файл

@ -20,6 +20,7 @@
*
* Contributor(s):
* Jeff Walden <jwalden+code@mit.edu> (original author)
* Robert Sayre <sayrer@gmail.com>
*
* 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);
};

Просмотреть файл

@ -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 <jwalden+code@mit.edu>.
* 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);
}

Просмотреть файл

@ -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 <jwalden+code@mit.edu>.
* 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!");
}