*** empty log message ***
authorMatthew Mondor <mmondor@pulsar-zone.net>
Mon, 4 Jul 2005 23:14:54 +0000 (23:14 +0000)
committerMatthew Mondor <mmondor@pulsar-zone.net>
Mon, 4 Jul 2005 23:14:54 +0000 (23:14 +0000)
tests/js-test/js/httpd/httpd.js
tests/js-test/js/httpd/options.js

index 9f3869b..b7d9185 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: httpd.js,v 1.12 2005/07/04 19:06:31 mmondor Exp $ */
+/* $Id: httpd.js,v 1.13 2005/07/04 23:14:54 mmondor Exp $ */
 
 /*
  * Copyright (c) 2005, Matthew Mondor
  * using multiple threads or processes.  Must be ran through ../../src/test,
  * compiled with ../../src/classes/js_fd.[ch] support.
  *
- * We support a maximum of 4 concurrent connections per IP address, as well
- * as a total maximum of 32 concurrent connections by default.
- * Change MAX_CONNECTIONS and MAX_CONNECTIONS_ADDR as needed.
- *
- * We also support per-connection I/O timeouts, defaulting to 60 seconds.
- * Change IO_TIMEOUT as wanted.
+ * Configuration options can be found on options.js
  *
  * XXX Possibly that with close var semantics changes or such, we could
  * support Keep-Alive for HTTP/1.1 connections.  This however is not a
  * priority at current time.
  *
+ * TODO:
  * - Implement path sanity checking (or import mmpath(3))?
  *   So that clients could invoke static files within vhosts
  * - We'll need a vhosts configuration file
- * - Move to a config file configurable options;
- * - We might also want to allow multiple listening addresses.
- *   However, if doing this, we'll not be able to rely anymore on our unique
- *   'bind' property name associated to the listening socket.
- *   We probably instead can add a property to the FD to distinguish bound
- *   FDs from client ones.
  * - We probably want to support proxy/cache queries that request if a
  *   document was modified, which would be ideal for static files, but
  *   would always return modified status for dynamic data.
+ *   A query which has a field such as:
+ *        ...
+ *        If-Modified-Since: Sat, 02 Oct 2004 10:52:44 GMT
+ *   Could receive a response such as:
+ *        HTTP/1.1 304 Not Modified
+ *        Date: Mon, 04 Jul 2005 22:45:37 GMT
+ *        Server: Apache/1.3.33 (Unix) mod_ssl/2.8.21 OpenSSL/0.9.7d
+ *        Connection: close
+ * - See what to do for HEAD and PUT
+ * - Think about how I'll handle invokation of scripts.  I'll most probably
+ *   need to eval() them initially and cache them once evaluated, until
+ *   modified, and to invoke them prior to pre-evaluation multiple times
+ *   as needed.  Possibly that these scripts could simply define an object
+ *   with an entry point or such.  At worse I'll need special a C support
+ *   object to implement this, which would at necessary use another context,
+ *   possibly persistent.
  */
 
 
@@ -85,7 +91,7 @@ LISTEN_ADDRESS                        = '0.0.0.0';
 const LISTEN_PORT              = 8080;
 
 SERVER_VERSION                 = 'mmondor_js_httpd/0.0.1 (NetBSD)';
-SERVER_CVSID                   = '$Id: httpd.js,v 1.12 2005/07/04 19:06:31 mmondor Exp $';
+SERVER_CVSID                   = '$Id: httpd.js,v 1.13 2005/07/04 23:14:54 mmondor Exp $';
 
 
 
@@ -396,6 +402,8 @@ FD.prototype.updateTimeout = function(time)
  */
 FD.prototype.init = function(time, idx)
 {
+       /* Not a bound socket */
+       this.bound = false;
        /* Initial state */
        this.transfer_state = STATE_TRANSFER_READ;
        this.transfer_src = this.transfer_dst = null;
@@ -450,6 +458,7 @@ FD.prototype.parseRequest = function()
        this.http_vars_cookies = {};
        this.http_agent = '';
        this.http_content_length = -1;
+       this.http_modified_since = undefined;
 
        /* Split request lines */
        lines = this.request_data.split("\r\n");
@@ -496,6 +505,9 @@ FD.prototype.parseRequest = function()
                                        evil = true;
                        } else if (words[0] == 'Content-Length:')
                                this.http_content_length = words[1].valueOf();
+                       else if (words[0] == 'If-Modified-Since:')
+                               this.http_modified_since = Math.round(
+                                   Date.parse(lines[i].substr(19)) / 60000);
                }
        }
 
@@ -730,6 +742,16 @@ FD.prototype.httpRespond = function()
        tr.addContent(td);
        table.addContent(tr);
 
+       tr = new MLTag('tr', true);
+       td = new MLTag('td', true);
+       td.addAttr('align', 'right');
+       td.addContent('Mod-Since:');
+       tr.addContent(td);
+       td = new MLTag('td', true);
+       td.addContent(this.http_modified_since);
+       tr.addContent(td);
+       table.addContent(tr);
+
        body.addContent(table);
 
        p = new MLTag('p', true);
@@ -817,7 +839,6 @@ function counters_dec(fd)
  * Main program
  */
 function main() {
-       var sock;
        var i;
 
        /*
@@ -828,39 +849,54 @@ function main() {
        /*
         * Initialize server
         */
-       try {
-               sock = new FD();
-               sock.socket(FD.AF_INET, FD.SOCK_STREAM, 0);
-               sock.bind(LISTEN_ADDRESS, LISTEN_PORT);
-               /*
-                * XXX Must either fix netbsd kernel bug and/or js_fd.c
-                * before using these, since at least one of the following
-                * calls causes a system panic!
-                */
-               /*
-               sock.setsockopt(FD.SO_REUSEADDR, 1);
-               sock.setsockopt(FD.SO_LINGER, -1);
-               sock.setsockopt(FD.SO_KEEPALIVE, 1);
-               */
-               sock.setsockopt(FD.TCP_NODELAY, 1);
-               sock.listen(options.max_connections);
-       } catch (x) {
-               err.put(x + "\n");
-               exit();
-       }
+       for (i in listen) {
+               var fd;
 
-       /*
-        * Add listening socket to polling set as a property to easily
-        * distinguish it from client FDs
-        */
-       sock.events = FD.POLLIN;
-       set['bind'] = sock;
+               try {
+                       fd = new FD();
+                       fd.socket(FD.AF_INET, FD.SOCK_STREAM, 0);
+                       /*
+                        * XXX How comes bind(2) fails if I don't first print
+                        * out the address!?  An odd bug it seems.
+                        * Like if listen[i] first needs to be
+                        * referenced/instanciated...
+                        * I then get error "Address family not supported by
+                        * protocol family".
+                        */
+                       err.put(uneval(listen[i]) + "\n");
+                       fd.bind(listen[i].address, listen[i].port);
+                       /*
+                        * XXX Must either fix netbsd kernel bug
+                        * and/or js_fd.c before using these, since
+                        * at least one of the following calls causes
+                        * a system panic!
+                        */
+                       /*
+                          sock.setsockopt(FD.SO_REUSEADDR, 1);
+                          sock.setsockopt(FD.SO_LINGER, -1);
+                          sock.setsockopt(FD.SO_KEEPALIVE, 1);
+                        */
+                       fd.setsockopt(FD.TCP_NODELAY, 1);
+                       fd.listen(options.max_connections);
+                       /*
+                        * Mark socket as bound one and add it to
+                        * polling set
+                        */
+                       fd.events = FD.POLLIN;
+                       fd.bound = true;
+                       set.push(fd);
+               } catch (x) {
+                       err.put(x + "\n");
+               }
+       }
+       if (set.length == 0)
+               exit();
 
        /*
         * Used for unique index associated to each FD for efficient removal
         * from polling set when closing connection
         */
-       var count = 0;
+       var count = listen.length + 1;
 
        /*
         * Main loop
@@ -875,7 +911,7 @@ function main() {
                for (i in set) {
                        var fd;
 
-                       if ((fd = set[i]) == undefined || i == 'bind')
+                       if ((fd = set[i]) == undefined || fd.bound == true)
                                continue;
                        if (fd.expires <= cur) {
                                /*
@@ -907,12 +943,9 @@ function main() {
                /*
                 * Verify if a timeout occurred.  Because our poll
                 * implementation returns an array, its timeout event can be
-                * checked against by verifying if the set is empty.  However,
-                * associative-array/object-attributes are not accounted
-                * properly with 'length' in JS, so also test fo the case of
-                * the 'bind' entry.
+                * checked against by verifying if the set is empty.
                 */
-               if (e.length == 0 && e['bind'] == undefined) {
+               if (e.length == 0) {
                        /* Timeout */
                        continue;
                }
@@ -927,43 +960,56 @@ function main() {
                cur = (Date.parse(new Date) / 1000);
 
                /*
-                * Process occurred events.  First handle new connections,
-                * if any.
+                * Run through set of descriptors with pending events
                 */
-               if (e['bind'] != undefined) {
+               for (i in e) {
+                       if (e[i] == undefined)
+                               continue;
+
                        /*
-                        * New connection, accept it
+                        * If descriptor is a bound one, attempt to accept
+                        * the new client connection
                         */
-                       var fd = sock.accept();
+                       if (e[i].bound == true &&
+                           (e[i].revents & FD.POLLIN) != 0) {
+                               var fd;
 
-                       if (!counters_inc(fd)) {
-                               http_error(fd, 403.9, 'Too Many Connections',
+                               try {
+                                       fd = e[i].accept();
+                                       if (!counters_inc(fd)) {
+                                               http_error(fd, 403.9,
+                                                   'Too Many Connections',
                                    'Your browser has exceeded its maximum ' +
                                    'allowed number of concurrent ' +
                                    'connections.');
-                               fd.close();
-                               delete fd;
-                       } else {
-                               /*
-                                * Setup client's initial state
-                                */
-                               fd.init(cur, ++count);
-                               /*
-                                * Add descriptor to polling set
-                                */
-                               set[count] = fd;
-                       }
-               }
-
-               /*
-                * Run through set of descriptors with pending events
-                */
-               for (i in e) {
-                       if (e[i] == undefined || i == 'bind')
+                                               fd.close();
+                                               delete fd;
+                                       } else {
+                                               /*
+                                                * Setup client's initial
+                                                * state and add FD to polling
+                                                * set
+                                                */
+                                               fd.init(cur, ++count);
+                                               set[count] = fd;
+                                       }
+                               } catch (x) {
+                                       err.put(x + "\n");
+                               }
                                continue;
+                       }
 
+                       /*
+                        * Not a new connection event
+                        */
                        var close = false;
 
+                       /*
+                        * Close connection on error conditions,
+                        * Call the FD's process function on interesting
+                        * events, which will tell when we should drop the
+                        * client.
+                        */
                        if ((e[i].revents & (FD.POLLHUP | FD.POLLERR)) != 0)
                                close = true;
                        else if ((e[i].revents & (FD.POLLIN | FD.POLLOUT))
@@ -980,7 +1026,6 @@ function main() {
        }
 
        /* NOTREACHED */
-       sock.close();
        err.close();
 }
 
index 7c9e363..507c880 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: options.js,v 1.1 2005/07/04 19:06:31 mmondor Exp $ */
+/* $Id: options.js,v 1.2 2005/07/04 23:14:54 mmondor Exp $ */
 
 var options = {
        /* Maximum number of concurrent clients that we should serve */
@@ -15,8 +15,14 @@ var options = {
 
 /* Address:port combinations to listen to */
 var listen = [
-       "127.0.0.1:8080",
-       "192.168.1.11:8080"
+       {
+               address:                "127.0.0.1",
+               port:                   8080
+       },
+       {
+               address:                "192.168.1.11",
+               port:                   8080
+       }
 ];
 
 var vhosts = [