-/* $Id: httpd.js,v 1.14 2005/07/06 20:33:28 mmondor Exp $ */
+/* $Id: httpd.js,v 1.15 2005/07/06 23:52:23 mmondor Exp $ */
/*
* Copyright (c) 2005, Matthew Mondor
* Server identification
*/
SERVER_VERSION = 'mmondor_js_httpd/0.0.1 (NetBSD)';
-SERVER_CVSID = '$Id: httpd.js,v 1.14 2005/07/06 20:33:28 mmondor Exp $';
+SERVER_CVSID = '$Id: httpd.js,v 1.15 2005/07/06 23:52:23 mmondor Exp $';
/*
+ * For the mime types database
+ */
+
+var mimetypes_table = {};
+
+
+
+/*
* The HTTPReply object allows to cache addHeader() and addContent() requests,
* and to eventually flush the whole reply to an arbitrary FD afterwards.
*/
var close = false;
var valid = false;
var evil = false;
+ var vhost = '';
var lines;
var words;
var i;
this.http_protocol = '';
this.http_method = '';
- this.http_vhost = '';
+ this.http_vhost = undefined;
this.http_path = '';
this.http_vars_get = {};
this.http_vars_post = {};
if ((i2 = words[1].indexOf(':')) != -1)
words[1] =
words[1].substr(0, i2);
- this.http_vhost = words[1];
+ vhost = words[1];
}
} else if (words[0] == 'Cookie:') {
words = (lines[i].substr(8)).split('=');
* Also set the name of the vhost to the actual vhost name despite
* it possibly being resolved through an alias.
*/
- if (this.http_vhost == '' ||
- vhosts_table[this.http_vhost] == undefined)
- this.http_vhost = options.default_vhost;
- this.http_vhost = vhosts_table[this.http_vhost].name;
+ if (vhost == '' || vhosts_table[vhost] == undefined)
+ vhost = options.default_vhost;
+ this.http_vhost = vhosts_table[vhost];
/*
* Fill in associative array with any GET-style supplied
if (!close)
close = this.httpRespond();
- /* XXX Switch state to transfer mode as a test */
- /*
- try {
- this.transfer_src = new FD();
- this.transfer_src.open('httpd.js', FD.O_RDONLY);
- var st = this.transfer_src.fstat();
- this.transfer_dst = this;
- this.process = process_transfer;
- this.events = FD.POLLOUT;
- var res = new HTTPReply(200, 'OK', 'text/plain');
- res.addNoCacheHeaders();
- res.flush(this, st.st_size);
- } catch (x) {
- err.put(x + "\n");
- }
- */
-
return close;
}
FD.prototype.httpRespond = function()
{
+ var path, fd, st, res, ext, mimetype, i, index;
+
+ index = false;
+ for (;;) {
+ path = this.http_vhost.htdocs_root.valid_virtual(
+ this.http_path);
+ if (!path) {
+ http_error(this, 403, 'Permission Denied',
+ 'You do not have the permission to access this ' +
+ 'resource.');
+ return true;
+ }
+
+ fd = new FD();
+ try {
+ fd.open(path.real, FD.O_RDONLY);
+ st = fd.fstat();
+ } catch (x) {
+ if (index) {
+ http_error(this, 403, 'Permission Denied',
+ 'You do not have the permission to ' +
+ 'access this resource.');
+ return true;
+ } else {
+ http_error(this, 404, 'Not Found',
+ path.virtual + ' could not be found.');
+ return true;
+ }
+ }
+
+ /*
+ * Is file a directory? If so, attempt to fallback to
+ * index.html inside it.
+ */
+ if (((st.st_mode & FD.S_IFMT) & FD.S_IFDIR) != 0) {
+ fd.close();
+ this.http_path += '/index.html';
+ index = true;
+ continue;
+ }
+
+ break;
+ }
+
+ /*
+ * Only continue if file is a regular file
+ */
+ if (((st.st_mode & FD.S_IFMT) & FD.S_IFREG) == 0) {
+ fd.close();
+ http_error(this, 403, 'Permission Denied',
+ 'You do not have the permission to access this resource.');
+ 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.
+ */
+ if (this.http_modified_since != undefined &&
+ st.st_mtime <= this.http_modified_since) {
+ fd.close();
+ res = new HTTPReply(304, 'Not Modified', null);
+ res.flush(this, null);
+ return true;
+ }
+
+ /*
+ * We really need to transfer it, so switch to outbound transfer mode.
+ */
+ this.transfer_src = fd;
+ this.transfer_dst = this;
+ this.process = process_transfer;
+ this.events = FD.POLLOUT;
+
+ /*
+ * Obtain mimetype for file and flush HTTP header.
+ * Then return with close=false, to delegate operations to
+ * process_transfer().
+ */
+ mimetype = options.default_mimetype;
+ 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];
+ }
+ res = new HTTPReply(200, 'OK', mimetype);
+ res.flush(this, st.st_size);
+
+ return false;
+}
+
+/*
+FD.prototype.httpRespond = function()
+{
var res = new HTTPReply(200, 'OK', 'text/html; charset=iso-8859-1');
res.addNoCacheHeaders();
td.addContent('VHost:');
tr.addContent(td);
td = new MLTag('td', true);
- td.addContent(this.http_vhost);
+ td.addContent(this.http_vhost.name);
tr.addContent(td);
table.addContent(tr);
return true;
}
+*/
/*
* Verifies if property name ends with [], which considers it as an array of
}
/*
+ * Populate mimetypes database.
+ * XXX If this got very large, because the destination strings
+ * are rather large, it might be good to use indexes or references
+ * to them rather than provide the string for each extension.
+ */
+ for (i in mimetypes) {
+ var i2, ext;
+
+ for (i2 in mimetypes[i]) {
+ ext = mimetypes[i][i2].toLowerCase();
+ if (mimetypes_table[ext] != undefined) {
+ err.put('Conflicting mime type ' +
+ ext + ' -> ' + i + "\n");
+ continue;
+ }
+ mimetypes_table[ext] = i;
+ }
+ }
+
+ /*
* Create polling array set
*/
var set = [];