Bug 401649 - JS CGI support in httpd.js -- create responses conditioned on header values and HTTP method types! r=sayrer
This commit is contained in:
Родитель
e122f42649
Коммит
f3da875427
|
@ -1,21 +1,21 @@
|
|||
MozJSHTTP README
|
||||
================
|
||||
httpd.js README
|
||||
===============
|
||||
|
||||
MozJSHTTP is a small cross-platform implementation of an HTTP/1.1 server in
|
||||
httpd.js is a small cross-platform implementation of an HTTP/1.1 server in
|
||||
JavaScript for the Mozilla platform.
|
||||
|
||||
MozJSHTTP may be used as an XPCOM component, as an inline script in a document
|
||||
httpd.js may be used as an XPCOM component, as an inline script in a document
|
||||
with XPCOM privileges, or from the XPCOM shell (xpcshell). Currently, its most-
|
||||
supported method of use is from the XPCOM shell, where you can get all the
|
||||
dynamicity of JS in adding request handlers and the like, but component-based
|
||||
equivalent functionality is planned.
|
||||
|
||||
|
||||
Using MozJSHTTP as an XPCOM Component
|
||||
-------------------------------------
|
||||
Using httpd.js as an XPCOM Component
|
||||
------------------------------------
|
||||
|
||||
First, create an XPT file for nsIHttpServer.idl, using the xpidl tool included
|
||||
in the Mozilla SDK for the environment in which you wish to run MozJSHTTP. See
|
||||
in the Mozilla SDK for the environment in which you wish to run httpd.js. See
|
||||
<http://developer.mozilla.org/en/docs/XPIDL:xpidl> for further details on how to
|
||||
do this.
|
||||
|
||||
|
@ -47,10 +47,10 @@ code which does this:
|
|||
server.stop();
|
||||
|
||||
|
||||
Using MozJSHTTP as an Inline Script or from xpcshell
|
||||
----------------------------------------------------
|
||||
Using httpd.js as an Inline Script or from xpcshell
|
||||
---------------------------------------------------
|
||||
|
||||
Using MozJSHTTP as a script or from xpcshell isn't very different from using it
|
||||
Using httpd.js as a script or from xpcshell isn't very different from using it
|
||||
as a component; the only real difference lies in how you create an instance of
|
||||
the server. To create an instance, do the following:
|
||||
|
||||
|
@ -58,18 +58,18 @@ the server. To create an instance, do the following:
|
|||
|
||||
You now can use |server| exactly as you would when |server| was created as an
|
||||
XPCOM component. Note, however, that doing so will trample over the global
|
||||
namespace, and global values defined in MozJSHTTP will leak into your script.
|
||||
namespace, and global values defined in httpd.js will leak into your script.
|
||||
This may typically be benign, but since some of the global values defined are
|
||||
constants (specifically, Cc/Ci/Cr as abbreviations for the classes, interfaces,
|
||||
and results properties of Components), it's possible this trampling could
|
||||
break your script. In general you should use MozJSHTTP as an XPCOM component
|
||||
break your script. In general you should use httpd.js as an XPCOM component
|
||||
whenever possible.
|
||||
|
||||
|
||||
Known Issues
|
||||
------------
|
||||
|
||||
While MozJSHTTP runs on Mozilla 1.8 and 1.9 platforms, it doesn't run quite as
|
||||
While httpd.js runs on Mozilla 1.8 and 1.9 platforms, it doesn't run quite as
|
||||
well on 1.8 due to the absence of some APIs, specifically the threading APIs.
|
||||
The biggest problem here is that server shutdown (see nsIHttpServer.stop) is not
|
||||
guaranteed to complete after all pending requests have been served; if you are
|
||||
|
@ -78,7 +78,7 @@ calling server.stop() before the host application closes to ensure that all
|
|||
requests have completed. Things probably aren't going to break too horribly if
|
||||
you don't do this, but better safe than sorry.
|
||||
|
||||
MozJSHTTP makes no effort to time out requests, beyond any the socket itself
|
||||
httpd.js makes no effort to time out requests, beyond any the socket itself
|
||||
might or might not provide. I don't believe it provides any by default, but
|
||||
I haven't verified this.
|
||||
|
||||
|
@ -96,6 +96,6 @@ A special testing function, |server|, is provided for use in xpcshell for quick
|
|||
testing of the server; see the source code for details on its use. You don't
|
||||
want to use this in a script, however, because doing so will block until the
|
||||
server is shut down. It's also a good example of how to use the basic
|
||||
functionality of MozJSHTTP, if you need one.
|
||||
functionality of httpd.js, if you need one.
|
||||
|
||||
Have fun!
|
||||
|
|
|
@ -49,11 +49,14 @@
|
|||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cr = Components.results;
|
||||
const Cu = Components.utils;
|
||||
const CC = Components.Constructor;
|
||||
|
||||
/** True if debugging output is enabled, false otherwise. */
|
||||
var DEBUG = false; // non-const *only* so tweakable in server tests
|
||||
|
||||
var gGlobalObject = this;
|
||||
|
||||
/**
|
||||
* Asserts that the given condition holds. If it doesn't, the given message is
|
||||
* dumped, a stack trace is printed, and an exception is thrown to attempt to
|
||||
|
@ -157,6 +160,9 @@ const HIDDEN_CHAR = "^";
|
|||
*/
|
||||
const HEADERS_SUFFIX = HIDDEN_CHAR + "headers" + HIDDEN_CHAR;
|
||||
|
||||
/** Type used to denote SJS scripts for CGI-like functionality. */
|
||||
const SJS_TYPE = "sjs";
|
||||
|
||||
|
||||
/** dump(str) with a trailing "\n" -- only outputs if DEBUG */
|
||||
function dumpn(str)
|
||||
|
@ -189,6 +195,9 @@ const ServerSocket = CC("@mozilla.org/network/server-socket;1",
|
|||
const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1",
|
||||
"nsIBinaryInputStream",
|
||||
"setInputStream");
|
||||
const ScriptableInputStream = CC("@mozilla.org/scriptableinputstream;1",
|
||||
"nsIScriptableInputStream",
|
||||
"init");
|
||||
const Pipe = CC("@mozilla.org/pipe;1",
|
||||
"nsIPipe",
|
||||
"init");
|
||||
|
@ -441,7 +450,7 @@ nsHttpServer.prototype =
|
|||
//
|
||||
registerFile: function(path, file)
|
||||
{
|
||||
if (!file.exists() || file.isDirectory())
|
||||
if (file && (!file.exists() || file.isDirectory()))
|
||||
throw Cr.NS_ERROR_INVALID_ARG;
|
||||
|
||||
this._handler.registerFile(path, file);
|
||||
|
@ -489,6 +498,14 @@ nsHttpServer.prototype =
|
|||
this._handler.setIndexHandler(handler);
|
||||
},
|
||||
|
||||
//
|
||||
// see nsIHttpServer.registerContentType
|
||||
//
|
||||
registerContentType: function(ext, type)
|
||||
{
|
||||
this._handler.registerContentType(ext, type);
|
||||
},
|
||||
|
||||
// NSISUPPORTS
|
||||
|
||||
//
|
||||
|
@ -1237,29 +1254,6 @@ LineData.prototype =
|
|||
|
||||
|
||||
|
||||
/**
|
||||
* Gets a content-type for the given file, as best as it is possible to do so.
|
||||
*
|
||||
* @param file : nsIFile
|
||||
* the nsIFile for which to get a file type
|
||||
* @returns string
|
||||
* the best content-type which can be determined for the file
|
||||
*/
|
||||
function getTypeFromFile(file)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Cc["@mozilla.org/uriloader/external-helper-app-service;1"]
|
||||
.getService(Ci.nsIMIMEService)
|
||||
.getTypeFromFile(file);
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
return "application/octet-stream";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a request-handling function for an nsIHttpRequestHandler object.
|
||||
*/
|
||||
|
@ -1525,6 +1519,12 @@ function ServerHandler(server)
|
|||
*/
|
||||
this._overrideErrors = {};
|
||||
|
||||
/**
|
||||
* Maps file extensions to their MIME types in the server, overriding any
|
||||
* mapping that might or might not exist in the MIME service.
|
||||
*/
|
||||
this._mimeMappings = {};
|
||||
|
||||
/**
|
||||
* The default handler for requests for directories, used to serve directories
|
||||
* when no index file is present.
|
||||
|
@ -1620,6 +1620,13 @@ ServerHandler.prototype =
|
|||
//
|
||||
registerFile: function(path, file)
|
||||
{
|
||||
if (!file)
|
||||
{
|
||||
dumpn("*** unregistering '" + path + "' mapping");
|
||||
delete this._overridePaths[path];
|
||||
return;
|
||||
}
|
||||
|
||||
dumpn("*** registering '" + path + "' as mapping to " + file.path);
|
||||
file = file.clone();
|
||||
|
||||
|
@ -1631,8 +1638,7 @@ ServerHandler.prototype =
|
|||
throw HTTP_404;
|
||||
|
||||
response.setStatusLine(metadata.httpVersion, 200, "OK");
|
||||
self._writeFileResponse(file, response);
|
||||
maybeAddHeaders(file, metadata, response);
|
||||
self._writeFileResponse(metadata, file, response);
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -1703,6 +1709,17 @@ ServerHandler.prototype =
|
|||
this._indexHandler = handler;
|
||||
},
|
||||
|
||||
//
|
||||
// see nsIHttpServer.registerContentType
|
||||
//
|
||||
registerContentType: function(ext, type)
|
||||
{
|
||||
if (!type)
|
||||
delete this._mimeMappings[ext];
|
||||
else
|
||||
this._mimeMappings[ext] = headerUtils.normalizeFieldValue(type);
|
||||
},
|
||||
|
||||
// NON-XPCOM PUBLIC API
|
||||
|
||||
/**
|
||||
|
@ -1784,37 +1801,94 @@ ServerHandler.prototype =
|
|||
|
||||
// finally...
|
||||
dumpn("*** handling '" + path + "' as mapping to " + file.path);
|
||||
this._writeFileResponse(file, response);
|
||||
|
||||
maybeAddHeaders(file, metadata, response);
|
||||
this._writeFileResponse(metadata, file, response);
|
||||
},
|
||||
|
||||
/**
|
||||
* Writes an HTTP response for the given file, including setting headers for
|
||||
* file metadata.
|
||||
*
|
||||
* @param metadata : Request
|
||||
* the Request for which a response is being generated
|
||||
* @param file : nsILocalFile
|
||||
* the file which is to be sent in the response
|
||||
* @param response : Response
|
||||
* the response to which the file should be written
|
||||
*/
|
||||
_writeFileResponse: function(file, response)
|
||||
_writeFileResponse: function(metadata, file, response)
|
||||
{
|
||||
const PR_RDONLY = 0x01;
|
||||
|
||||
var type = this._getTypeFromFile(file);
|
||||
if (type == SJS_TYPE)
|
||||
{
|
||||
try
|
||||
{
|
||||
var fis = new FileInputStream(file, PR_RDONLY, 0444,
|
||||
Ci.nsIFileInputStream.CLOSE_ON_EOF);
|
||||
var sis = new ScriptableInputStream(fis);
|
||||
var s = Cu.Sandbox(gGlobalObject);
|
||||
Cu.evalInSandbox(sis.read(file.fileSize), s);
|
||||
s.handleRequest(metadata, response);
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
dumpn("*** error running SJS: " + e);
|
||||
throw HTTP_500;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
response.setHeader("Last-Modified",
|
||||
toDateString(file.lastModifiedTime),
|
||||
false);
|
||||
}
|
||||
catch (e) { /* lastModifiedTime threw, ignore */ }
|
||||
|
||||
response.setHeader("Content-Type", type, false);
|
||||
|
||||
var fis = new FileInputStream(file, PR_RDONLY, 0444,
|
||||
Ci.nsIFileInputStream.CLOSE_ON_EOF);
|
||||
response.bodyOutputStream.writeFrom(fis, file.fileSize);
|
||||
fis.close();
|
||||
|
||||
maybeAddHeaders(file, metadata, response);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets a content-type for the given file, first by checking for any custom
|
||||
* MIME-types registered with this handler for the file's extension, second by
|
||||
* asking the global MIME service for a content-type, and finally by failing
|
||||
* over to application/octet-stream.
|
||||
*
|
||||
* @param file : nsIFile
|
||||
* the nsIFile for which to get a file type
|
||||
* @returns string
|
||||
* the best content-type which can be determined for the file
|
||||
*/
|
||||
_getTypeFromFile: function(file)
|
||||
{
|
||||
try
|
||||
{
|
||||
response.setHeader("Last-Modified",
|
||||
toDateString(file.lastModifiedTime),
|
||||
false);
|
||||
var name = file.leafName;
|
||||
var dot = name.lastIndexOf(".");
|
||||
if (dot > 0)
|
||||
{
|
||||
var ext = name.slice(dot + 1);
|
||||
if (ext in this._mimeMappings)
|
||||
return this._mimeMappings[ext];
|
||||
}
|
||||
return Cc["@mozilla.org/uriloader/external-helper-app-service;1"]
|
||||
.getService(Ci.nsIMIMEService)
|
||||
.getTypeFromFile(file);
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
return "application/octet-stream";
|
||||
}
|
||||
catch (e) { /* lastModifiedTime threw, ignore */ }
|
||||
|
||||
response.setHeader("Content-Type", getTypeFromFile(file), false);
|
||||
|
||||
const PR_RDONLY = 0x01;
|
||||
var fis = new FileInputStream(file, PR_RDONLY, 0444,
|
||||
Ci.nsIFileInputStream.CLOSE_ON_EOF);
|
||||
response.bodyOutputStream.writeFrom(fis, file.fileSize);
|
||||
fis.close();
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -2037,7 +2111,7 @@ ServerHandler.prototype =
|
|||
{
|
||||
// post-processing
|
||||
response.setHeader("Connection", "close", false);
|
||||
response.setHeader("Server", "MozJSHTTP", false);
|
||||
response.setHeader("Server", "httpd.js", false);
|
||||
response.setHeader("Date", toDateString(Date.now()), false);
|
||||
|
||||
var bodyStream = response.bodyInputStream;
|
||||
|
@ -3291,6 +3365,7 @@ function server(port, basePath)
|
|||
var srv = new nsHttpServer();
|
||||
if (lp)
|
||||
srv.registerDirectory("/", lp);
|
||||
srv.registerContentType("sjs", SJS_TYPE);
|
||||
srv.start(port);
|
||||
|
||||
var thread = gThreadManager.currentThread;
|
||||
|
|
|
@ -87,8 +87,8 @@ interface nsIHttpServer : nsIServerSocketListener
|
|||
* the path which is to be mapped to the given file; must begin with "/" and
|
||||
* be a valid URI path (i.e., no query string, hash reference, etc.)
|
||||
* @param file
|
||||
* the file to serve for the given path; this file must exist for the
|
||||
* lifetime of the server
|
||||
* the file to serve for the given path, or null to remove any mapping that
|
||||
* might exist; this file must exist for the lifetime of the server
|
||||
*/
|
||||
void registerFile(in string path, in nsILocalFile file);
|
||||
|
||||
|
@ -148,6 +148,27 @@ interface nsIHttpServer : nsIServerSocketListener
|
|||
*/
|
||||
void registerDirectory(in string path, in nsILocalFile dir);
|
||||
|
||||
/**
|
||||
* Associates files with the given extension with the given Content-Type when
|
||||
* served by this server, in the absence of any file-specific information
|
||||
* about the desired Content-Type. If type is empty, removes any extant
|
||||
* mapping, if one is present.
|
||||
*
|
||||
* @throws NS_ERROR_INVALID_ARG
|
||||
* if the given type is not a valid header field value, i.e. if it doesn't
|
||||
* match the field-value production in RFC 2616
|
||||
* @note
|
||||
* No syntax checking is done of the given type, beyond ensuring that it is
|
||||
* a valid header field value. Behavior when not given a string matching
|
||||
* the media-type production in RFC 2616 section 3.7 is undefined.
|
||||
* Implementations may choose to define specific behavior for types which do
|
||||
* not match the production, such as for CGI functionality.
|
||||
* @note
|
||||
* Implementations MAY treat type as a trusted argument; users who fail to
|
||||
* generate this string from trusted data risk security vulnerabilities.
|
||||
*/
|
||||
void registerContentType(in string extension, in string type);
|
||||
|
||||
/**
|
||||
* Sets the handler used to display the contents of a directory if
|
||||
* the directory contains no index page.
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
function handleRequest(request, response)
|
||||
{
|
||||
if (request.queryString == "throw")
|
||||
throw "monkey wrench!";
|
||||
|
||||
response.setHeader("Content-Type", "text/plain", false);
|
||||
response.write("PASS");
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
HTTP 500 Error
|
||||
This-Header: SHOULD NOT APPEAR IN CGI.JSC RESPONSES!
|
|
@ -0,0 +1,4 @@
|
|||
function handleRequest(request, response)
|
||||
{
|
||||
response.write("FAIL");
|
||||
}
|
|
@ -82,6 +82,26 @@ function makeBIS(stream)
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the contents of the file as a string.
|
||||
*
|
||||
* @param file : nsILocalFile
|
||||
* the file whose contents are to be read
|
||||
* @returns string
|
||||
* the contents of the file
|
||||
*/
|
||||
function fileContents(file)
|
||||
{
|
||||
const PR_RDONLY = 0x01;
|
||||
var fis = new FileInputStream(file, PR_RDONLY, 0444,
|
||||
Ci.nsIFileInputStream.CLOSE_ON_EOF);
|
||||
var sis = new ScriptableInputStream(fis);
|
||||
var contents = sis.read(file.fileSize);
|
||||
sis.close();
|
||||
return contents;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************
|
||||
* SIMPLE SUPPORT FOR LOADING/TESTING A SERIES OF URLS *
|
||||
*******************************************************/
|
||||
|
|
|
@ -40,10 +40,6 @@
|
|||
|
||||
const PREFIX = "http://localhost:4444";
|
||||
|
||||
const ScriptableInputStream = CC("@mozilla.org/scriptableinputstream;1",
|
||||
"nsIScriptableInputStream",
|
||||
"init");
|
||||
|
||||
var tests =
|
||||
[
|
||||
new Test(PREFIX + "/test_both.html",
|
||||
|
|
|
@ -0,0 +1,245 @@
|
|||
/* -*- 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
|
||||
* Jeff Walden <jwalden+code@mit.edu>.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2007
|
||||
* 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 ***** */
|
||||
|
||||
// tests support for server JS-generated pages
|
||||
|
||||
const BASE = "http://localhost:4444";
|
||||
|
||||
var sjs = do_get_file("netwerk/test/httpserver/test/data/sjs/cgi.sjs");
|
||||
var srv;
|
||||
var test;
|
||||
var tests = [];
|
||||
|
||||
|
||||
/*********************
|
||||
* UTILITY FUNCTIONS *
|
||||
*********************/
|
||||
|
||||
function isException(e, code)
|
||||
{
|
||||
if (e !== code && e.result !== code)
|
||||
do_throw("unexpected error: " + e);
|
||||
}
|
||||
|
||||
function bytesToString(bytes)
|
||||
{
|
||||
return bytes.map(function(v) { return String.fromCharCode(v); }).join("");
|
||||
}
|
||||
|
||||
function skipCache(ch)
|
||||
{
|
||||
ch.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
|
||||
}
|
||||
|
||||
|
||||
/********************
|
||||
* DEFINE THE TESTS *
|
||||
********************/
|
||||
|
||||
/**
|
||||
* Adds the set of tests defined in here, differentiating between tests with a
|
||||
* SJS which throws an exception and creates a server error and tests with a
|
||||
* normal, successful SJS.
|
||||
*/
|
||||
function setupTests(throwing)
|
||||
{
|
||||
const TEST_URL = BASE + "/cgi.sjs" + (throwing ? "?throw" : "");
|
||||
|
||||
// registerFile with SJS => raw text
|
||||
|
||||
function setupFile(ch)
|
||||
{
|
||||
srv.registerFile("/cgi.sjs", sjs);
|
||||
skipCache(ch);
|
||||
}
|
||||
|
||||
function verifyRawText(channel, cx, status, bytes)
|
||||
{
|
||||
dumpn(channel.originalURI.spec);
|
||||
do_check_eq(bytesToString(bytes), fileContents(sjs));
|
||||
}
|
||||
|
||||
test = new Test(TEST_URL, setupFile, null, verifyRawText);
|
||||
tests.push(test);
|
||||
|
||||
|
||||
// add mapping, => interpreted
|
||||
|
||||
function addTypeMapping(ch)
|
||||
{
|
||||
srv.registerContentType("sjs", "sjs");
|
||||
skipCache(ch);
|
||||
}
|
||||
|
||||
function checkType(ch, cx)
|
||||
{
|
||||
if (throwing)
|
||||
{
|
||||
do_check_false(ch.requestSucceeded);
|
||||
do_check_eq(ch.responseStatus, 500);
|
||||
}
|
||||
else
|
||||
{
|
||||
do_check_eq(ch.contentType, "text/plain");
|
||||
}
|
||||
}
|
||||
|
||||
function checkContents(ch, cx, status, data)
|
||||
{
|
||||
if (!throwing)
|
||||
do_check_eq("PASS", bytesToString(data));
|
||||
}
|
||||
|
||||
test = new Test(TEST_URL, addTypeMapping, checkType, checkContents);
|
||||
tests.push(test);
|
||||
|
||||
|
||||
// remove file/type mapping, map containing directory => raw text
|
||||
|
||||
function setupDirectoryAndRemoveType(ch)
|
||||
{
|
||||
dumpn("removing type mapping");
|
||||
srv.registerContentType("sjs", null);
|
||||
srv.registerFile("/cgi.sjs", null);
|
||||
srv.registerDirectory("/", sjs.parent);
|
||||
skipCache(ch);
|
||||
}
|
||||
|
||||
test = new Test(TEST_URL, setupDirectoryAndRemoveType, null, verifyRawText);
|
||||
tests.push(test);
|
||||
|
||||
|
||||
// add mapping, => interpreted
|
||||
|
||||
function contentAndCleanup(ch, cx, status, data)
|
||||
{
|
||||
checkContents(ch, cx, status, data);
|
||||
|
||||
// clean up state we've set up
|
||||
srv.registerDirectory("/", null);
|
||||
srv.registerContentType("sjs", null);
|
||||
}
|
||||
|
||||
test = new Test(TEST_URL, addTypeMapping, checkType, contentAndCleanup);
|
||||
tests.push(test);
|
||||
|
||||
// NB: No remaining state in the server right now! If we have any here,
|
||||
// either the second run of tests (without ?throw) or the two tests
|
||||
// added after the two sets will almost certainly fail.
|
||||
}
|
||||
|
||||
|
||||
/*****************
|
||||
* ADD THE TESTS *
|
||||
*****************/
|
||||
|
||||
setupTests(true);
|
||||
setupTests(false);
|
||||
|
||||
// Test that when extension-mappings are used, the entire filename cannot be
|
||||
// treated as an extension -- there must be at least one dot for a filename to
|
||||
// match an extension.
|
||||
|
||||
function init(ch)
|
||||
{
|
||||
// clean up state we've set up
|
||||
srv.registerDirectory("/", sjs.parent);
|
||||
srv.registerContentType("sjs", "sjs");
|
||||
skipCache(ch);
|
||||
}
|
||||
|
||||
function checkNotSJS(ch, cx, status, data)
|
||||
{
|
||||
do_check_neq("FAIL", bytesToString(data));
|
||||
}
|
||||
|
||||
test = new Test(BASE + "/sjs", init, null, checkNotSJS);
|
||||
tests.push(test);
|
||||
|
||||
|
||||
// One last test: for file mappings, the content-type is determined by the
|
||||
// extension of the file on the server, not by the extension of the requested
|
||||
// path.
|
||||
|
||||
function setupFileMapping(ch)
|
||||
{
|
||||
srv.registerFile("/script.html", sjs);
|
||||
}
|
||||
|
||||
function onStart(ch, cx)
|
||||
{
|
||||
do_check_eq(ch.contentType, "text/plain");
|
||||
}
|
||||
|
||||
function onStop(ch, cx, status, data)
|
||||
{
|
||||
do_check_eq("PASS", bytesToString(data));
|
||||
}
|
||||
|
||||
test = new Test(BASE + "/script.html", setupFileMapping, onStart, onStop);
|
||||
tests.push(test);
|
||||
|
||||
|
||||
/*****************
|
||||
* RUN THE TESTS *
|
||||
*****************/
|
||||
|
||||
function run_test()
|
||||
{
|
||||
srv = createServer();
|
||||
|
||||
// Test for a content-type which isn't a field-value
|
||||
try
|
||||
{
|
||||
srv.registerContentType("foo", "bar\nbaz");
|
||||
throw "this server throws on content-types which aren't field-values";
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
isException(e, Cr.NS_ERROR_INVALID_ARG);
|
||||
}
|
||||
|
||||
|
||||
// NB: The server has no state at this point -- all state is set up and torn
|
||||
// down in the tests, because we run the same tests twice with only a
|
||||
// different query string on the requests, followed by the oddball
|
||||
// test that doesn't care about throwing or not.
|
||||
|
||||
srv.start(4444);
|
||||
runHttpTests(tests, function() { srv.stop(); });
|
||||
}
|
Загрузка…
Ссылка в новой задаче