changeset 1388:eb35203124e4

Implemented the file dpi based on select() (removed its pthreads dependency) It should be faster and use less resources.
author Jorge Arellano Cid <jcid@dillo.org>
date Sun, 01 Nov 2009 16:31:59 -0300
parents 16cf380cd04c
children c1c822f70582
files config.h.in dpi/Makefile.am dpi/bookmarks.c dpi/cookies.c dpi/file.c dpid/main.c dpip/dpip.c dpip/dpip.h
diffstat 8 files changed, 510 insertions(+), 396 deletions(-) [+]
line wrap: on
line diff
--- a/config.h.in	Sun Nov 01 16:31:59 2009 -0300
+++ b/config.h.in	Sun Nov 01 16:31:59 2009 -0300
@@ -81,6 +81,9 @@
 /* Define to the home page for this package. */
 #undef PACKAGE_URL
 
+/* Define to the home page for this package. */
+#undef PACKAGE_URL
+
 /* Define to the version of this package. */
 #undef PACKAGE_VERSION
 
--- a/dpi/Makefile.am	Sun Nov 01 16:31:59 2009 -0300
+++ b/dpi/Makefile.am	Sun Nov 01 16:31:59 2009 -0300
@@ -20,11 +20,10 @@
 ftp_filter_dpi_LDADD = ../dpip/libDpip.a ../dlib/libDlib.a
 https_filter_dpi_LDADD = @LIBSSL_LIBS@ ../dpip/libDpip.a ../dlib/libDlib.a
 hello_filter_dpi_LDADD = ../dpip/libDpip.a ../dlib/libDlib.a
-file_dpi_LDADD = @LIBPTHREAD_LIBS@ ../dpip/libDpip.a ../dlib/libDlib.a
+file_dpi_LDADD = ../dpip/libDpip.a ../dlib/libDlib.a
 cookies_dpi_LDADD = ../dpip/libDpip.a ../dlib/libDlib.a
 datauri_filter_dpi_LDADD = ../dpip/libDpip.a ../dlib/libDlib.a
 
-file_dpi_LDFLAGS = @LIBPTHREAD_LDFLAGS@
 downloads_dpi_CXXFLAGS = @LIBFLTK_CXXFLAGS@
 
 bookmarks_dpi_SOURCES = bookmarks.c dpiutil.c dpiutil.h
--- a/dpi/bookmarks.c	Sun Nov 01 16:31:59 2009 -0300
+++ b/dpi/bookmarks.c	Sun Nov 01 16:31:59 2009 -0300
@@ -1727,12 +1727,9 @@
 
       while (1) {
          code = 1;
-         if ((tok = a_Dpip_dsh_read_token(sh)) != NULL) {
+         if ((tok = a_Dpip_dsh_read_token(sh, 1)) != NULL) {
             /* Let's see what we fished... */
             code = Bmsrv_parse_token(sh, tok);
-         } else if (sh->status == DPIP_EAGAIN) {
-            /* may reach here when the tag size is larger than kernel buffer */
-            continue;
          }
          dFree(tok);
 
--- a/dpi/cookies.c	Sun Nov 01 16:31:59 2009 -0300
+++ b/dpi/cookies.c	Sun Nov 01 16:31:59 2009 -0300
@@ -1482,14 +1482,11 @@
 
       while (1) {
          code = 1;
-         if ((buf = a_Dpip_dsh_read_token(sh)) != NULL) {
+         if ((buf = a_Dpip_dsh_read_token(sh, 1)) != NULL) {
             /* Let's see what we fished... */
             _MSG(" buf = {%s}\n", buf);
             code = srv_parse_tok(sh, client, buf);
             dFree(buf);
-         } else if (sh->status == DPIP_EAGAIN) {
-            /* may reach here when the tag size is larger than kernel buffer */
-            continue;
          }
 
          _MSG(" code = %d %s\n", code, code == 1 ? "EXIT" : "BREAK");
--- a/dpi/file.c	Sun Nov 01 16:31:59 2009 -0300
+++ b/dpi/file.c	Sun Nov 01 16:31:59 2009 -0300
@@ -15,14 +15,13 @@
  * With new HTML layout.
  */
 
-#include <pthread.h>
-
 #include <ctype.h>           /* for tolower */
 #include <errno.h>           /* for errno */
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <sys/select.h>
 #include <sys/socket.h>
 #include <sys/stat.h>
 #include <sys/types.h>
@@ -32,6 +31,7 @@
 #include <fcntl.h>
 #include <time.h>
 #include <signal.h>
+#include <netinet/in.h>
 
 #include "../dpip/dpip.h"
 #include "dpiutil.h"
@@ -42,11 +42,32 @@
  */
 #define _MSG(...)
 #define MSG(...)  printf("[file dpi]: " __VA_ARGS__)
+#define _MSG_RAW(...)
+#define MSG_RAW(...)  printf(__VA_ARGS__)
 
 
 #define MAXNAMESIZE 30
 #define HIDE_DOTFILES TRUE
 
+/*
+ * Communication flags
+ */
+#define FILE_AUTH_OK     1     /* Authentication done */
+#define FILE_READ        2     /* Waiting data */
+#define FILE_WRITE       4     /* Sending data */
+#define FILE_DONE        8     /* Operation done */
+#define FILE_ERR        16     /* Operation error */
+
+
+typedef enum {
+   st_start,
+   st_dpip,
+   st_http,
+   st_content,
+   st_done,
+   st_err
+} FileState;
+
 typedef struct {
    char *full_path;
    const char *filename;
@@ -62,34 +83,32 @@
 
 typedef struct {
    Dsh *sh;
-   int status;
+   char *orig_url;
+   char *filename;
+   int file_fd;
+   off_t file_sz;
+   DilloDir *d_dir;
+   FileState state;
+   int err_code;
+   int flags;
    int old_style;
-   pthread_t thrID;
-   int done;
 } ClientInfo;
 
 /*
  * Forward references
  */
 static const char *File_content_type(const char *filename);
-static int File_get_file(ClientInfo *Client,
-                         const char *filename,
-                         struct stat *sb,
-                         const char *orig_url);
-static int File_get_dir(ClientInfo *Client,
-                        const char *DirName,
-                        const char *orig_url);
 
 /*
  * Global variables
  */
-static volatile int DPIBYE = 0;
-static volatile int ThreadRunning = 0;
+static int DPIBYE = 0;
 static int OLD_STYLE = 0;
 /* A list for the clients we are serving */
 static Dlist *Clients;
-/* a mutex for operations on clients */
-static pthread_mutex_t ClMut;
+/* Set of filedescriptors we're working on */
+fd_set read_set, write_set;
+
 
 /*
  * Close a file descriptor, but handling EINTR
@@ -250,6 +269,8 @@
    int i;
    FileInfo *finfo;
 
+   dReturn_if (Ddir == NULL);
+
    for (i = 0; i < dList_length(Ddir->flist); ++i) {
       finfo = dList_nth_data(Ddir->flist, i);
       dFree(finfo->full_path);
@@ -264,7 +285,7 @@
 /*
  * Output the string for parent directory
  */
-static void File_print_parent_dir(ClientInfo *Client, const char *dirname)
+static void File_print_parent_dir(ClientInfo *client, const char *dirname)
 {
    if (strcmp(dirname, "/") != 0) {        /* Not the root dir */
       char *p, *parent, *HUparent, *Uparent;
@@ -278,7 +299,7 @@
 
       Uparent = Escape_uri_str(parent, NULL);
       HUparent = Escape_html_str(Uparent);
-      a_Dpip_dsh_printf(Client->sh, 0,
+      a_Dpip_dsh_printf(client->sh, 0,
          "<a href='file:%s'>Parent directory</a>", HUparent);
       dFree(HUparent);
       dFree(Uparent);
@@ -289,20 +310,20 @@
 /*
  * Given a timestamp, output an HTML-formatted date string.
  */
-static void File_print_mtime(ClientInfo *Client, time_t mtime)
+static void File_print_mtime(ClientInfo *client, time_t mtime)
 {
    char *ds = ctime(&mtime);
 
    /* Month, day and {hour or year} */
-   if (Client->old_style) {
-      a_Dpip_dsh_printf(Client->sh, 0, " %.3s %.2s", ds + 4, ds + 8);
+   if (client->old_style) {
+      a_Dpip_dsh_printf(client->sh, 0, " %.3s %.2s", ds + 4, ds + 8);
       if (time(NULL) - mtime > 15811200) {
-         a_Dpip_dsh_printf(Client->sh, 0, "  %.4s", ds + 20);
+         a_Dpip_dsh_printf(client->sh, 0, "  %.4s", ds + 20);
       } else {
-         a_Dpip_dsh_printf(Client->sh, 0, " %.5s", ds + 11);
+         a_Dpip_dsh_printf(client->sh, 0, " %.5s", ds + 11);
       }
    } else {
-      a_Dpip_dsh_printf(Client->sh, 0,
+      a_Dpip_dsh_printf(client->sh, 0,
          "<td>%.3s&nbsp;%.2s&nbsp;%.5s", ds + 4, ds + 8,
          /* (more than 6 months old) ? year : hour; */
          (time(NULL) - mtime > 15811200) ? ds + 20 : ds + 11);
@@ -312,7 +333,7 @@
 /*
  * Return a HTML-line from file info.
  */
-static void File_info2html(ClientInfo *Client, FileInfo *finfo, int n)
+static void File_info2html(ClientInfo *client, FileInfo *finfo, int n)
 {
    int size;
    char *sizeunits;
@@ -355,10 +376,10 @@
    HUref = Escape_html_str(Uref);
    Hname = Escape_html_str(name);
 
-   if (Client->old_style) {
+   if (client->old_style) {
       char *dots = ".. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. ..";
       int ndots = MAXNAMESIZE - strlen(name);
-      a_Dpip_dsh_printf(Client->sh, 0,
+      a_Dpip_dsh_printf(client->sh, 0,
          "%s<a href='%s'>%s</a>"
          " %s"
          " %-11s%4d %-5s",
@@ -367,15 +388,15 @@
          filecont, size, sizeunits);
 
    } else {
-      a_Dpip_dsh_printf(Client->sh, 0,
+      a_Dpip_dsh_printf(client->sh, 0,
          "<tr align=center %s><td>%s<td align=left><a href='%s'>%s</a>"
          "<td>%s<td>%d&nbsp;%s",
          (n & 1) ? "bgcolor=#dcdcdc" : "",
          S_ISDIR (finfo->mode) ? ">" : " ", HUref, Hname,
          filecont, size, sizeunits);
    }
-   File_print_mtime(Client, finfo->mtime);
-   a_Dpip_dsh_write_str(Client->sh, 0, "\n");
+   File_print_mtime(client, finfo->mtime);
+   a_Dpip_dsh_write_str(client->sh, 0, "\n");
 
    dFree(Hname);
    dFree(HUref);
@@ -383,81 +404,92 @@
 }
 
 /*
- * Read a local directory and translate it to html.
+ * Send the HTML directory page in HTTP.
  */
-static void File_transfer_dir(ClientInfo *Client,
-                              DilloDir *Ddir, const char *orig_url)
+static void File_send_dir(ClientInfo *client)
 {
    int n;
    char *d_cmd, *Hdirname, *Udirname, *HUdirname;
-
-   /* Send DPI header */
-   d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", orig_url);
-   a_Dpip_dsh_write_str(Client->sh, 1, d_cmd);
-   dFree(d_cmd);
-
-   /* Send page title */
-   Udirname = Escape_uri_str(Ddir->dirname, NULL);
-   HUdirname = Escape_html_str(Udirname);
-   Hdirname = Escape_html_str(Ddir->dirname);
-
-   a_Dpip_dsh_printf(Client->sh, 0,
-      "HTTP/1.1 200 OK\r\n"
-      "Content-Type: text/html\r\n"
-      "\r\n"
-      "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>\n"
-      "<HTML>\n<HEAD>\n <BASE href='file:%s'>\n"
-      " <TITLE>file:%s</TITLE>\n</HEAD>\n"
-      "<BODY><H1>Directory listing of %s</H1>\n",
-      HUdirname, Hdirname, Hdirname);
-   dFree(Hdirname);
-   dFree(HUdirname);
-   dFree(Udirname);
-
-   if (Client->old_style) {
-      a_Dpip_dsh_write_str(Client->sh, 0, "<pre>\n");
-   }
-
-   /* Output the parent directory */
-   File_print_parent_dir(Client, Ddir->dirname);
+   DilloDir *Ddir = client->d_dir;
 
-   /* HTML style toggle */
-   a_Dpip_dsh_write_str(Client->sh, 0,
-      "&nbsp;&nbsp;<a href='dpi:/file/toggle'>%</a>\n");
+   if (client->state == st_start) {
+      /* Send DPI command */
+      d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", 
+                               client->orig_url);
+      a_Dpip_dsh_write_str(client->sh, 1, d_cmd);
+      dFree(d_cmd);
+      client->state = st_dpip;
 
-   if (dList_length(Ddir->flist)) {
-      if (Client->old_style) {
-         a_Dpip_dsh_write_str(Client->sh, 0, "\n\n");
+   } else if (client->state == st_dpip) {
+      /* send HTTP header and HTML top part */
+
+      /* Send page title */
+      Udirname = Escape_uri_str(Ddir->dirname, NULL);
+      HUdirname = Escape_html_str(Udirname);
+      Hdirname = Escape_html_str(Ddir->dirname);
+   
+      a_Dpip_dsh_printf(client->sh, 0,
+         "HTTP/1.1 200 OK\r\n"
+         "Content-Type: text/html\r\n"
+         "\r\n"
+         "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>\n"
+         "<HTML>\n<HEAD>\n <BASE href='file:%s'>\n"
+         " <TITLE>file:%s</TITLE>\n</HEAD>\n"
+         "<BODY><H1>Directory listing of %s</H1>\n",
+         HUdirname, Hdirname, Hdirname);
+      dFree(Hdirname);
+      dFree(HUdirname);
+      dFree(Udirname);
+   
+      if (client->old_style) {
+         a_Dpip_dsh_write_str(client->sh, 0, "<pre>\n");
+      }
+   
+      /* Output the parent directory */
+      File_print_parent_dir(client, Ddir->dirname);
+   
+      /* HTML style toggle */
+      a_Dpip_dsh_write_str(client->sh, 0,
+         "&nbsp;&nbsp;<a href='dpi:/file/toggle'>%</a>\n");
+   
+      if (dList_length(Ddir->flist)) {
+         if (client->old_style) {
+            a_Dpip_dsh_write_str(client->sh, 0, "\n\n");
+         } else {
+            a_Dpip_dsh_write_str(client->sh, 0,
+               "<br><br>\n"
+               "<table border=0 cellpadding=1 cellspacing=0"
+               " bgcolor=#E0E0E0 width=100%>\n"
+               "<tr align=center>\n"
+               "<td>\n"
+               "<td width=60%><b>Filename</b>"
+               "<td><b>Type</b>"
+               "<td><b>Size</b>"
+               "<td><b>Modified&nbsp;at</b>\n");
+         }
       } else {
-         a_Dpip_dsh_write_str(Client->sh, 0,
-            "<br><br>\n"
-            "<table border=0 cellpadding=1 cellspacing=0"
-            " bgcolor=#E0E0E0 width=100%>\n"
-            "<tr align=center>\n"
-            "<td>\n"
-            "<td width=60%><b>Filename</b>"
-            "<td><b>Type</b>"
-            "<td><b>Size</b>"
-            "<td><b>Modified&nbsp;at</b>\n");
+         a_Dpip_dsh_write_str(client->sh, 0, "<br><br>Directory is empty...");
       }
-   } else {
-      a_Dpip_dsh_write_str(Client->sh, 0, "<br><br>Directory is empty...");
+      client->state = st_http;
+
+   } else if (client->state == st_http) {
+      /* send directories as HTML contents */
+      for (n = 0; n < dList_length(Ddir->flist); ++n) {
+         File_info2html(client, dList_nth_data(Ddir->flist,n), n+1);
+      }
+   
+      if (dList_length(Ddir->flist)) {
+         if (client->old_style) {
+            a_Dpip_dsh_write_str(client->sh, 0, "</pre>\n");
+         } else {
+            a_Dpip_dsh_write_str(client->sh, 0, "</table>\n");
+         }
+      }
+   
+      a_Dpip_dsh_write_str(client->sh, 1, "</BODY></HTML>\n");
+      client->state = st_content;
+      client->flags |= FILE_DONE;
    }
-
-   /* Output entries */
-   for (n = 0; n < dList_length(Ddir->flist); ++n) {
-      File_info2html(Client, dList_nth_data(Ddir->flist,n), n+1);
-   }
-
-   if (dList_length(Ddir->flist)) {
-      if (Client->old_style) {
-         a_Dpip_dsh_write_str(Client->sh, 0, "</pre>\n");
-      } else {
-         a_Dpip_dsh_write_str(Client->sh, 0, "</table>\n");
-      }
-   }
-
-   a_Dpip_dsh_write_str(Client->sh, 0, "</BODY></HTML>\n");
 }
 
 /*
@@ -522,70 +554,60 @@
 /*
  * Send an error page
  */
-static void File_send_error_page(ClientInfo *Client, int res,
-                                 const char *orig_url)
+static void File_prepare_send_error_page(ClientInfo *client, int res,
+                                         const char *orig_url)
+{
+   client->state = st_err;
+   client->err_code = res;
+   client->orig_url = dStrdup(orig_url);
+   client->flags &= ~FILE_READ;
+   client->flags |= FILE_WRITE;
+}
+
+/*
+ * Send an error page
+ */
+static void File_send_error_page(ClientInfo *client)
 {
    const char *status;
-   const char *body;
    char *d_cmd;
+   Dstr *body = dStr_sized_new(128);
 
-   switch (res) {
-   case EACCES:
+   if (client->err_code == EACCES) {
       status = "403 Forbidden";
-      break;
-   case ENOENT:
+   } else if (client->err_code == ENOENT) {
       status = "404 Not Found";
-      break;
-   default:
+   } else {
       /* good enough */
       status = "500 Internal Server Error";
    }
-
-   body = status; /* it's simple */
+   dStr_append(body, status);
+   dStr_append(body, "\n");
+   dStr_append(body, dStrerror(client->err_code));
 
    /* Send DPI command */
-   d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", orig_url);
-   a_Dpip_dsh_write_str(Client->sh, 1, d_cmd);
+   d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page",
+                            client->orig_url);
+   a_Dpip_dsh_write_str(client->sh, 0, d_cmd);
    dFree(d_cmd);
 
-   a_Dpip_dsh_printf(Client->sh, 0, 
+   a_Dpip_dsh_printf(client->sh, 0,
                      "HTTP/1.1 %s\r\n"
                      "Content-Type: text/plain\r\n"
-                     "Content-Length: %ld\r\n"
+                     "Content-Length: %d\r\n"
                      "\r\n"
                      "%s",
-                     status, strlen(body), body);
+                     status, body->len, body->str);
+   dStr_free(body, TRUE);
+
+   client->flags |= FILE_DONE;
 }
 
 /*
- * Try to stat the file and determine if it's readable.
+ * Scan the directory, sort and prepare to send it enclosed in HTTP.
  */
-static void File_get(ClientInfo *Client, const char *filename,
-                     const char *orig_url)
-{
-   int res;
-   struct stat sb;
-
-   if (stat(filename, &sb) != 0) {
-      /* stat failed, prepare a file-not-found error. */
-      res = ENOENT;
-   } else if (S_ISDIR(sb.st_mode)) {
-      /* set up for reading directory */
-      res = File_get_dir(Client, filename, orig_url);
-   } else {
-      /* set up for reading a file */
-      res = File_get_file(Client, filename, &sb, orig_url);
-   }
-   if (res != 0) {
-      File_send_error_page(Client, res, orig_url);
-   }
-}
-
-/*
- *
- */
-static int File_get_dir(ClientInfo *Client,
-                         const char *DirName, const char *orig_url)
+static int File_prepare_send_dir(ClientInfo *client,
+                                 const char *DirName, const char *orig_url)
 {
    Dstr *ds_dirname;
    DilloDir *Ddir;
@@ -599,89 +621,152 @@
    Ddir = File_dillodir_new(ds_dirname->str);
    dStr_free(ds_dirname, TRUE);
    if (Ddir) {
-      File_transfer_dir(Client, Ddir, orig_url);
-      File_dillodir_free(Ddir);
+      /* looks ok, set things accordingly */
+      client->orig_url = dStrdup(orig_url);
+      client->d_dir = Ddir;
+      client->state = st_start;
+      client->flags &= ~FILE_READ;
+      client->flags |= FILE_WRITE;
       return 0;
    } else
       return EACCES;
 }
 
 /*
- * Send HTTP headers and then send the file itself.
+ * Prepare to send HTTP headers and then the file itself.
  */
-static int File_get_file(ClientInfo *Client,
-                         const char *filename,
-                         struct stat *sb,
-                         const char *orig_url)
+static int File_prepare_send_file(ClientInfo *client,
+                                  const char *filename,
+                                  const char *orig_url)
+{
+   int fd, res = -1;
+   struct stat sb;
+
+   if (stat(filename, &sb) != 0) {
+      /* prepare a file-not-found error */
+      res = ENOENT;
+   } else if ((fd = open(filename, O_RDONLY | O_NONBLOCK)) < 0) {
+      /* prepare an error message */
+      res = errno;
+   } else {
+      /* looks ok, set things accordingly */
+      client->file_fd = fd;
+      client->file_sz = sb.st_size;
+      client->d_dir = NULL;
+      client->state = st_start;
+      client->filename = dStrdup(filename);
+      client->orig_url = dStrdup(orig_url);
+      client->flags &= ~FILE_READ;
+      client->flags |= FILE_WRITE;
+      res = 0;
+   }
+   return res;
+}
+
+/*
+ * Try to stat the file and determine if it's readable.
+ */
+static void File_get(ClientInfo *client, const char *filename,
+                     const char *orig_url)
+{
+   int res;
+   struct stat sb;
+
+   if (stat(filename, &sb) != 0) {
+      /* stat failed, prepare a file-not-found error. */
+      res = ENOENT;
+   } else if (S_ISDIR(sb.st_mode)) {
+      /* set up for reading directory */
+      res = File_prepare_send_dir(client, filename, orig_url);
+   } else {
+      /* set up for reading a file */
+      res = File_prepare_send_file(client, filename, orig_url);
+   }
+   if (res != 0) {
+      File_prepare_send_error_page(client, res, orig_url);
+   }
+}
+
+/*
+ * Send HTTP headers and then the file itself.
+ */
+static int File_send_file(ClientInfo *client)
 {
 #define LBUF 16*1024
 
    const char *ct;
    const char *unknown_type = "application/octet-stream";
    char buf[LBUF], *d_cmd, *name;
-   int fd, st, namelen;
+   int st, namelen;
    bool_t gzipped = FALSE;
 
-   if ((fd = open(filename, O_RDONLY | O_NONBLOCK)) < 0)
-      return errno;
-
-   /* Send DPI command */
-   d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", orig_url);
-   a_Dpip_dsh_write_str(Client->sh, 1, d_cmd);
-   dFree(d_cmd);
+   if (client->state == st_start) {
+      /* Send DPI command */
+      d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page",
+                               client->orig_url);
+      a_Dpip_dsh_write_str(client->sh, 1, d_cmd);
+      dFree(d_cmd);
+      client->state = st_dpip;
 
-   /* Check for gzipped file */
-   namelen = strlen(filename);
-   if (namelen > 3 && !dStrcasecmp(filename + namelen - 3, ".gz")) {
-      gzipped = TRUE;
-      namelen -= 3;
-   }
+   } else if (client->state == st_dpip) {
+      /* send HTTP header */
 
-   /* Content-Type info is based on filename extension (with ".gz" removed).
-    * If there's no known extension, perform data sniffing.
-    * If this doesn't lead to a conclusion, use "application/octet-stream".
-    */
-   name = dStrndup(filename, namelen);
-   if (!(ct = File_content_type(name)))
-      ct = unknown_type;
-   dFree(name);
+      /* Check for gzipped file */
+      namelen = strlen(client->filename);
+      if (namelen > 3 && !dStrcasecmp(client->filename + namelen - 3, ".gz")) {
+         gzipped = TRUE;
+         namelen -= 3;
+      }
+      /* Content-Type info is based on filename extension (with ".gz" removed).
+       * If there's no known extension, perform data sniffing.
+       * If this doesn't lead to a conclusion, use "application/octet-stream".
+       */
+      name = dStrndup(client->filename, namelen);
+      if (!(ct = File_content_type(name)))
+         ct = unknown_type;
+      dFree(name);
+   
+      /* Send HTTP headers */
+      a_Dpip_dsh_write_str(client->sh, 0, "HTTP/1.1 200 OK\r\n");
+      if (gzipped) {
+         a_Dpip_dsh_write_str(client->sh, 0, "Content-Encoding: gzip\r\n");
+      }
+      if (!gzipped || strcmp(ct, unknown_type)) {
+         a_Dpip_dsh_printf(client->sh, 0, "Content-Type: %s\r\n", ct);
+      } else {
+         /* If we don't know type for gzipped data, let dillo figure it out. */
+      }
+      a_Dpip_dsh_printf(client->sh, 1,
+                        "Content-Length: %ld\r\n"
+                        "\r\n",
+                        client->file_sz);
+      client->state = st_http;
 
-   /* Send HTTP headers */
-   a_Dpip_dsh_write_str(Client->sh, 0, "HTTP/1.1 200 OK\r\n");
-   if (gzipped) {
-      a_Dpip_dsh_write_str(Client->sh, 0, "Content-Encoding: gzip\r\n");
+   } else if (client->state == st_http) {
+      /* Send body -- raw file contents */
+      do {
+         if ((st = read(client->file_fd, buf, LBUF)) > 0) {
+            if (a_Dpip_dsh_write(client->sh, 0, buf, (size_t)st) != 0)
+               break;
+         } else if (st < 0) {
+            perror("[read]");
+            if (errno == EINTR || errno == EAGAIN)
+               continue;
+         }
+      } while (st > 0);
+   
+      /* TODO: It may be better to send an error report to dillo instead of
+       *       calling exit() */
+      if (st == -1) {
+         MSG("ERROR while reading from file \"%s\", error was \"%s\"\n",
+             client->filename, dStrerror(errno));
+         exit(1);
+      } else if (st == 0) {
+         client->state = st_content;
+         File_close(client->file_fd);
+         client->flags |= FILE_DONE;
+      }
    }
-   if (!gzipped || strcmp(ct, unknown_type)) {
-      a_Dpip_dsh_printf(Client->sh, 0, "Content-Type: %s\r\n", ct);
-   } else {
-      /* If we don't know type for gzipped data, let dillo figure it out. */
-   }
-   a_Dpip_dsh_printf(Client->sh, 0,
-                     "Content-Length: %ld\r\n"
-                     "\r\n",
-                     sb->st_size);
-
-   /* Send body -- raw file contents */
-   do {
-      if ((st = read(fd, buf, LBUF)) > 0) {
-         if (a_Dpip_dsh_write(Client->sh, 0, buf, (size_t)st) != 0)
-            break;
-      } else if (st < 0) {
-         perror("[read]");
-         if (errno == EINTR || errno == EAGAIN)
-            continue;
-      }
-   } while (st > 0);
-
-   /* TODO: It may be better to send an error report to dillo instead of
-    *       calling exit() */
-   if (st == -1) {
-      MSG("ERROR while reading from file \"%s\", error was \"%s\"\n",
-          filename, dStrerror(errno));
-      exit(1);
-   }
-
-   File_close(fd);
 
    return 0;
 }
@@ -714,6 +799,8 @@
 {
    char *str = (char *) orig, *basename = NULL, *ret = NULL, *p;
 
+   dReturn_val_if (orig == NULL, ret);
+
    /* Make sure the string starts with "file:/" */
    if (strncmp(str, "file:/", 5) != 0)
       return ret;
@@ -757,13 +844,13 @@
 /*
  * Set the style flag and ask for a reload, so it shows immediately.
  */
-static void File_toggle_html_style(ClientInfo *Client)
+static void File_toggle_html_style(ClientInfo *client)
 {
    char *d_cmd;
 
    OLD_STYLE = !OLD_STYLE;
    d_cmd = a_Dpip_build_cmd("cmd=%s", "reload_request");
-   a_Dpip_dsh_write_str(Client->sh, 1, d_cmd);
+   a_Dpip_dsh_write_str(client->sh, 1, d_cmd);
    dFree(d_cmd);
 }
 
@@ -783,182 +870,180 @@
  */
 static ClientInfo *File_add_client(int sock_fd)
 {
-   ClientInfo *NewClient;
+   ClientInfo *new_client;
 
-   NewClient = dNew(ClientInfo, 1);
-   NewClient->sh = a_Dpip_dsh_new(sock_fd, sock_fd, 8*1024);
-   NewClient->status = 0;
-   NewClient->done = 0;
-   NewClient->old_style = OLD_STYLE;
-  pthread_mutex_lock(&ClMut);
-   dList_append(Clients, NewClient);
-  pthread_mutex_unlock(&ClMut);
-   return NewClient;
-}
-
-/*
- * Get client record by number
- */
-static void *File_get_client_n(uint_t n)
-{
-   void *client;
-
-  pthread_mutex_lock(&ClMut);
-   client = dList_nth_data(Clients, n);
-  pthread_mutex_unlock(&ClMut);
-
-   return client;
+   new_client = dNew(ClientInfo, 1);
+   new_client->sh = a_Dpip_dsh_new(sock_fd, sock_fd, 8*1024);
+   new_client->orig_url = NULL;
+   new_client->filename = NULL;
+   new_client->file_fd = -1;
+   new_client->file_sz = 0;
+   new_client->d_dir = NULL;
+   new_client->state = 0;
+   new_client->err_code = 0;
+   new_client->flags = FILE_READ;
+   new_client->old_style = OLD_STYLE;
+   dList_append(Clients, new_client);
+   return new_client;
 }
 
 /*
  * Remove a client from the list.
  */
-static void File_remove_client_n(uint_t n)
+static void File_remove_client(ClientInfo *client)
 {
-   ClientInfo *Client;
-
-  pthread_mutex_lock(&ClMut);
-   Client = dList_nth_data(Clients, n);
-   dList_remove(Clients, (void *)Client);
-  pthread_mutex_unlock(&ClMut);
+   dList_remove(Clients, (void *)client);
 
    _MSG("Closing Socket Handler\n");
-   a_Dpip_dsh_close(Client->sh);
-   a_Dpip_dsh_free(Client->sh);
-   dFree(Client);
-}
+   a_Dpip_dsh_close(client->sh);
+   a_Dpip_dsh_free(client->sh);
+   dFree(client->orig_url);
+   dFree(client->filename);
+   File_dillodir_free(client->d_dir);
 
-/*
- * Return the number of clients.
- */
-static int File_num_clients(void)
-{
-   uint_t n;
-
-  pthread_mutex_lock(&ClMut);
-   n = dList_length(Clients);
-  pthread_mutex_unlock(&ClMut);
-
-   return n;
+   dFree(client);
 }
 
 /*
  * Serve this client.
- * (this function runs on its own thread)
  */
-static void File_serve_client(void *data)
+static void File_serve_client(void *data, int f_write)
 {
    char *dpip_tag = NULL, *cmd = NULL, *url = NULL, *path;
-   ClientInfo *Client = data;
+   ClientInfo *client = data;
    int st;
 
-   /* Authenticate our client... */
-   do {
-      dpip_tag = a_Dpip_dsh_read_token(Client->sh);
-   } while (!dpip_tag && Client->sh->status == DPIP_EAGAIN);
-   _MSG("auth={%s}\n", dpip_tag);
-   st = a_Dpip_check_auth(dpip_tag);
-   dFree(dpip_tag);
-   _MSG("a_Dpip_check_auth returned %d\n", st);
-   dReturn_if (st == -1);
-
-   /* Read the dpi command */
-   do {
-      dpip_tag = a_Dpip_dsh_read_token(Client->sh);
-   } while (!dpip_tag && Client->sh->status == DPIP_EAGAIN);
-   _MSG("dpip_tag={%s}\n", dpip_tag);
-
-   if (dpip_tag) {
-      cmd = a_Dpip_get_attr(dpip_tag, "cmd");
-      if (cmd) {
-         if (strcmp(cmd, "DpiBye") == 0) {
-            DPIBYE = 1;
-            MSG("file.dpi:: (pid %d): Got DpiBye.\n", (int)getpid());
+   while (1) {
+      MSG("File_serve_client %p, flags=%d\n", client, client->flags);
+      if (client->flags & (FILE_DONE | FILE_ERR))
+         break;
+      if (client->flags & FILE_READ) {
+         dpip_tag = a_Dpip_dsh_read_token(client->sh, 0);
+         MSG("dpip_tag={%s}\n", dpip_tag);
+         if (!dpip_tag)
+            break;
+      }
+   
+      if (client->flags & FILE_READ) {
+         if (!(client->flags & FILE_AUTH_OK)) {
+            /* Authenticate our client... */
+            st = a_Dpip_check_auth(dpip_tag);
+            MSG("a_Dpip_check_auth returned %d\n", st);
+            client->flags |= (st == 1) ? FILE_AUTH_OK : FILE_ERR;
          } else {
+            /* Get file request */
+            cmd = a_Dpip_get_attr(dpip_tag, "cmd");
             url = a_Dpip_get_attr(dpip_tag, "url");
-            if (!url)
-               MSG("file.dpi:: Failed to parse 'url'\n");
+            path = File_normalize_path(url);
+            if (cmd) {
+               if (strcmp(cmd, "DpiBye") == 0) {
+                  DPIBYE = 1;
+                  MSG("(pid %d): Got DpiBye.\n", (int)getpid());
+                  client->flags |= FILE_DONE;
+               } else if (url && strcmp(url, "dpi:/file/toggle") == 0) {
+                  File_toggle_html_style(client);
+               } else if (path) {
+                  File_get(client, path, url);
+               } else {
+                  client->flags |= FILE_ERR;
+                  MSG("ERROR: URL was %s\n", url);
+               }
+            }
+            dFree(path);
+            dFree(url);
+            dFree(cmd);
+            break;
          }
-      }
-      dFree(cmd);
-   }
-   dFree(dpip_tag);
-
-   if (!DPIBYE && url) {
-      _MSG("url = '%s'\n", url);
+         dFree(dpip_tag);
 
-      path = File_normalize_path(url);
-      if (path) {
-         _MSG("path = '%s'\n", path);
-         File_get(Client, path, url);
-      } else if (strcmp(url, "dpi:/file/toggle") == 0) {
-         File_toggle_html_style(Client);
-      } else {
-         MSG("ERROR: URL path was %s\n", url);
+      } else if (f_write) {
+         /* send our answer */
+         if (client->state == st_err)
+            File_send_error_page(client);
+         else if (client->d_dir)
+            File_send_dir(client);
+         else
+            File_send_file(client);
+         break;
       }
-      dFree(path);
-   }
-   dFree(url);
+   } /*while*/
 
-   /* flag the the transfer finished */
-   Client->done = 1;
+   client->flags |= (client->sh->status & DPIP_ERROR) ? FILE_ERR : 0;
+   client->flags |= (client->sh->status & DPIP_EOF) ? FILE_DONE : 0;
 }
 
 /*
  * Serve the client queue.
- * (this function runs on its own thread)
  */
-static void *File_serve_clients(void *client)
+static void File_serve_clients()
 {
-   /* switch to detached state */
-   pthread_detach(pthread_self());
+   int i, f_read, f_write;
+   ClientInfo *client;
 
-   while (File_num_clients()) {
-      client = File_get_client_n((uint_t)0);
-      File_serve_client(client);
-      File_remove_client_n((uint_t)0);
+   for (i = 0; (client = dList_nth_data(Clients, i)); ++i) {
+      f_read = FD_ISSET(client->sh->fd_in, &read_set);
+      f_write = FD_ISSET(client->sh->fd_out, &write_set);
+      if (!f_read && !f_write)
+         continue;
+      File_serve_client(client, f_write);
+      if (client->flags & (FILE_DONE | FILE_ERR)) {
+         File_remove_client(client);
+         --i;
+      }
    }
-   ThreadRunning = 0;
-
-   return NULL;
 }
 
 /* --------------------------------------------------------------------------*/
 
 /*
- * Check a fd for activity, with a max timeout.
+ * Check the fd sets for activity, with a max timeout.
  * return value: 0 if timeout, 1 if input available, -1 if error.
  */
-static int File_check_fd(int filedes, uint_t seconds)
+static int File_check_fds(uint_t seconds)
 {
-  int st;
-  fd_set set;
-  struct timeval timeout;
+   int i, st;
+   ClientInfo *client;
+   struct timeval timeout;
 
-  /* Initialize the file descriptor set. */
-  FD_ZERO (&set);
-  FD_SET (filedes, &set);
+   /* initialize observed file descriptors */
+   FD_ZERO (&read_set);
+   FD_ZERO (&write_set);
+   FD_SET (STDIN_FILENO, &read_set);
+   for (i = 0; (client = dList_nth_data(Clients, i)); ++i) {
+      if (client->flags & FILE_READ)
+         FD_SET (client->sh->fd_in, &read_set);
+      if (client->flags & FILE_WRITE)
+         FD_SET (client->sh->fd_out, &write_set);
+   }
+   MSG("Watching %d fds:", dList_length(Clients) + 1);
 
-  /* Initialize the timeout data structure. */
-  timeout.tv_sec = seconds;
-  timeout.tv_usec = 0;
+   /* Initialize the timeout data structure. */
+   timeout.tv_sec = seconds;
+   timeout.tv_usec = 0;
 
-  do {
-     st = select(FD_SETSIZE, &set, NULL, NULL, &timeout);
-  } while (st == -1 && errno == EINTR);
+   do {
+      st = select(FD_SETSIZE, &read_set, &write_set, NULL, &timeout);
+   } while (st == -1 && errno == EINTR);
 
-  return st;
+   MSG_RAW(" (%d%s%s)", STDIN_FILENO,
+           FD_ISSET(STDIN_FILENO, &read_set) ? "R" : "",
+           FD_ISSET(STDIN_FILENO, &write_set) ? "W" : "");
+   for (i = 0; (client = dList_nth_data(Clients, i)); ++i) {
+      MSG_RAW(" (%d%s%s)", client->sh->fd_in,
+              FD_ISSET(client->sh->fd_in, &read_set) ? "R" : "",
+              FD_ISSET(client->sh->fd_out, &write_set) ? "W" : "");
+   }
+   MSG_RAW("\n");
+ 
+   return st;
 }
 
 
 int main(void)
 {
-   ClientInfo *NewClient;
-   struct sockaddr_un spun;
-   int temp_sock_descriptor;
+   struct sockaddr_in sin;
    socklen_t address_size;
-   int c_st, st = 1;
-   uint_t i;
+   int tmp_fd, c_st, st = 1;
 
    /* Arrange the cleanup function for abnormal terminations */
    if (signal (SIGINT, termination_handler) == SIG_IGN)
@@ -968,57 +1053,46 @@
    if (signal (SIGTERM, termination_handler) == SIG_IGN)
      signal (SIGTERM, SIG_IGN);
 
-   MSG("(v.1) accepting connections...\n");
+   MSG("(v.2) accepting connections...\n");
 
-   /* initialize mutex */
-   pthread_mutex_init(&ClMut, NULL);
+   /* initialize observed file descriptors */
+   FD_ZERO (&read_set);
+   FD_ZERO (&write_set);
+   FD_SET (STDIN_FILENO, &read_set);
 
    /* initialize Clients list */
    Clients = dList_new(512);
 
    /* some OSes may need this... */
-   address_size = sizeof(struct sockaddr_un);
+   address_size = sizeof(struct sockaddr_in);
 
    /* start the service loop */
    while (!DPIBYE) {
-      /* wait for a connection */
+      /* wait for activity */
       do {
-         c_st = File_check_fd(STDIN_FILENO, 1);
+         c_st = File_check_fds(10);
       } while (c_st == 0 && !DPIBYE);
       if (c_st < 0) {
-         perror("[select]");
+         MSG(" select() %s\n", dStrerror(errno));
          break;
       }
       if (DPIBYE)
          break;
 
-      temp_sock_descriptor =
-         accept(STDIN_FILENO, (struct sockaddr *)&spun, &address_size);
-
-      if (temp_sock_descriptor == -1) {
-         perror("[accept]");
-         break;
+      if (FD_ISSET(STDIN_FILENO, &read_set)) {
+         /* accept the incoming connection */
+         tmp_fd = accept(STDIN_FILENO, (struct sockaddr *)&sin, &address_size);
+         if (tmp_fd == -1) {
+            MSG(" accept() %s\n", dStrerror(errno));
+            break;
+         } else {
+            /* Create and initialize a new client */
+            File_add_client(tmp_fd);
+         }
+         continue;
       }
 
-      /* Create and initialize a new client */
-      NewClient = File_add_client(temp_sock_descriptor);
-
-      if (!ThreadRunning) {
-         ThreadRunning = 1;
-         /* Serve the client from a thread (avoids deadlocks) */
-         if (pthread_create(&NewClient->thrID, NULL,
-                            File_serve_clients, NewClient) != 0) {
-            perror("[pthread_create]");
-            ThreadRunning = 0;
-            break;
-         }
-      }
-   }
-
-   /* TODO: handle a running thread better. */
-   for (i = 0; i < 5 && ThreadRunning; ++i) {
-      MSG("sleep i=%u", i);
-      sleep(i);
+      File_serve_clients();
    }
 
    if (DPIBYE)
--- a/dpid/main.c	Sun Nov 01 16:31:59 2009 -0300
+++ b/dpid/main.c	Sun Nov 01 16:31:59 2009 -0300
@@ -117,9 +117,7 @@
    char *dpip_tag;
 
    (void) sigprocmask(SIG_BLOCK, &mask_sigchld, NULL);
-   do {
-      dpip_tag = a_Dpip_dsh_read_token(sh);
-   } while (!dpip_tag && sh->status == DPIP_EAGAIN);
+   dpip_tag = a_Dpip_dsh_read_token(sh, 1);
    (void) sigprocmask(SIG_UNBLOCK, &mask_sigchld, NULL);
 
    return dpip_tag;
--- a/dpip/dpip.c	Sun Nov 01 16:31:59 2009 -0300
+++ b/dpip/dpip.c	Sun Nov 01 16:31:59 2009 -0300
@@ -22,6 +22,9 @@
 #include "dpip.h"
 #include "d_size.h"
 
+//#define RBUF_SZ 16384
+#define RBUF_SZ 1
+
 #define DPIP_TAG_END            " '>"
 #define DPIP_MODE_SWITCH_TAG    "cmd='start_send_page' "
 #define MSG_ERR(...)            fprintf(stderr, "[dpip]: " __VA_ARGS__)
@@ -292,27 +295,24 @@
 }
 
 /*
- * Read new data from the socket into our buffer.
- * Used by both blocking and non-blocking IO.
+ * Read raw data from the socket into our buffer without blocking.
  */
-static void Dpip_dsh_read(Dsh *dsh)
+static void Dpip_dsh_read_nb(Dsh *dsh)
 {
-//#define LBUF_SZ 16384
-#define LBUF_SZ 1
-
    ssize_t st;
    int old_flags, blocking;
-   char buf[LBUF_SZ];
+   char buf[RBUF_SZ];
 
    blocking = !(dsh->mode & DPIP_NONBLOCK);
    if (blocking) {
+      /* set NONBLOCKING temporarily... */
       old_flags = fcntl(dsh->fd_in, F_GETFL);
+      fcntl(dsh->fd_in, F_SETFL, O_NONBLOCK | old_flags);
    }
 
    while (1) {
-      /* can't use fread() */
       do
-         st = read(dsh->fd_in, buf, LBUF_SZ);
+         st = read(dsh->fd_in, buf, RBUF_SZ);
       while (st < 0 && errno == EINTR);
 
       if (st < 0) {
@@ -330,10 +330,6 @@
       } else {
          /* append to buf */
          dStr_append_l(dsh->rd_dbuf, buf, st);
-         if (blocking) {
-            /* set NONBLOCKING temporarily... */
-            fcntl(dsh->fd_in, F_SETFL, O_NONBLOCK | old_flags);
-         }
       }
    }
 
@@ -344,23 +340,73 @@
 }
 
 /*
+ * Read raw data from the socket into our buffer in BLOCKING mode.
+ */
+static void Dpip_dsh_read(Dsh *dsh)
+{
+
+   ssize_t st;
+   int old_flags, non_blocking;
+   char buf[RBUF_SZ];
+
+   non_blocking = (dsh->mode & DPIP_NONBLOCK);
+   if (non_blocking) {
+      /* set blocking mode temporarily */
+      old_flags = fcntl(dsh->fd_in, F_GETFL);
+      fcntl(dsh->fd_in, F_SETFL, old_flags);
+   }
+
+   while (1) {
+      do
+         st = read(dsh->fd_in, buf, RBUF_SZ);
+      while (st < 0 && errno == EINTR);
+
+      if (st < 0) {
+         MSG_ERR("[Dpip_dsh_read] %s\n", dStrerror(errno));
+         dsh->status = DPIP_ERROR;
+         break;
+      } else if (st == 0) {
+         dsh->status = DPIP_EOF;
+         break;
+      } else {
+         /* append to buf */
+         dStr_append_l(dsh->rd_dbuf, buf, st);
+         break;
+      }
+   }
+
+   if (non_blocking) {
+      /* restore non blocking mode */
+      fcntl(dsh->fd_in, F_SETFL, old_flags);
+   }
+
+   /* assert there's no more data in the wire... */
+   Dpip_dsh_read_nb(dsh);
+}
+
+/*
  * Return a newlly allocated string with the next dpip token in the socket.
  * Return value: token string on success, NULL otherwise
  */
-char *a_Dpip_dsh_read_token(Dsh *dsh)
+char *a_Dpip_dsh_read_token(Dsh *dsh, int blocking)
 {
    char *p, *ret = NULL;
 
+   /* Read all available data without blocking */
+   Dpip_dsh_read_nb(dsh);
+
    /* switch mode upon request */
    if (dsh->mode & DPIP_LAST_TAG)
       dsh->mode = DPIP_RAW;
 
-   /* Only read from socket when there's no data in buffer or
-    * the tag is incomplete */
-   if (dsh->rd_dbuf->len == 0 ||
-       (dsh->mode & DPIP_TAG &&
-        !(p = strstr(dsh->rd_dbuf->str, DPIP_TAG_END)))) {
-      Dpip_dsh_read(dsh);
+   if (blocking && dsh->mode & DPIP_TAG) {
+      /* Only wait for data when the tag is incomplete */
+      if (!strstr(dsh->rd_dbuf->str, DPIP_TAG_END)) {
+         do {
+            Dpip_dsh_read(dsh);
+            p = strstr(dsh->rd_dbuf->str, DPIP_TAG_END);
+         } while (!p && dsh->status == EAGAIN);
+      }
    }
 
    if (dsh->mode & DPIP_TAG) {
--- a/dpip/dpip.h	Sun Nov 01 16:31:59 2009 -0300
+++ b/dpip/dpip.h	Sun Nov 01 16:31:59 2009 -0300
@@ -68,7 +68,7 @@
 Dsh *a_Dpip_dsh_new(int fd_in, int fd_out, int flush_sz);
 int a_Dpip_dsh_write(Dsh *dsh, int flush, const char *Data, int DataSize);
 int a_Dpip_dsh_write_str(Dsh *dsh, int flush, const char *str);
-char *a_Dpip_dsh_read_token(Dsh *dsh);
+char *a_Dpip_dsh_read_token(Dsh *dsh, int blocking);
 void a_Dpip_dsh_close(Dsh *dsh);
 void a_Dpip_dsh_free(Dsh *dsh);