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:
2008-02-05 17:12:32 -08:00
parent c3f3ac6788
commit c0672b4a6d
9 changed files with 435 additions and 64 deletions

View File

@@ -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;