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:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user