-/* $Id: httpd.js,v 1.32 2005/07/08 17:57:28 mmondor Exp $ */
+/* $Id: httpd.js,v 1.33 2005/07/09 05:44:53 mmondor Exp $ */
/*
* Copyright (c) 2005, Matthew Mondor
return contents;
}
+/*
+ * Similar to the above function, but works with an already open FD object
+ * and closes it.
+ */
+function file_read2(fd)
+{
+ var contents = '';
+ var data;
+
+ try {
+ for (;;) {
+ data = fd.read(8192);
+ if (data.length == 0)
+ break;
+ contents += data;
+ }
+ } catch (x) {
+ err.put(x + "\n");
+ }
+ fd.close();
+
+ return contents;
+}
+
eval(file_read('options.js')); /* Configuration */
eval(file_read('ml.js')); /* MLTag object for HTML generation */
eval(file_read('root.js')); /* Root object for virtual chroot(2) */
* Server identification
*/
SERVER_VERSION = 'mmondor_js_httpd/0.0.1 (NetBSD)';
-SERVER_CVSID = '$Id: httpd.js,v 1.32 2005/07/08 17:57:28 mmondor Exp $';
+SERVER_CVSID = '$Id: httpd.js,v 1.33 2005/07/09 05:44:53 mmondor Exp $';
/*
+ * And our pre-evaluated scripts cache
+ */
+var jso_cache = {};
+
+function JSO(path, time, script)
+{
+ this.path = path;
+ this.time = time;
+ this.script = script;
+
+ jso_cache[this.path] = this;
+}
+
+
+
+/*
* The HTTPReply object allows to cache addHeader() and addContent() requests,
* and to eventually flush the whole reply to an arbitrary FD afterwards.
*/
this.desc = desc;
this.headers = [];
this.contents = [];
+ this.type = type;
/*
* Insert our standard headers.
this.headers.push('Date: ' + this.gmttime);
this.headers.push('Server: ' + SERVER_VERSION);
this.headers.push('Connection: close');
- if (type != null)
- this.headers.push('Content-Type: ' + type);
}
/*
* HTTPReply prototype object
*/
HTTPReply.prototype = {
+ setType: function(type)
+ {
+
+ this.type = type;
+ },
+
addHeader: function(data)
{
headers += 'HTTP/1.1 ' + this.code + ' ' + this.desc + "\r\n";
for (i = 0; i < this.headers.length; i++)
headers += this.headers[i] + "\r\n";
+ if (this.type != null)
+ headers += 'Content-Type: ' + this.type + "\r\n";
headers += "\r\n";
fd.write(headers + contents);
this.http_vars_session.count++;
/*
+ * Verify if document consists of an .jso (server javascript module)
+ * file, part of a dynamic HTTP application.
+ * Extract file extension, as well as its mime type.
+ */
+ if ((i = path.virtual.lastIndexOf('.')) != -1) {
+ ext = (path.virtual.substr(i + 1)).toLowerCase();
+ if (ext.lastIndexOf('/') == -1 &&
+ mimetypes_table[ext] != undefined)
+ mimetype = mimetypes_table[ext];
+ else
+ mimetype = options.default_mimetype;
+ }
+ mimetype += '; charset=' + (this.http_vhost.charset == undefined ?
+ options.default_charset : this.http_vhost.charset);
+
+ /*
+ * JavaScript Object (JSO) files are processed especially.
+ * We interpret them, providing them with an environment to
+ * access the needed resources (_GET/_POST/_COOKIE variables,
+ * as well as the document object they can use.
+ * We flush the document once the script returns, or fire up
+ * an error if something fails.
+ */
+ if (ext == 'jso') {
+
+ /* Create document object */
+ var obj = new HTTPReply(200, 'OK', mimetype);
+ obj.addNoCacheHeaders();
+ if (cookie != undefined)
+ obj.addHeader(cookie);
+
+ /* XXX Add other objects to export as necessary */
+ obj._GET = this.http_vars_get;
+ obj._POST = this.http_vars_post;
+ obj._COOKIES = this.http_vars_cookies;
+ obj._SESSION = this.http_vars_session;
+
+ /*
+ * Check if object in our cache already and file not modified
+ * since cached entry. Reuse function then. Otherwise,
+ * load in function from file, store a cache entry for it
+ * and launch it.
+ */
+
+ var data, s, o;
+
+ if (((o = jso_cache[path.real]) != undefined) &&
+ o.time == st.st_mtime)
+ obj.script = o.script;
+ else {
+ try {
+ data = file_read2(fd);
+ s = 'obj.script = function() {' + data + '}';
+ eval(s);
+ o = new JSO(path.real, st.st_mtime,
+ obj.script);
+ } catch (x) {
+ err.put(x + "\n");
+ http_error(this, 500, 'Internal Server Error',
+ 'Please try again later.');
+ return true;
+ }
+ }
+
+ try {
+ if (obj.script != undefined)
+ obj.script();
+ obj.flush(this, null);
+ } catch (x) {
+ err.put(x + "\n");
+ http_error(this, 500, 'Internal Server Error',
+ 'Please try again later.');
+ }
+
+ return true;
+ }
+
+ /*
* If client only wanted the document if it wasn't modified since
* a certain time, report that it wasn't if it is the case.
*/
this.events = FD.POLLOUT;
/*
- * Obtain mimetype for file and flush HTTP header.
- * Then return with close=false, to delegate operations to
- * process_transfer().
+ * Flush HTTP header. Then return with close=false, to delegate
+ * operations to process_transfer().
*/
- if ((i = path.virtual.lastIndexOf('.')) != -1) {
- ext = (path.virtual.substr(i + 1)).toLowerCase();
- if (ext.lastIndexOf('/') == -1 &&
- mimetypes_table[ext] != undefined)
- mimetype = mimetypes_table[ext];
- }
- mimetype += '; charset=' + (this.http_vhost.charset == undefined ?
- options.default_charset : this.http_vhost.charset);
res = new HTTPReply(200, 'OK', mimetype);
if (cookie != undefined)
res.addHeader(cookie);
mimetypes_table[ext] = i;
}
}
+ if (mimetypes_table['jso'] == undefined)
+ mimetypes_table['jso'] = 'text/html';
/*
* Create polling array set