gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] r5980 - in libmicrohttpd: . src/daemon src/include


From: gnunet
Subject: [GNUnet-SVN] r5980 - in libmicrohttpd: . src/daemon src/include
Date: Wed, 19 Dec 2007 21:23:00 -0700 (MST)

Author: grothoff
Date: 2007-12-19 21:23:00 -0700 (Wed, 19 Dec 2007)
New Revision: 5980

Modified:
   libmicrohttpd/ChangeLog
   libmicrohttpd/README
   libmicrohttpd/configure.ac
   libmicrohttpd/src/daemon/Makefile.am
   libmicrohttpd/src/daemon/connection.c
   libmicrohttpd/src/daemon/connection.h
   libmicrohttpd/src/daemon/daemon.c
   libmicrohttpd/src/daemon/daemontest_get.c
   libmicrohttpd/src/daemon/daemontest_large_put.c
   libmicrohttpd/src/daemon/daemontest_postform.c
   libmicrohttpd/src/daemon/daemontest_put_chunked.c
   libmicrohttpd/src/daemon/fileserver_example.c
   libmicrohttpd/src/daemon/internal.h
   libmicrohttpd/src/daemon/memorypool.c
   libmicrohttpd/src/daemon/memorypool.h
   libmicrohttpd/src/daemon/minimal_example.c
   libmicrohttpd/src/include/microhttpd.h
Log:
new MHD with support for chunked encoding

Modified: libmicrohttpd/ChangeLog
===================================================================
--- libmicrohttpd/ChangeLog     2007-12-20 00:40:49 UTC (rev 5979)
+++ libmicrohttpd/ChangeLog     2007-12-20 04:23:00 UTC (rev 5980)
@@ -1,3 +1,12 @@
+Wed Dec 19 21:12:04 MST 2007
+        Implemented chunked (HTTP 1.1) downloads (including
+        sending of HTTP footers).  Also allowed queuing of
+        a response early to suppress the otherwise automatic
+        "100 CONTINUE" response.  Removed the mostly useless
+        "(un)register handler" methods from the API.  Changed
+        the internal implementation to use a finite state
+        machine (cleaner code, slightly less memory consumption). - CG
+       
 Sun Dec 16 03:24:13 MST 2007
         Implemented handling of chunked (HTTP 1.1) uploads.
         Note that the upload callback must be able to 

Modified: libmicrohttpd/README
===================================================================
--- libmicrohttpd/README        2007-12-20 00:40:49 UTC (rev 5979)
+++ libmicrohttpd/README        2007-12-20 04:23:00 UTC (rev 5980)
@@ -24,7 +24,7 @@
 default to reduce the size of the library (error messages take
 space!).  If you are concerned about space, you should set "CFLAGS" to
 "-Os --fomit-frame-pointer" to have gcc generate tight code.  The
-resulting binary should be less than 25k (on x86).
+resulting binary should be about 25k (on x86).
 
 
 Portability
@@ -50,11 +50,6 @@
 feature.
 
 
-For http/1.1-compliance:
-========================
-connection.c:
-- support sending of chunked responses (#1302, TEST, ARCH)
-
 For POST:
 =========
 - add support to decode multipart/form-data with 
@@ -71,6 +66,10 @@
 - add testcases for http/1.1 pipelining (need
   to figure out how to ensure curl pipelines)
 - add testcases for resource limit enforcement
+- add testcases for client queuing early response,
+  suppressing 100 CONTINUE
+- extend testcase for chunked encoding to validate
+  handling of footers
 
 Documentation:
 ==============

Modified: libmicrohttpd/configure.ac
===================================================================
--- libmicrohttpd/configure.ac  2007-12-20 00:40:49 UTC (rev 5979)
+++ libmicrohttpd/configure.ac  2007-12-20 04:23:00 UTC (rev 5980)
@@ -21,8 +21,8 @@
 #
 #
 AC_PREREQ(2.57)
-AC_INIT([libmicrohttpd], [0.1.2],address@hidden)
-AM_INIT_AUTOMAKE([libmicrohttpd], [0.1.2])
+AC_INIT([libmicrohttpd], [0.2.0],address@hidden)
+AM_INIT_AUTOMAKE([libmicrohttpd], [0.2.0])
 AM_CONFIG_HEADER([config.h])
 
 AH_TOP([#define _GNU_SOURCE  1])

Modified: libmicrohttpd/src/daemon/Makefile.am
===================================================================
--- libmicrohttpd/src/daemon/Makefile.am        2007-12-20 00:40:49 UTC (rev 
5979)
+++ libmicrohttpd/src/daemon/Makefile.am        2007-12-20 04:23:00 UTC (rev 
5980)
@@ -12,7 +12,7 @@
   libmicrohttpd.la
 
 libmicrohttpd_la_LDFLAGS = \
-  -export-dynamic -version-info 2:1:1 $(retaincommand)
+  -export-dynamic -version-info 3:0:0 $(retaincommand)
 libmicrohttpd_la_SOURCES = \
   connection.c connection.h \
   reason_phrase.c reason_phrase.h \
@@ -48,7 +48,6 @@
   daemontest_postform \
   daemontest_post_loop \
   daemontest_put \
-  daemontest_put_chunked \
   daemontest_large_put \
   daemontest_get11 \
   daemontest_post11 \
@@ -56,7 +55,9 @@
   daemontest_post_loop11 \
   daemontest_put11 \
   daemontest_large_put11 \
-  daemontest_long_header
+  daemontest_long_header \
+  daemontest_get_chunked \
+  daemontest_put_chunked 
 
 TESTS = $(check_PROGRAMS)
 
@@ -71,6 +72,12 @@
   $(top_builddir)/src/daemon/libmicrohttpd.la \
   @LIBCURL@ 
 
+daemontest_get_chunked_SOURCES = \
+  daemontest_get_chunked.c
+daemontest_get_chunked_LDADD = \
+  $(top_builddir)/src/daemon/libmicrohttpd.la \
+  @LIBCURL@ 
+
 daemontest_post_SOURCES = \
   daemontest_post.c
 daemontest_post_LDADD = \

Modified: libmicrohttpd/src/daemon/connection.c
===================================================================
--- libmicrohttpd/src/daemon/connection.c       2007-12-20 00:40:49 UTC (rev 
5979)
+++ libmicrohttpd/src/daemon/connection.c       2007-12-20 04:23:00 UTC (rev 
5980)
@@ -2,7 +2,6 @@
      This file is part of libmicrohttpd
      (C) 2007 Daniel Pittman and Christian Grothoff
 
-
      This library is free software; you can redistribute it and/or
      modify it under the terms of the GNU Lesser General Public
      License as published by the Free Software Foundation; either
@@ -62,6 +61,14 @@
  */
 #define REQUEST_LACKS_HOST ""
 
+#define EXTRA_CHECKS MHD_YES
+
+#if EXTRA_CHECKS
+#define EXTRA_CHECK(a) if (!(a)) abort();
+#else
+#define EXTRA_CHECK(a)
+#endif
+
 /**
  * Add extra debug messages with reasons for closing connections
  * (non-error reasons).
@@ -73,6 +80,10 @@
  */
 #define DEBUG_SEND_DATA MHD_NO
 
+/**
+ * Should all state transitions be printed to stderr?
+ */
+#define DEBUG_STATES MHD_NO
 
 /**
  * Get all of the headers from the request.
@@ -109,7 +120,6 @@
   return ret;
 }
 
-
 /**
  * Get a particular header value.  If multiple
  * values match the kind, return any one of them.
@@ -149,11 +159,11 @@
 MHD_queue_response (struct MHD_Connection *connection,
                     unsigned int status_code, struct MHD_Response *response)
 {
-  if ((connection == NULL) ||
-      (response == NULL) ||
-      (connection->response != NULL) ||
-      (connection->have_received_body == MHD_NO)
-      || (connection->have_received_headers == MHD_NO))
+  if ( (connection == NULL) ||
+       (response == NULL) ||
+       (connection->response != NULL) ||
+       ( (connection->state != MHD_CONNECTION_HEADERS_PROCESSED) &&
+        (connection->state != MHD_CONNECTION_FOOTERS_RECEIVED) ) )
     return MHD_NO;
   MHD_increment_response_rc (response);
   connection->response = response;
@@ -165,6 +175,21 @@
          have already sent the full message body */
       connection->response_write_position = response->total_size;
     }
+  if ( (response->total_size == -1) &&
+       (0 == strcasecmp(connection->version,
+                       MHD_HTTP_VERSION_1_1)) )
+    connection->have_chunked_response = MHD_YES;
+  else
+    connection->have_chunked_response = MHD_NO;
+  if (connection->state == MHD_CONNECTION_HEADERS_PROCESSED) 
+    {
+      /* response was queued "early", 
+        refuse to read body / footers or further
+        requests! */
+      SHUTDOWN (connection->socket_fd, SHUT_RD);
+      connection->read_closed = MHD_YES;
+      connection->state = MHD_CONNECTION_FOOTERS_RECEIVED;
+    }
   return MHD_YES;
 }
 
@@ -173,14 +198,14 @@
  * message for this connection?
  */
 static int
-MHD_need_100_continue (struct MHD_Connection *connection)
+need_100_continue (struct MHD_Connection *connection)
 {
   const char *expect;
 
-  return ((connection->version != NULL) &&
+  return ((connection->response == NULL) &&
+         (connection->version != NULL) &&
           (0 == strcasecmp (connection->version,
                             MHD_HTTP_VERSION_1_1)) &&
-          (connection->have_received_headers == MHD_YES) &&
           (NULL != (expect = MHD_lookup_connection_value (connection,
                                                           MHD_HEADER_KIND,
                                                           
MHD_HTTP_HEADER_EXPECT)))
@@ -199,6 +224,7 @@
   SHUTDOWN (connection->socket_fd, SHUT_RDWR);
   CLOSE (connection->socket_fd);
   connection->socket_fd = -1;
+  connection->state = MHD_CONNECTION_CLOSED;
   if (connection->daemon->notify_completed != NULL)
     connection->daemon->notify_completed (connection->daemon->
                                           notify_completed_cls, connection,
@@ -216,12 +242,14 @@
  * @return MHD_NO if readying the response failed
  */
 static int
-ready_response (struct MHD_Connection *connection)
+try_ready_normal_body (struct MHD_Connection *connection)
 {
   int ret;
   struct MHD_Response *response;
 
   response = connection->response;
+  if (response->crc == NULL)
+    return MHD_YES;
   ret = response->crc (response->crc_cls,
                        connection->response_write_position,
                        response->data,
@@ -230,7 +258,8 @@
                             connection->response_write_position));
   if (ret == -1)
     {
-      /* end of message, signal other side by closing! */
+      /* either error or http 1.0 transfer, close
+        socket! */
 #if DEBUG_CLOSE
 #if HAVE_MESSAGES
       MHD_DLOG (connection->daemon, "Closing connection (end of response)\n");
@@ -239,94 +268,281 @@
       response->total_size = connection->response_write_position;
       connection_close_error (connection);
       return MHD_NO;
-    }
+    }    
   response->data_start = connection->response_write_position;
   response->data_size = ret;
   if (ret == 0)
+    return MHD_NO;
+  return MHD_YES;
+}
+
+/**
+ * Prepare the response buffer of this connection for
+ * sending.  Assumes that the response mutex is 
+ * already held.  If the transmission is complete,
+ * this function may close the socket (and return
+ * MHD_NO).
+ * 
+ * @return MHD_NO if readying the response failed
+ */
+static int
+try_ready_chunked_body (struct MHD_Connection *connection)
+{
+  int ret;
+  char * buf;
+  struct MHD_Response *response;
+  unsigned int size;
+  char cbuf[9];
+
+  response = connection->response;  
+  if (connection->write_buffer_size == 0)
     {
-      /* avoid busy-waiting when using external select
-         (we assume that the main application will 
-         wake up the external select once more data
-         is ready).  With internal selects, we
-         have no choice; if the app uses a thread
-         per connection, ret==0 is likely a bug --
-         the application should block until data
-         is ready! */
-      if ((0 ==
-           (connection->daemon->
-            options & (MHD_USE_SELECT_INTERNALLY |
-                       MHD_USE_THREAD_PER_CONNECTION))))
-        connection->response_unready = MHD_YES;
+      size = connection->daemon->pool_size;
+      do 
+       {
+         size /= 2;
+         if (size < 128) 
+           {
+             /* not enough memory */
+#if DEBUG_CLOSE
+#if HAVE_MESSAGES
+             MHD_DLOG (connection->daemon, "Closing connection (out of 
memory)\n");
+#endif
+#endif
+             connection_close_error (connection);
+             return MHD_NO; 
+           }
+         buf = MHD_pool_allocate (connection->pool,
+                                  size,
+                                  MHD_NO);
+       } 
+      while (buf == NULL);
+      connection->write_buffer_size = size;
+      connection->write_buffer = buf;
+    }
+  
+  ret = response->crc (response->crc_cls,
+                       connection->response_write_position,
+                       &connection->write_buffer[8],
+                       connection->write_buffer_size - 8 - 2);
+  if (ret == -1)
+    {
+      /* end of message, signal other side! */
+      strcpy(connection->write_buffer,
+            "0\r\n");      
+      connection->write_buffer_append_offset = 3;
+      connection->write_buffer_send_offset = 0;
+      response->total_size = connection->response_write_position;
+      return MHD_YES;
+    }
+  if (ret == 0)
+    {
+      connection->state = MHD_CONNECTION_CHUNKED_BODY_UNREADY;
       return MHD_NO;
     }
-  connection->response_unready = MHD_NO;
+  if (ret > 0xFFFFFF)
+    ret = 0xFFFFFF;
+  snprintf(cbuf,
+          8,
+          "%X\r\n",
+          ret);
+  memcpy(&connection->write_buffer[8 - strlen(cbuf)],
+        cbuf,
+        strlen(cbuf));
+  memcpy(&connection->write_buffer[8 + ret],
+        "\r\n",
+        2);
+  connection->response_write_position += ret;
+  connection->write_buffer_send_offset = 8 - strlen(cbuf);
+  connection->write_buffer_append_offset = 8 + ret + 2; 
   return MHD_YES;
 }
 
 /**
- * Obtain the select sets for this connection
- *
- * @return MHD_YES on success
+ * Check if we need to set some additional headers
+ * for http-compiliance.
  */
-int
-MHD_connection_get_fdset (struct MHD_Connection *connection,
-                          fd_set * read_fd_set,
-                          fd_set * write_fd_set,
-                          fd_set * except_fd_set, int *max_fd)
+static void
+add_extra_headers (struct MHD_Connection *connection)
 {
-  int fd;
-  void *buf;
+  const char *have;
+  char buf[128];
 
-  fd = connection->socket_fd;
-  if (fd == -1)
-    return MHD_YES;
-  if ((connection->read_close == MHD_NO) &&
-      ((connection->have_received_headers == MHD_NO) ||
-       (connection->read_buffer_offset < connection->read_buffer_size)))
+  connection->have_chunked_upload = MHD_NO;
+  if (connection->response->total_size == -1)
+    {      
+      have = MHD_get_response_header (connection->response,
+                                      MHD_HTTP_HEADER_CONNECTION);
+      if ( (have == NULL) ||
+          (0 != strcasecmp(have, "close")) )
+       {
+         if ( (connection->version != NULL) &&
+              (0 == strcasecmp(connection->version,
+                               MHD_HTTP_VERSION_1_1)) )
+           {
+             connection->have_chunked_upload = MHD_YES;
+             have = MHD_get_response_header (connection->response,
+                                             
MHD_HTTP_HEADER_TRANSFER_ENCODING);
+             if (have == NULL)
+               MHD_add_response_header (connection->response,
+                                        MHD_HTTP_HEADER_TRANSFER_ENCODING, 
+                                        "chunked");
+           }
+         else        
+           {
+             MHD_add_response_header (connection->response,
+                                      MHD_HTTP_HEADER_CONNECTION, "close");
+           }
+       }
+    }
+  else if (NULL == MHD_get_response_header (connection->response,
+                                            MHD_HTTP_HEADER_CONTENT_LENGTH))
     {
-      FD_SET (fd, read_fd_set);
-      if (fd > *max_fd)
-        *max_fd = fd;
+      _REAL_SNPRINTF (buf,
+                      128,
+                      "%llu",
+                      (unsigned long long) connection->response->total_size);
+      MHD_add_response_header (connection->response,
+                               MHD_HTTP_HEADER_CONTENT_LENGTH, buf);
     }
+}
+
+/**
+ * Produce HTTP "Date:" header.
+ * @param date where to write the header
+ * @param max maximum number of characters to write
+ */
+static void
+get_date_string (char *date, unsigned int max)
+{
+  static const char *days[] =
+    { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
+  static const char *mons[] =
+    { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct",
+    "Nov", "Dec"
+  };
+  struct tm now;
+  time_t t;
+
+  time (&t);
+  gmtime_r (&t, &now);
+  snprintf (date,
+            max - 1,
+            "Date: %3s, %02u %3s %04u %02u:%02u:%02u GMT\r\n",
+            days[now.tm_wday % 7],
+            now.tm_mday,
+            mons[now.tm_mon % 12],
+            now.tm_year, now.tm_hour, now.tm_min, now.tm_sec);
+}
+
+/**
+ * try growing the read buffer
+ * @return MHD_YES on success, MHD_NO on failure
+ */
+static int
+try_grow_read_buffer(struct MHD_Connection * connection)
+{
+  void * buf;
+
+  buf = MHD_pool_reallocate (connection->pool,
+                            connection->read_buffer,
+                            connection->read_buffer_size,
+                            connection->read_buffer_size * 2 +
+                            MHD_BUF_INC_SIZE + 1);
+  if (buf == NULL)
+    return MHD_NO;
+  /* we can actually grow the buffer, do it! */
+  connection->read_buffer = buf;
+  connection->read_buffer_size =
+    connection->read_buffer_size * 2 + MHD_BUF_INC_SIZE;
+  return MHD_YES;  
+}
+
+/**
+ * Allocate the connection's write buffer and
+ * fill it with all of the headers (or footers,
+ * if we have already sent the body) from the
+ * HTTPd's response.
+ */
+static int
+build_header_response (struct MHD_Connection *connection)
+{
+  size_t size;
+  size_t off;
+  struct MHD_HTTP_Header *pos;
+  char code[128];
+  char date[128];
+  char *data;
+  enum MHD_ValueKind kind;
+  const char *reason_phrase;
+    
+  if (connection->state == MHD_CONNECTION_FOOTERS_RECEIVED)
+    {
+      add_extra_headers (connection);
+      reason_phrase =
+       MHD_get_reason_phrase_for (connection->responseCode);
+      _REAL_SNPRINTF (code, 128, "%s %u %s\r\n", MHD_HTTP_VERSION_1_1,
+                     connection->responseCode, reason_phrase);
+      off = strlen (code);
+      /* estimate size */
+      size = off + 2;               /* extra \r\n at the end */
+      kind = MHD_HEADER_KIND;
+      if (NULL == MHD_get_response_header (connection->response,
+                                          MHD_HTTP_HEADER_DATE))
+       get_date_string (date, sizeof (date));
+      else
+       date[0] = '\0';
+      size += strlen (date);
+    }
   else
     {
-      if ((connection->read_close == MHD_NO) &&
-          ((connection->have_received_headers == MHD_YES) &&
-           (connection->read_buffer_offset == connection->read_buffer_size)))
-        {
-          /* try growing the read buffer, just in case */
-          buf = MHD_pool_reallocate (connection->pool,
-                                     connection->read_buffer,
-                                     connection->read_buffer_size,
-                                     connection->read_buffer_size * 2 +
-                                     MHD_BUF_INC_SIZE);
-          if (buf != NULL)
-            {
-              /* we can actually grow the buffer, do it! */
-              connection->read_buffer = buf;
-              connection->read_buffer_size =
-                connection->read_buffer_size * 2 + MHD_BUF_INC_SIZE;
-              FD_SET (fd, read_fd_set);
-              if (fd > *max_fd)
-                *max_fd = fd;
-            }
-        }
+      size = 2;
+      kind = MHD_FOOTER_KIND;
+      off = 0;
     }
-  if ((connection->response != NULL) &&
-      (connection->response_unready == MHD_YES))
+  pos = connection->response->first_header;
+  while (pos != NULL)
     {
-      pthread_mutex_lock (&connection->response->mutex);
-      ready_response (connection);
-      pthread_mutex_unlock (&connection->response->mutex);
+      if (pos->kind == kind)
+       size += strlen (pos->header) + strlen (pos->value) + 4;   /* colon, 
space, linefeeds */
+      pos = pos->next;
     }
-  if (((connection->response != NULL) &&
-       (connection->response_unready == MHD_NO)) ||
-      MHD_need_100_continue (connection))
+  /* produce data */  
+  data = MHD_pool_allocate (connection->pool, size + 1, MHD_YES);
+  if (data == NULL)
     {
-      FD_SET (fd, write_fd_set);
-      if (fd > *max_fd)
-        *max_fd = fd;
+#if HAVE_MESSAGES
+      MHD_DLOG (connection->daemon, "Not enough memory for write!\n");
+#endif
+      return MHD_NO;
     }
+  if (connection->state == MHD_CONNECTION_FOOTERS_RECEIVED)
+    {
+      memcpy (data, code, off);
+    }
+  pos = connection->response->first_header;
+  while (pos != NULL)
+    {
+      if (pos->kind == kind) 
+       {
+         SPRINTF (&data[off], "%s: %s\r\n", pos->header, pos->value);
+         off += strlen (pos->header) + strlen (pos->value) + 4;
+       }
+      pos = pos->next;
+    }
+  if (connection->state == MHD_CONNECTION_FOOTERS_RECEIVED)
+    {
+      strcpy (&data[off], date);
+      off += strlen (date);
+    }
+  sprintf (&data[off], "\r\n");
+  off += 2;
+  if (off != size)
+    abort ();
+  connection->write_buffer = data;
+  connection->write_buffer_append_offset = size;
+  connection->write_buffer_send_offset = 0;
+  connection->write_buffer_size = size + 1;
   return MHD_YES;
 }
 
@@ -338,15 +554,14 @@
  * @param status_code the response code to send (413 or 414)
  */
 static void
-MHD_excessive_data_handler (struct MHD_Connection *connection,
-                            unsigned int status_code)
+excessive_data_handler (struct MHD_Connection *connection,
+                       unsigned int status_code)
 {
   struct MHD_Response *response;
 
   /* die, header far too long to be reasonable */
-  connection->read_close = MHD_YES;
-  connection->have_received_headers = MHD_YES;
-  connection->have_received_body = MHD_YES;
+  connection->state = MHD_CONNECTION_FOOTERS_RECEIVED;
+  connection->read_closed = MHD_YES;
 #if HAVE_MESSAGES
   MHD_DLOG (connection->daemon,
             "Received excessively long header, closing connection.\n");
@@ -354,10 +569,169 @@
   response = MHD_create_response_from_data (strlen (REQUEST_TOO_BIG),
                                             REQUEST_TOO_BIG, MHD_NO, MHD_NO);
   MHD_queue_response (connection, status_code, response);
+  EXTRA_CHECK(connection->response != NULL);
   MHD_destroy_response (response);
+  if (MHD_NO == build_header_response (connection))
+    {
+      /* oops - close! */
+#if HAVE_MESSAGES
+      MHD_DLOG (connection->daemon,
+               "Closing connection (failed to create response header)\n");
+#endif
+      connection->state = MHD_CONNECTION_CLOSED;
+    } 
+  else
+    {
+      connection->state = MHD_CONNECTION_HEADERS_SENDING;
+    } 
 }
 
 /**
+ * Add "fd" to the "fd_set".  If "fd" is
+ * greater than "*max", set "*max" to fd.
+ */
+static void
+do_fd_set(int fd,
+         fd_set * set,
+         int * max_fd) 
+{
+  FD_SET (fd, set);
+  if (fd > *max_fd)
+    *max_fd = fd;
+}
+
+/**
+ * Obtain the select sets for this connection
+ *
+ * @return MHD_YES on success
+ */
+int
+MHD_connection_get_fdset (struct MHD_Connection *connection,
+                          fd_set * read_fd_set,
+                          fd_set * write_fd_set,
+                          fd_set * except_fd_set, int *max_fd)
+{
+  int fd;
+
+  if (connection->pool == NULL)
+    connection->pool = MHD_pool_create (connection->daemon->pool_size);
+  if (connection->pool == NULL)
+    {
+#if HAVE_MESSAGES
+      MHD_DLOG (connection->daemon, "Failed to create memory pool!\n");
+#endif
+      connection_close_error (connection);
+      return MHD_NO;
+    }
+  fd = connection->socket_fd;
+  if (fd == -1)
+    return MHD_YES;
+  while (1) {
+#if DEBUG_STATES
+    fprintf(stderr,
+           "`%s' in state %u\n",
+           __FUNCTION__,
+           connection->state);
+#endif    
+    switch (connection->state)
+      {
+      case MHD_CONNECTION_INIT:
+      case MHD_CONNECTION_URL_RECEIVED:
+      case MHD_CONNECTION_HEADER_PART_RECEIVED:
+       /* while reading headers, we always grow the
+          read buffer if needed, no size-check required */
+       if ( (connection->read_closed) &&
+            (connection->read_buffer_offset == 0) )
+         {
+           connection->state = MHD_CONNECTION_CLOSED;
+           continue;
+         }     
+       if ( (connection->read_buffer_offset == connection->read_buffer_size) &&
+            (MHD_NO == try_grow_read_buffer(connection)) )
+         {
+            excessive_data_handler (connection,
+                                   (connection->url != NULL)
+                                   ? MHD_HTTP_REQUEST_ENTITY_TOO_LARGE
+                                   : MHD_HTTP_REQUEST_URI_TOO_LONG);
+           continue;
+         }
+       if (MHD_NO == connection->read_closed)
+         do_fd_set (fd, read_fd_set, max_fd);
+       break;
+      case MHD_CONNECTION_HEADERS_RECEIVED:
+       /* we should never get here */
+       EXTRA_CHECK(0);
+       break;
+      case MHD_CONNECTION_HEADERS_PROCESSED:
+       EXTRA_CHECK(0);
+       break;
+      case MHD_CONNECTION_CONTINUE_SENDING:
+       do_fd_set (fd, write_fd_set, max_fd);
+       break;
+      case MHD_CONNECTION_CONTINUE_SENT:
+       if (connection->read_buffer_offset == connection->read_buffer_size)
+         try_grow_read_buffer(connection);
+       if (connection->read_buffer_offset < connection->read_buffer_size)
+         do_fd_set (fd, read_fd_set, max_fd);
+       break;
+      case MHD_CONNECTION_BODY_RECEIVED:       
+      case MHD_CONNECTION_FOOTER_PART_RECEIVED:        
+       /* while reading footers, we always grow the
+          read buffer if needed, no size-check required */
+       if (MHD_YES == connection->read_closed)
+         {
+           connection->state = MHD_CONNECTION_CLOSED;
+           continue;
+         }
+       do_fd_set (fd, read_fd_set, max_fd);
+       /* transition to FOOTERS_RECEIVED
+          happens in read handler */
+       break;
+      case MHD_CONNECTION_FOOTERS_RECEIVED:    
+       /* no socket action, wait for client
+          to provide response */
+       break;
+      case MHD_CONNECTION_HEADERS_SENDING:
+       /* headers in buffer, keep writing */
+       do_fd_set(fd, write_fd_set, max_fd);
+       break;
+      case MHD_CONNECTION_HEADERS_SENT:
+       EXTRA_CHECK(0);
+       break;
+      case MHD_CONNECTION_NORMAL_BODY_READY:
+       do_fd_set (fd, write_fd_set, max_fd);
+       break;
+      case MHD_CONNECTION_NORMAL_BODY_UNREADY:
+       /* not ready, no socket action */
+       break;
+      case MHD_CONNECTION_CHUNKED_BODY_READY:
+       do_fd_set (fd, write_fd_set, max_fd);
+       break;
+      case MHD_CONNECTION_CHUNKED_BODY_UNREADY:
+       /* not ready, no socket action */
+       break;
+      case MHD_CONNECTION_BODY_SENT:
+       EXTRA_CHECK(0);
+       break;
+      case MHD_CONNECTION_FOOTERS_SENDING:
+       do_fd_set (fd, write_fd_set, max_fd);
+       break;
+      case MHD_CONNECTION_FOOTERS_SENT:
+       EXTRA_CHECK(0);
+       break;
+      case MHD_CONNECTION_CLOSED:
+       if (connection->socket_fd != -1) 
+         connection_close_error(connection);     
+       return MHD_YES; /* do nothing, not even reading */
+      default:
+       EXTRA_CHECK(0);
+      }
+    break;
+  }
+  return MHD_YES;
+}
+
+/**
  * Parse a single line of the HTTP header.  Advance
  * read_buffer (!) appropriately.  If the current line does not
  * fit, consider growing the buffer.  If the line is
@@ -366,7 +740,7 @@
  * return NULL.  Otherwise return a pointer to the line.
  */
 static char *
-MHD_get_next_header_line (struct MHD_Connection *connection)
+get_next_header_line (struct MHD_Connection *connection)
 {
   char *rbuf;
   size_t pos;
@@ -390,10 +764,10 @@
                                       MHD_BUF_INC_SIZE);
           if (rbuf == NULL)
             {
-              MHD_excessive_data_handler (connection,
-                                          (connection->url != NULL)
-                                          ? MHD_HTTP_REQUEST_ENTITY_TOO_LARGE
-                                          : MHD_HTTP_REQUEST_URI_TOO_LONG);
+              excessive_data_handler (connection,
+                                     (connection->url != NULL)
+                                     ? MHD_HTTP_REQUEST_ENTITY_TOO_LARGE
+                                     : MHD_HTTP_REQUEST_URI_TOO_LONG);
             }
           else
             {
@@ -418,8 +792,8 @@
  * @return MHD_NO on failure (out of memory), MHD_YES for success
  */
 static int
-MHD_connection_add_header (struct MHD_Connection *connection,
-                           char *key, char *value, enum MHD_ValueKind kind)
+connection_add_header (struct MHD_Connection *connection,
+                      char *key, char *value, enum MHD_ValueKind kind)
 {
   struct MHD_HTTP_Header *hdr;
 
@@ -431,8 +805,8 @@
       MHD_DLOG (connection->daemon,
                 "Not enough memory to allocate header record!\n");
 #endif
-      MHD_excessive_data_handler (connection,
-                                  MHD_HTTP_REQUEST_ENTITY_TOO_LARGE);
+      excessive_data_handler (connection,
+                             MHD_HTTP_REQUEST_ENTITY_TOO_LARGE);
       return MHD_NO;
     }
   hdr->next = connection->headers_received;
@@ -468,8 +842,8 @@
         }
       MHD_http_unescape (args);
       MHD_http_unescape (equals);
-      if (MHD_NO == MHD_connection_add_header (connection,
-                                               args, equals, kind))
+      if (MHD_NO == connection_add_header (connection,
+                                          args, equals, kind))
         return MHD_NO;
       args = amper;
     }
@@ -482,7 +856,7 @@
  * @return MHD_YES for success, MHD_NO for failure (malformed, out of memory)
  */
 static int
-MHD_parse_cookie_header (struct MHD_Connection *connection)
+parse_cookie_header (struct MHD_Connection *connection)
 {
   const char *hdr;
   char *cpy;
@@ -500,8 +874,8 @@
 #if HAVE_MESSAGES
       MHD_DLOG (connection->daemon, "Not enough memory to parse cookies!\n");
 #endif
-      MHD_excessive_data_handler (connection,
-                                  MHD_HTTP_REQUEST_ENTITY_TOO_LARGE);
+      excessive_data_handler (connection,
+                             MHD_HTTP_REQUEST_ENTITY_TOO_LARGE);
       return MHD_NO;
     }
   memcpy (cpy, hdr, strlen (hdr) + 1);
@@ -536,8 +910,8 @@
           equals[strlen (equals) - 1] = '\0';
           equals++;
         }
-      if (MHD_NO == MHD_connection_add_header (connection,
-                                               pos, equals, MHD_COOKIE_KIND))
+      if (MHD_NO == connection_add_header (connection,
+                                          pos, equals, MHD_COOKIE_KIND))
         return MHD_NO;
       pos = semicolon;
     }
@@ -587,235 +961,29 @@
   return MHD_YES;
 }
 
-
 /**
- * This function is designed to parse the input buffer of a given
- * connection for HTTP headers -- and in the case of chunked encoding,
- * also for HTTP "footers".
- *
- * Once the header is complete, it should have set the
- * headers_received, url and method values and set
- * have_received_headers to MHD_YES.  If no body is expected, it
- * should also set "have_received_body" to MHD_YES.  Otherwise, it
- * should set "remaining_upload_size" to the expected size of the
- * body.  If the size of the body is unknown, it should be set to -1.
+ * Call the handler of the application for this
+ * connection.  Handles chunking of the upload
+ * as well as normal uploads.
  */
 static void
-MHD_parse_connection_headers (struct MHD_Connection *connection)
+call_connection_handler (struct MHD_Connection *connection)
 {
-  char *last;
-  char *line;
-  char *colon;
-  char *tmp;
-  const char *clen;
-  unsigned long long cval;
-  struct MHD_Response *response;
-
-  if (((connection->have_received_body == MHD_YES) &&
-       (connection->have_chunked_upload == MHD_NO)) ||
-      (connection->have_received_headers == MHD_YES))
-    abort ();
-  colon = NULL;                 /* make gcc happy */
-  last = NULL;
-  while (NULL != (line = MHD_get_next_header_line (connection)))
-    {
-      if (last != NULL)
-        {
-          if ((line[0] == ' ') || (line[0] == '\t'))
-            {
-              /* value was continued on the next line, see
-                 http://www.jmarshall.com/easy/http/ */
-              last = MHD_pool_reallocate (connection->pool,
-                                          last,
-                                          strlen (last) + 1,
-                                          strlen (line) + strlen (last) + 1);
-              if (last == NULL)
-                {
-                  MHD_excessive_data_handler (connection,
-                                              
MHD_HTTP_REQUEST_ENTITY_TOO_LARGE);
-                  break;
-                }
-              tmp = line;
-              while ((tmp[0] == ' ') || (tmp[0] == '\t'))
-                tmp++;          /* skip whitespace at start of 2nd line */
-              strcat (last, tmp);
-              continue;         /* possibly more than 2 lines... */
-            }
-          else
-            {
-              if (MHD_NO == MHD_connection_add_header (connection,
-                                                       last,
-                                                       colon,
-                                                       MHD_HEADER_KIND))
-                return;
-              last = NULL;
-            }
-        }
-      if (connection->url == NULL)
-        {
-          /* line must be request line (first line of header) */
-          if (MHD_NO == parse_initial_message_line (connection, line))
-            goto DIE;
-          continue;
-        }
-      /* check if this is the end of the header */
-      if (strlen (line) == 0)
-        {
-          /* end of header */
-          connection->have_received_headers = MHD_YES;
-          clen = MHD_lookup_connection_value (connection,
-                                              MHD_HEADER_KIND,
-                                              MHD_HTTP_HEADER_CONTENT_LENGTH);
-          if (clen != NULL)
-            {
-              if (1 != sscanf (clen, "%llu", &cval))
-                {
-#if HAVE_MESSAGES
-                  MHD_DLOG (connection->daemon,
-                            "Failed to parse `%s' header `%s', closing 
connection.\n",
-                            MHD_HTTP_HEADER_CONTENT_LENGTH, clen);
-#endif
-                  goto DIE;
-                }
-              connection->remaining_upload_size = cval;
-              connection->have_received_body = cval == 0 ? MHD_YES : MHD_NO;
-            }
-          else
-            {
-              if (NULL == MHD_lookup_connection_value (connection,
-                                                       MHD_HEADER_KIND,
-                                                       
MHD_HTTP_HEADER_TRANSFER_ENCODING))
-                {
-                  /* this request does not have a body */
-                  connection->remaining_upload_size = 0;
-                  connection->have_received_body = MHD_YES;
-                }
-              else
-                {
-                  if (connection->have_chunked_upload == MHD_NO)
-                    {
-                      connection->remaining_upload_size = -1;   /* unknown 
size */
-                      if (0 ==
-                          strcasecmp (MHD_lookup_connection_value
-                                      (connection, MHD_HEADER_KIND,
-                                       MHD_HTTP_HEADER_TRANSFER_ENCODING),
-                                      "chunked"))
-                        connection->have_chunked_upload = MHD_YES;
-                    }
-                  else
-                    {
-                      /* we were actually processing the footers at the
-                         END of a chunked encoding; give connection 
-                         handler an extra chance... */
-                      connection->have_chunked_upload = MHD_NO; /* no more! */
-                      MHD_call_connection_handler (connection);
-                    }
-                }
-            }
-          if ((0 != (MHD_USE_PEDANTIC_CHECKS & connection->daemon->options))
-              && (NULL != connection->version)
-              && (0 == strcasecmp (MHD_HTTP_VERSION_1_1, connection->version))
-              && (NULL ==
-                  MHD_lookup_connection_value (connection, MHD_HEADER_KIND,
-                                               MHD_HTTP_HEADER_HOST)))
-            {
-              /* die, http 1.1 request without host and we are pedantic */
-              connection->have_received_body = MHD_YES;
-              connection->read_close = MHD_YES;
-#if HAVE_MESSAGES
-              MHD_DLOG (connection->daemon,
-                        "Received `%s' request without `%s' header.\n",
-                        MHD_HTTP_VERSION_1_1, MHD_HTTP_HEADER_HOST);
-#endif
-              response =
-                MHD_create_response_from_data (strlen (REQUEST_LACKS_HOST),
-                                               REQUEST_LACKS_HOST, MHD_NO,
-                                               MHD_NO);
-              MHD_queue_response (connection, MHD_HTTP_BAD_REQUEST, response);
-              MHD_destroy_response (response);
-            }
-          break;
-        }
-      /* line should be normal header line, find colon */
-      colon = strstr (line, ":");
-      if (colon == NULL)
-        {
-          /* error in header line, die hard */
-#if HAVE_MESSAGES
-          MHD_DLOG (connection->daemon,
-                    "Received malformed line (no colon), closing 
connection.\n");
-#endif
-          goto DIE;
-        }
-      /* zero-terminate header */
-      colon[0] = '\0';
-      colon++;                  /* advance to value */
-      while ((colon[0] != '\0') && ((colon[0] == ' ') || (colon[0] == '\t')))
-        colon++;
-      /* we do the actual adding of the connection
-         header at the beginning of the while
-         loop since we need to be able to inspect
-         the *next* header line (in case it starts
-         with a space...) */
-      last = line;
-    }
-  if ((last != NULL) &&
-      (MHD_NO == MHD_connection_add_header (connection,
-                                            last, colon, MHD_HEADER_KIND)))
-    return;                     /* error */
-  MHD_parse_cookie_header (connection);
-  return;
-DIE:
-#if HAVE_MESSAGES
-  MHD_DLOG (connection->daemon,
-            "Closing connection (problem parsing headers)\n");
-#endif
-  connection_close_error (connection);
-}
-
-
-/**
- * Find the handler responsible for this request.
- */
-static struct MHD_Access_Handler *
-MHD_find_access_handler (struct MHD_Connection *connection)
-{
-  struct MHD_Access_Handler *pos;
-
-  pos = connection->daemon->handlers;
-  while (pos != NULL)
-    {
-      if (0 == strcmp (connection->url, pos->uri_prefix))
-        return pos;
-      pos = pos->next;
-    }
-  return &connection->daemon->default_handler;
-}
-
-
-/**
- * Call the handler of the application for this
- * connection.
- */
-void
-MHD_call_connection_handler (struct MHD_Connection *connection)
-{
-  struct MHD_Access_Handler *ah;
   unsigned int processed;
   unsigned int available;
   unsigned int used;
   int instant_retry;
   unsigned int i;
+  int malformed;
 
   if (connection->response != NULL)
-    return;                     /* already queued a response */
-  if (connection->have_received_headers == MHD_NO)
-    abort ();                   /* bad timing... */
+    return; /* already queued a response */
   do
     {
       instant_retry = MHD_NO;
       available = connection->read_buffer_offset;
-      if (connection->have_chunked_upload == MHD_YES)
+      if ( (connection->have_chunked_upload == MHD_YES) &&
+          (connection->remaining_upload_size == -1) )
         {
           if ((connection->current_chunk_offset ==
                connection->current_chunk_size)
@@ -876,18 +1044,17 @@
                 }
               if (i >= available)
                 return;         /* need more data... */
-              /* The following if-statement is a bit crazy -- we
-                 use the second clause only for the side-effect,
-                 0-terminating the buffer for the following sscanf
-                 attempts; yes, there should be only a single 
-                 "="-sign (assignment!) in the read_buffer[i]-line. */
-              if ((i >= 6) ||
-                  ((connection->read_buffer[i] = '\0')) ||
-                  ((1 != sscanf (connection->read_buffer,
-                                 "%X",
-                                 &connection->current_chunk_size)) &&
-                   (1 != sscanf (connection->read_buffer,
-                                 "%x", &connection->current_chunk_size))))
+             malformed = (i >= 6);
+             if (! malformed) 
+               {
+                  connection->read_buffer[i] = '\0';
+                 malformed = (1 != sscanf (connection->read_buffer,
+                                           "%X",
+                                           &connection->current_chunk_size)) &&
+                   (1 != sscanf (connection->read_buffer,
+                                 "%x", &connection->current_chunk_size));      
          
+               }
+              if (malformed)
                 {
                   /* malformed encoding */
 #if HAVE_MESSAGES
@@ -908,11 +1075,7 @@
               instant_retry = MHD_YES;
               if (connection->current_chunk_size == 0)
                 {
-                  /* we're back to reading HEADERS (footers!) */
-                  connection->have_received_body = MHD_YES;
                   connection->remaining_upload_size = 0;
-                  connection->have_received_headers = MHD_NO;
-                  MHD_parse_connection_headers (connection);
                   return;
                 }
               continue;
@@ -925,8 +1088,7 @@
           available = 0;
         }
       used = processed;
-      ah = MHD_find_access_handler (connection);
-      if (MHD_NO == ah->dh (ah->dh_cls,
+      if (MHD_NO == connection->daemon->default_handler 
(connection->daemon->default_handler_cls,
                             connection,
                             connection->url,
                             connection->method,
@@ -942,6 +1104,8 @@
           connection_close_error (connection);
           return;
         }
+      if (processed > used)
+       abort(); /* fatal client API violation! */
       if (processed != 0)
         instant_retry = MHD_NO; /* client did not process everything */
       used -= processed;
@@ -953,79 +1117,26 @@
                  &connection->read_buffer[used], processed + available);
       if (connection->remaining_upload_size != -1)
         connection->remaining_upload_size -= used;
-      connection->read_buffer_offset = processed + available;
-      if ((connection->remaining_upload_size == 0) ||
-          ((connection->read_buffer_offset == 0) &&
-           (connection->remaining_upload_size == -1)
-           && (connection->socket_fd == -1)))
-        {
-          connection->have_received_body = MHD_YES;
-          if (connection->read_buffer != NULL)
-            MHD_pool_reallocate (connection->pool,
-                                 connection->read_buffer,
-                                 (connection->read_buffer ==
-                                  NULL) ? 0 : connection->read_buffer_size +
-                                 1, 0);
-          connection->read_buffer_offset = 0;
-          connection->read_buffer_size = 0;
-          connection->read_buffer = NULL;
-        }
+      connection->read_buffer_offset = processed + available;      
     }
   while (instant_retry == MHD_YES);
 }
 
 /**
- * This function handles a particular connection when it has been
- * determined that there is data to be read off a socket. All implementations
- * (multithreaded, external select, internal select) call this function
- * to handle reads.
+ * Try reading data from the socket into the
+ * read buffer of the connection.
+ *
+ * @return MHD_YES if something changed,
+ *         MHD_NO if we were interrupted or if
+ *                no space was available
  */
-int
-MHD_connection_handle_read (struct MHD_Connection *connection)
+static int
+do_read(struct MHD_Connection * connection)
 {
   int bytes_read;
-  void *tmp;
-
-  if (connection->pool == NULL)
-    connection->pool = MHD_pool_create (connection->daemon->pool_size);
-  if (connection->pool == NULL)
-    {
-#if HAVE_MESSAGES
-      MHD_DLOG (connection->daemon, "Failed to create memory pool!\n");
-#endif
-      connection_close_error (connection);
-      return MHD_NO;
-    }
-  if ((connection->read_buffer_offset >= connection->read_buffer_size) &&
-      (connection->have_received_headers == MHD_NO))
-    {
-      /* need to grow read buffer */
-      tmp = MHD_pool_reallocate (connection->pool,
-                                 connection->read_buffer,
-                                 connection->read_buffer_size,
-                                 connection->read_buffer_size * 2 +
-                                 MHD_BUF_INC_SIZE + 1);
-      if (tmp == NULL)
-        {
-#if HAVE_MESSAGES
-          MHD_DLOG (connection->daemon,
-                    "Not enough memory for reading headers!\n");
-#endif
-          MHD_excessive_data_handler (connection,
-                                      MHD_HTTP_REQUEST_ENTITY_TOO_LARGE);
-          return MHD_NO;
-        }
-      connection->read_buffer = tmp;
-      connection->read_buffer_size =
-        connection->read_buffer_size * 2 + MHD_BUF_INC_SIZE;
-    }
-  if (connection->read_buffer_offset >= connection->read_buffer_size)
-    {
-#if HAVE_MESSAGES
-      MHD_DLOG (connection->daemon, "Unexpected call to %s.\n", __FUNCTION__);
-#endif
-      return MHD_NO;
-    }
+  
+  if (connection->read_buffer_size == connection->read_buffer_offset)
+    return MHD_NO;
   bytes_read = RECV (connection->socket_fd,
                      &connection->read_buffer[connection->read_buffer_offset],
                      connection->read_buffer_size -
@@ -1044,150 +1155,297 @@
   if (bytes_read == 0)
     {
       /* other side closed connection */
-      connection->read_close = MHD_YES;
-      if ((connection->have_received_headers == MHD_YES)
-          && (connection->read_buffer_offset > 0))
-        MHD_call_connection_handler (connection);
-#if DEBUG_CLOSE
+      connection->read_closed = MHD_YES;
+      return MHD_NO;
+    }
+  connection->read_buffer_offset += bytes_read;
+  return MHD_YES;
+}
+
+/**
+ * We have received (possibly the beginning of) a line in the
+ * header (or footer).  Validate (check for ":") and prepare
+ * to process.
+ */
+static void
+process_header_line(struct MHD_Connection * connection,
+                   char * line) 
+{
+  char *colon;
+
+  /* line should be normal header line, find colon */
+  colon = strstr (line, ":");
+  if (colon == NULL)
+    {
+      /* error in header line, die hard */
 #if HAVE_MESSAGES
       MHD_DLOG (connection->daemon,
-                "Shutting down connection for reading (other side closed 
connection)\n");
+               "Received malformed line (no colon), closing connection.\n");
 #endif
-#endif
-      shutdown (connection->socket_fd, SHUT_RD);
-      if ((connection->have_received_headers == MHD_NO) ||
-          (connection->have_received_body == MHD_NO))
-        {
-          /* no request => no response! */
-          CLOSE (connection->socket_fd);
-          connection->socket_fd = -1;
-        }
-      return MHD_YES;
+      connection->state = MHD_CONNECTION_CLOSED;
+      return;
     }
-  connection->read_buffer_offset += bytes_read;
-  if (connection->have_received_headers == MHD_NO)
-    MHD_parse_connection_headers (connection);
-  if ((connection->have_received_headers == MHD_YES)
-      && (connection->method != NULL))
-    MHD_call_connection_handler (connection);
-  return MHD_YES;
+  /* zero-terminate header */
+  colon[0] = '\0';
+  colon++;                  /* advance to value */
+  while ((colon[0] != '\0') && ((colon[0] == ' ') || (colon[0] == '\t')))
+    colon++;
+  /* we do the actual adding of the connection
+     header at the beginning of the while
+     loop since we need to be able to inspect
+     the *next* header line (in case it starts
+     with a space...) */
+  connection->last = line;
+  connection->colon = colon;
 }
 
 /**
- * Check if we need to set some additional headers
- * for http-compiliance.
+ * Process a header value that spans multiple lines.
+ * The previous line(s) are in connection->last.  
+ *
+ * @param line the current input line
+ * @param kind if the line is complete, add a header
+ *        of the given kind
  */
 static void
-MHD_add_extra_headers (struct MHD_Connection *connection)
+process_broken_line(struct MHD_Connection * connection,
+                   char * line,
+                   enum MHD_ValueKind kind) 
 {
-  const char *have;
-  char buf[128];
+  char * last;
+  char * tmp;
 
-  if (connection->response->total_size == -1)
+  last = connection->last;
+  if ((line[0] == ' ') || (line[0] == '\t'))
     {
-      have = MHD_get_response_header (connection->response,
-                                      MHD_HTTP_HEADER_CONNECTION);
-      if (have == NULL)
-        MHD_add_response_header (connection->response,
-                                 MHD_HTTP_HEADER_CONNECTION, "close");
+      /* value was continued on the next line, see
+        http://www.jmarshall.com/easy/http/ */
+      last = MHD_pool_reallocate (connection->pool,
+                                 last,
+                                 strlen (last) + 1,
+                                 strlen (line) + strlen (last) + 1);
+      if (last == NULL)
+       {
+         excessive_data_handler (connection,
+                                 MHD_HTTP_REQUEST_ENTITY_TOO_LARGE);
+         return;
+       }
+      tmp = line;
+      while ((tmp[0] == ' ') || (tmp[0] == '\t'))
+       tmp++;          /* skip whitespace at start of 2nd line */
+      strcat (last, tmp);
+      connection->last = last;
+      return;         /* possibly more than 2 lines... */
     }
-  else if (NULL == MHD_get_response_header (connection->response,
-                                            MHD_HTTP_HEADER_CONTENT_LENGTH))
+  if (MHD_NO == connection_add_header (connection,
+                                      last,
+                                      connection->colon,
+                                      kind))
     {
-      _REAL_SNPRINTF (buf,
-                      128,
-                      "%llu",
-                      (unsigned long long) connection->response->total_size);
-      MHD_add_response_header (connection->response,
-                               MHD_HTTP_HEADER_CONTENT_LENGTH, buf);
+      excessive_data_handler (connection,
+                             MHD_HTTP_REQUEST_ENTITY_TOO_LARGE);
+      return;
     }
+  /* we still have the current line to deal with... */
+  if (strlen(line) != 0)
+    process_header_line(connection,
+                       line);
 }
 
+/**
+ * Parse the various headers; figure out the size
+ * of the upload and make sure the headers follow
+ * the protocol.  Advance to the appropriate state.
+ */
 static void
-get_date_string (char *date, unsigned int max)
+parse_connection_headers(struct MHD_Connection * connection)
 {
-  static const char *days[] =
-    { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
-  static const char *mons[] =
-    { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct",
-    "Nov", "Dec"
-  };
-  struct tm now;
-  time_t t;
+  const char *clen;
+  unsigned long long cval;
+  struct MHD_Response *response;
 
-  time (&t);
-  gmtime_r (&t, &now);
-  snprintf (date,
-            max - 1,
-            "Date: %3s, %02u %3s %04u %02u:%02u:%02u GMT\r\n",
-            days[now.tm_wday % 7],
-            now.tm_mday,
-            mons[now.tm_mon % 12],
-            now.tm_year, now.tm_hour, now.tm_min, now.tm_sec);
+  parse_cookie_header (connection);
+  if ((0 != (MHD_USE_PEDANTIC_CHECKS & connection->daemon->options))
+      && (NULL != connection->version)
+      && (0 == strcasecmp (MHD_HTTP_VERSION_1_1, connection->version))
+      && (NULL ==
+         MHD_lookup_connection_value (connection, MHD_HEADER_KIND,
+                                      MHD_HTTP_HEADER_HOST)))
+    {
+      /* die, http 1.1 request without host and we are pedantic */
+      connection->state = MHD_CONNECTION_FOOTERS_RECEIVED;
+      connection->read_closed = MHD_YES;
+#if HAVE_MESSAGES
+      MHD_DLOG (connection->daemon,
+               "Received `%s' request without `%s' header.\n",
+               MHD_HTTP_VERSION_1_1, MHD_HTTP_HEADER_HOST);
+#endif
+      response =
+       MHD_create_response_from_data (strlen (REQUEST_LACKS_HOST),
+                                      REQUEST_LACKS_HOST, MHD_NO,
+                                      MHD_NO);
+      MHD_queue_response (connection, MHD_HTTP_BAD_REQUEST, response);
+      MHD_destroy_response (response);
+      return;
+    }
+
+  clen = MHD_lookup_connection_value (connection,
+                                     MHD_HEADER_KIND,
+                                     MHD_HTTP_HEADER_CONTENT_LENGTH);
+  if (clen != NULL)
+    {
+      if (1 != sscanf (clen, "%llu", &cval))
+       {
+#if HAVE_MESSAGES
+         MHD_DLOG (connection->daemon,
+                   "Failed to parse `%s' header `%s', closing connection.\n",
+                   MHD_HTTP_HEADER_CONTENT_LENGTH, clen);
+#endif
+         connection->state = MHD_CONNECTION_CLOSED;
+         return;
+       }
+      connection->remaining_upload_size = cval;
+    }
+  else
+    {
+      if (NULL == MHD_lookup_connection_value (connection,
+                                              MHD_HEADER_KIND,
+                                              
MHD_HTTP_HEADER_TRANSFER_ENCODING))
+       {
+         /* this request does not have a body */
+         connection->remaining_upload_size = 0;
+       }
+      else
+       {
+         connection->remaining_upload_size = -1;   /* unknown size */
+         if (0 ==
+             strcasecmp (MHD_lookup_connection_value
+                         (connection, MHD_HEADER_KIND,
+                          MHD_HTTP_HEADER_TRANSFER_ENCODING),
+                         "chunked"))
+           connection->have_chunked_upload = MHD_YES;  
+       }
+    }  
 }
 
 /**
- * Allocate the connection's write buffer and
- * fill it with all of the headers from the
- * HTTPd's response.
+ * This function handles a particular connection when it has been
+ * determined that there is data to be read off a socket. All
+ * implementations (multithreaded, external select, internal select)
+ * call this function to handle reads.
+ * 
+ * @return MHD_YES if we should continue to process the
+ *         connection (not dead yet), MHD_NO if it died
  */
+int
+MHD_connection_handle_read (struct MHD_Connection *connection)
+{
+  connection->last_activity = time(NULL);
+  if (connection->state == MHD_CONNECTION_CLOSED) 
+    return MHD_NO;
+  if (MHD_NO == do_read(connection))
+    return MHD_YES;
+  while (1) {
+#if DEBUG_STATES
+    fprintf(stderr,
+           "`%s' in state %u\n",
+           __FUNCTION__,
+           connection->state);
+#endif    
+    switch (connection->state)
+      {
+      case MHD_CONNECTION_INIT:
+      case MHD_CONNECTION_URL_RECEIVED:
+      case MHD_CONNECTION_HEADER_PART_RECEIVED:
+      case MHD_CONNECTION_HEADERS_RECEIVED:
+      case MHD_CONNECTION_HEADERS_PROCESSED:
+      case MHD_CONNECTION_CONTINUE_SENDING:
+      case MHD_CONNECTION_CONTINUE_SENT:
+      case MHD_CONNECTION_BODY_RECEIVED:
+      case MHD_CONNECTION_FOOTER_PART_RECEIVED:
+       /* nothing to do but default action */
+       if (MHD_YES == connection->read_closed) 
+         {
+           connection->state = MHD_CONNECTION_CLOSED;
+           continue;
+         }
+       break;
+      case MHD_CONNECTION_CLOSED:
+       if (connection->socket_fd != -1)
+         connection_close_error (connection);
+       return MHD_NO;
+      default:
+       /* shrink read buffer to how much is actually used */
+       MHD_pool_reallocate (connection->pool,
+                            connection->read_buffer,
+                            connection->read_buffer_size + 1,
+                            connection->read_buffer_offset);       
+       break;
+      }
+    break;
+  }
+  return MHD_YES;
+}
+
+/**
+ * Try writing data to the socket from the
+ * write buffer of the connection.
+ *
+ * @return MHD_YES if something changed,
+ *         MHD_NO if we were interrupted 
+ */
 static int
-MHD_build_header_response (struct MHD_Connection *connection)
+do_write(struct MHD_Connection * connection)
 {
-  size_t size;
-  size_t off;
-  struct MHD_HTTP_Header *pos;
-  char code[128];
-  char date[128];
-  char *data;
+  int ret;
 
-  MHD_add_extra_headers (connection);
-  const char *reason_phrase =
-    MHD_get_reason_phrase_for (connection->responseCode);
-  _REAL_SNPRINTF (code, 128, "%s %u %s\r\n", MHD_HTTP_VERSION_1_1,
-                  connection->responseCode, reason_phrase);
-  off = strlen (code);
-  /* estimate size */
-  size = off + 2;               /* extra \r\n at the end */
-  pos = connection->response->first_header;
-  while (pos != NULL)
+  ret = SEND (connection->socket_fd,
+             &connection->write_buffer[connection->
+                                       write_buffer_send_offset],
+             connection->write_buffer_append_offset -
+             connection->write_buffer_send_offset, MSG_NOSIGNAL);
+  if (ret < 0)
     {
-      size += strlen (pos->header) + strlen (pos->value) + 4;   /* colon, 
space, linefeeds */
-      pos = pos->next;
-    }
-  if (NULL == MHD_get_response_header (connection->response,
-                                       MHD_HTTP_HEADER_DATE))
-    get_date_string (date, sizeof (date));
-  else
-    date[0] = '\0';
-  size += strlen (date);
-  /* produce data */
-  data = MHD_pool_allocate (connection->pool, size + 1, MHD_YES);
-  if (data == NULL)
-    {
+      if (errno == EINTR)
+       return MHD_NO;
 #if HAVE_MESSAGES
-      MHD_DLOG (connection->daemon, "Not enough memory for write!\n");
+      MHD_DLOG (connection->daemon,
+               "Failed to send data: %s\n", STRERROR (errno));
 #endif
-      return MHD_NO;
+      connection_close_error (connection);
+      return MHD_YES;
     }
-  memcpy (data, code, off);
-  pos = connection->response->first_header;
-  while (pos != NULL)
-    {
-      SPRINTF (&data[off], "%s: %s\r\n", pos->header, pos->value);
-      off += strlen (pos->header) + strlen (pos->value) + 4;
-      pos = pos->next;
-    }
-  strcpy (&data[off], date);
-  off += strlen (date);
-  sprintf (&data[off], "\r\n");
-  off += 2;
-  if (off != size)
-    abort ();
-  connection->write_buffer = data;
-  connection->write_buffer_append_offset = size;
+#if DEBUG_SEND_DATA
+  fprintf (stderr,
+          "Sent HEADER response: `%.*s'\n",
+          ret,
+          &connection->write_buffer[connection->
+                                    write_buffer_send_offset]);
+#endif
+  connection->write_buffer_send_offset += ret;
+  return MHD_YES;
+}
+
+/**
+ * Check if we are done sending the write-buffer.
+ * If so, transition into "next_state".
+ * @return MHY_NO if we are not done, MHD_YES if we are
+ */
+static int
+check_write_done(struct MHD_Connection * connection,
+                enum MHD_CONNECTION_STATE next_state)
+{
+  if (connection->write_buffer_append_offset !=
+      connection->write_buffer_send_offset)
+    return MHD_NO;    
+  connection->write_buffer_append_offset = 0;
   connection->write_buffer_send_offset = 0;
-  connection->write_buffer_size = size + 1;
+  connection->state = next_state;
+  MHD_pool_reallocate (connection->pool,
+                      connection->write_buffer,
+                      connection->write_buffer_size, 0);
+  connection->write_buffer = NULL;
+  connection->write_buffer_size = 0;
   return MHD_YES;
 }
 
@@ -1196,207 +1454,414 @@
  * been determined that the socket can be written to. All
  * implementations (multithreaded, external select, internal select)
  * call this function
+ * 
+ * @return MHD_YES if we should continue to process the
+ *         connection (not dead yet), MHD_NO if it died
  */
 int
 MHD_connection_handle_write (struct MHD_Connection *connection)
 {
   struct MHD_Response *response;
   int ret;
-  const char *end;
 
-  if (MHD_need_100_continue (connection))
-    {
-      ret = SEND (connection->socket_fd,
-                  &HTTP_100_CONTINUE[connection->
-                                     continue_message_write_offset],
-                  strlen (HTTP_100_CONTINUE) -
-                  connection->continue_message_write_offset, MSG_NOSIGNAL);
-      if (ret < 0)
-        {
-          if (errno == EINTR)
-            return MHD_YES;
+  connection->last_activity = time(NULL);
+  while (1) {
+#if DEBUG_STATES
+    fprintf(stderr,
+           "`%s' in state %u\n",
+           __FUNCTION__,
+           connection->state);
+#endif    
+    switch (connection->state)
+      {
+      case MHD_CONNECTION_INIT:
+      case MHD_CONNECTION_URL_RECEIVED:
+      case MHD_CONNECTION_HEADER_PART_RECEIVED:
+      case MHD_CONNECTION_HEADERS_RECEIVED:
+       EXTRA_CHECK(0);
+       break;
+      case MHD_CONNECTION_HEADERS_PROCESSED:
+       break;
+      case MHD_CONNECTION_CONTINUE_SENDING:
+       ret = SEND (connection->socket_fd,
+                   &HTTP_100_CONTINUE[connection->
+                                      continue_message_write_offset],
+                   strlen (HTTP_100_CONTINUE) -
+                   connection->continue_message_write_offset, MSG_NOSIGNAL);
+       if (ret < 0)
+         {
+           if (errno == EINTR)
+             break;
 #if HAVE_MESSAGES
-          MHD_DLOG (connection->daemon,
-                    "Failed to send data: %s\n", STRERROR (errno));
+           MHD_DLOG (connection->daemon,
+                     "Failed to send data: %s\n", STRERROR (errno));
 #endif
-          connection_close_error (connection);
-          return MHD_YES;
-        }
+           connection_close_error (connection);
+           return MHD_NO;
+         }
 #if DEBUG_SEND_DATA
-      fprintf (stderr,
-               "Sent 100 continue response: `%.*s'\n",
-               ret,
-               &HTTP_100_CONTINUE[connection->continue_message_write_offset]);
+       fprintf (stderr,
+                "Sent 100 continue response: `%.*s'\n",
+                ret,
+                &HTTP_100_CONTINUE[connection->continue_message_write_offset]);
 #endif
-      connection->continue_message_write_offset += ret;
-      return MHD_YES;
-    }
-  response = connection->response;
-  if (response == NULL)
-    {
+       connection->continue_message_write_offset += ret;
+       break;
+      case MHD_CONNECTION_CONTINUE_SENT:
+      case MHD_CONNECTION_BODY_RECEIVED:
+      case MHD_CONNECTION_FOOTER_PART_RECEIVED:
+      case MHD_CONNECTION_FOOTERS_RECEIVED:
+       EXTRA_CHECK(0);
+       break;
+      case MHD_CONNECTION_HEADERS_SENDING:
+       do_write(connection);
+       check_write_done(connection,
+                        MHD_CONNECTION_HEADERS_SENT);
+       break;
+      case MHD_CONNECTION_HEADERS_SENT:
+       EXTRA_CHECK(0);
+       break;  
+      case MHD_CONNECTION_NORMAL_BODY_READY:
+       response = connection->response;
+       if (response->crc != NULL)
+         pthread_mutex_lock (&response->mutex);
+       if (MHD_YES != try_ready_normal_body(connection))
+         {
+           if (response->crc != NULL)
+             pthread_mutex_unlock (&response->mutex);
+           connection->state = MHD_CONNECTION_NORMAL_BODY_UNREADY;
+           break;
+         }
+       ret = SEND (connection->socket_fd,
+                   &response->data[connection->response_write_position -
+                                   response->data_start],
+                   response->data_size - (connection->response_write_position -
+                                          response->data_start), MSG_NOSIGNAL);
+#if DEBUG_SEND_DATA
+       if (ret > 0)
+         fprintf (stderr,
+                  "Sent DATA response: `%.*s'\n",
+                  ret,
+                  &response->data[connection->response_write_position -
+                                  response->data_start]);
+#endif 
+       if (response->crc != NULL)
+         pthread_mutex_unlock (&response->mutex);
+       if (ret < 0)
+         {
+           if (errno == EINTR)
+             return MHD_YES;
 #if HAVE_MESSAGES
-      MHD_DLOG (connection->daemon, "Unexpected call to %s.\n", __FUNCTION__);
+           MHD_DLOG (connection->daemon,
+                     "Failed to send data: %s\n", STRERROR (errno));
 #endif
-      return MHD_NO;
-    }
-  if (MHD_NO == connection->have_sent_headers)
-    {
-      if ((connection->write_buffer == NULL) &&
-          (MHD_NO == MHD_build_header_response (connection)))
-        {
-          /* oops - close! */
+           connection_close_error (connection);
+           return MHD_NO;
+         }
+       connection->response_write_position += ret;
+       if (connection->response_write_position == 
connection->response->total_size)
+         connection->state = MHD_CONNECTION_FOOTERS_SENT; /* have no 
footers... */
+       break;  
+      case MHD_CONNECTION_NORMAL_BODY_UNREADY:
+       EXTRA_CHECK(0);
+       break;
+      case MHD_CONNECTION_CHUNKED_BODY_READY:
+       do_write(connection);
+       check_write_done(connection,
+                        (connection->response->total_size == 
connection->response_write_position) 
+                        ? MHD_CONNECTION_BODY_SENT
+                        : MHD_CONNECTION_CHUNKED_BODY_UNREADY);
+       break;
+      case MHD_CONNECTION_CHUNKED_BODY_UNREADY:
+      case MHD_CONNECTION_BODY_SENT:
+       EXTRA_CHECK(0);
+       break;
+      case MHD_CONNECTION_FOOTERS_SENDING:
+       do_write(connection);
+       check_write_done(connection,
+                        MHD_CONNECTION_FOOTERS_SENT);
+       break;          
+      case MHD_CONNECTION_FOOTERS_SENT:
+       EXTRA_CHECK(0);
+       break;
+      case MHD_CONNECTION_CLOSED:
+       if (connection->socket_fd != -1) 
+         connection_close_error(connection);     
+       return MHD_NO;
+     }
+    break;
+  }
+  return MHD_YES;
+}
+
+/**
+ * This function was created to handle per-connection processing that
+ * has to happen even if the socket cannot be read or written to.  All
+ * implementations (multithreaded, external select, internal select)
+ * call this function.
+ * 
+ * @return MHD_YES if we should continue to process the
+ *         connection (not dead yet), MHD_NO if it died
+ */
+int
+MHD_connection_handle_idle (struct MHD_Connection *connection)
+{
+  unsigned int timeout;
+  const char * end;
+  char * line;
+
+  while (1) {
+#if DEBUG_STATES
+    fprintf(stderr,
+           "`%s' in state %u\n",
+           __FUNCTION__,
+           connection->state);
+#endif    
+    switch (connection->state)
+      {
+      case MHD_CONNECTION_INIT:
+       line = get_next_header_line(connection);
+       if (line == NULL)
+         break;
+       if (MHD_NO == parse_initial_message_line (connection, line))
+         connection->state = MHD_CONNECTION_CLOSED;
+       else
+         connection->state = MHD_CONNECTION_URL_RECEIVED;
+       continue;
+      case MHD_CONNECTION_URL_RECEIVED:
+       line = get_next_header_line(connection);
+       if (line == NULL)
+         break;
+       if (strlen(line) == 0)
+         {
+           connection->state = MHD_CONNECTION_HEADERS_RECEIVED;
+           continue;
+         }
+       process_header_line(connection, line);
+       connection->state = MHD_CONNECTION_HEADER_PART_RECEIVED;
+       continue;
+      case MHD_CONNECTION_HEADER_PART_RECEIVED:
+       line = get_next_header_line(connection);
+       if (line == NULL)
+         break;
+       process_broken_line(connection,
+                           line,
+                           MHD_HEADER_KIND);
+       if (strlen(line) == 0)
+         {
+           connection->state = MHD_CONNECTION_HEADERS_RECEIVED;
+           continue;
+         }
+       continue;
+      case MHD_CONNECTION_HEADERS_RECEIVED:
+       parse_connection_headers (connection);
+       if (connection->state == MHD_CONNECTION_CLOSED)
+         continue;
+       connection->state = MHD_CONNECTION_HEADERS_PROCESSED;
+       continue;
+      case MHD_CONNECTION_HEADERS_PROCESSED:
+       call_connection_handler (connection); /* first call */
+       if (need_100_continue(connection)) 
+         {
+           connection->state = MHD_CONNECTION_CONTINUE_SENDING;
+           break;
+         }
+       connection->state = (connection->remaining_upload_size == 0)
+         ? MHD_CONNECTION_FOOTERS_RECEIVED 
+         : MHD_CONNECTION_CONTINUE_SENT;
+       continue;
+      case MHD_CONNECTION_CONTINUE_SENDING:
+       if (connection->continue_message_write_offset ==
+           strlen (HTTP_100_CONTINUE))
+         {
+           connection->state = MHD_CONNECTION_CONTINUE_SENT;
+           continue;
+         }
+       break;
+      case MHD_CONNECTION_CONTINUE_SENT:
+       if (connection->read_buffer_offset != 0) {
+         call_connection_handler(connection); /* loop call */
+         if (connection->state == MHD_CONNECTION_CLOSED)
+           continue;
+       }
+       if ( (connection->remaining_upload_size == 0) ||
+            ( (connection->remaining_upload_size == -1) &&
+              (connection->read_buffer_offset == 0) &&
+              (MHD_YES == connection->read_closed) ) )
+         {
+           if ( (MHD_YES == connection->have_chunked_upload) &&
+                (MHD_NO == connection->read_closed) ) 
+             connection->state = MHD_CONNECTION_BODY_RECEIVED;
+           else
+             connection->state = MHD_CONNECTION_FOOTERS_RECEIVED;
+           continue;
+         }
+       break;
+      case MHD_CONNECTION_BODY_RECEIVED:
+       line = get_next_header_line(connection);
+       if (line == NULL)
+         break;
+       if (strlen(line) == 0)
+         {
+           connection->state = MHD_CONNECTION_FOOTERS_RECEIVED;
+           continue;
+         }
+       process_header_line(connection, line);
+       connection->state = MHD_CONNECTION_FOOTER_PART_RECEIVED;
+       continue;
+      case MHD_CONNECTION_FOOTER_PART_RECEIVED:
+       line = get_next_header_line(connection);
+       if (line == NULL)
+         break;
+       process_broken_line(connection,
+                           line,
+                           MHD_FOOTER_KIND);
+       if (strlen(line) == 0)
+         {
+           connection->state = MHD_CONNECTION_FOOTERS_RECEIVED;
+           continue;
+         }
+       continue;
+      case MHD_CONNECTION_FOOTERS_RECEIVED:    
+       call_connection_handler (connection); /* "final" call */
+       if (connection->state == MHD_CONNECTION_CLOSED)
+         continue;
+       if (connection->response == NULL) 
+         break; /* try again next time */        
+       if (MHD_NO == build_header_response (connection))
+         {
+           /* oops - close! */
 #if HAVE_MESSAGES
-          MHD_DLOG (connection->daemon,
-                    "Closing connection (failed to create response header)\n");
+           MHD_DLOG (connection->daemon,
+                     "Closing connection (failed to create response 
header)\n");
 #endif
-          connection_close_error (connection);
-          return MHD_NO;
-        }
-      ret = SEND (connection->socket_fd,
-                  &connection->write_buffer[connection->
-                                            write_buffer_send_offset],
-                  connection->write_buffer_append_offset -
-                  connection->write_buffer_send_offset, MSG_NOSIGNAL);
-      if (ret < 0)
-        {
-          if (errno == EINTR)
-            return MHD_YES;
-#if HAVE_MESSAGES
-          MHD_DLOG (connection->daemon,
-                    "Failed to send data: %s\n", STRERROR (errno));
-#endif
-          connection_close_error (connection);
-          return MHD_YES;
-        }
-#if DEBUG_SEND_DATA
-      fprintf (stderr,
-               "Sent HEADER response: `%.*s'\n",
-               ret,
-               &connection->write_buffer[connection->
-                                         write_buffer_send_offset]);
-#endif
-      connection->write_buffer_send_offset += ret;
-      if (connection->write_buffer_append_offset ==
-          connection->write_buffer_send_offset)
-        {
-          connection->write_buffer_append_offset = 0;
-          connection->write_buffer_send_offset = 0;
-          connection->have_sent_headers = MHD_YES;
-          MHD_pool_reallocate (connection->pool,
-                               connection->write_buffer,
-                               connection->write_buffer_size, 0);
-          connection->write_buffer = NULL;
-          connection->write_buffer_size = 0;
-        }
-      return MHD_YES;
-    }
-  if (response->total_size < connection->response_write_position)
-    abort ();                   /* internal error */
-  if (response->crc != NULL)
-    pthread_mutex_lock (&response->mutex);
-
-  /* prepare send buffer */
-  if ((response->crc != NULL) &&
-      ((response->data_start > connection->response_write_position) ||
-       (response->data_start + response->data_size <=
-        connection->response_write_position))
-      && (MHD_YES != ready_response (connection)))
+           connection->state = MHD_CONNECTION_CLOSED;
+           continue;           
+         }
+       connection->state = MHD_CONNECTION_HEADERS_SENDING;
+       break;
+      case MHD_CONNECTION_HEADERS_SENDING:
+       /* no default action */
+       break;
+      case MHD_CONNECTION_HEADERS_SENT:
+       if (connection->have_chunked_upload)
+         connection->state = MHD_CONNECTION_CHUNKED_BODY_UNREADY;
+       else
+         connection->state = MHD_CONNECTION_NORMAL_BODY_UNREADY;
+       continue;
+      case MHD_CONNECTION_NORMAL_BODY_READY:
+       /* nothing to do here */
+       break;
+      case MHD_CONNECTION_NORMAL_BODY_UNREADY:
+       if (connection->response->crc != NULL)
+         pthread_mutex_lock (&connection->response->mutex);
+       if (MHD_YES == try_ready_normal_body(connection))
+         {
+           if (connection->response->crc != NULL)
+             pthread_mutex_unlock (&connection->response->mutex);
+           connection->state = MHD_CONNECTION_NORMAL_BODY_READY;
+           break;
+         }     
+       if (connection->response->crc != NULL)
+         pthread_mutex_unlock (&connection->response->mutex);
+       /* not ready, no socket action */
+       break;
+      case MHD_CONNECTION_CHUNKED_BODY_READY:
+       /* nothing to do here */
+       break;
+      case MHD_CONNECTION_CHUNKED_BODY_UNREADY:        
+       if (connection->response->crc != NULL)
+         pthread_mutex_lock (&connection->response->mutex);
+       if (MHD_YES == try_ready_chunked_body(connection))
+         {
+           if (connection->response->crc != NULL)
+             pthread_mutex_unlock (&connection->response->mutex);
+           connection->state = MHD_CONNECTION_CHUNKED_BODY_READY;
+           continue;
+         }
+       if (connection->response->crc != NULL)
+         pthread_mutex_unlock (&connection->response->mutex);
+       break;
+      case MHD_CONNECTION_BODY_SENT:
+       build_header_response(connection);
+       if (connection->write_buffer_send_offset  == 
connection->write_buffer_append_offset)
+         connection->state = MHD_CONNECTION_FOOTERS_SENT;
+       else
+         connection->state = MHD_CONNECTION_FOOTERS_SENDING;
+       continue;
+      case MHD_CONNECTION_FOOTERS_SENDING:
+       /* no default action */
+       break;
+      case MHD_CONNECTION_FOOTERS_SENT:
+       MHD_destroy_response (connection->response);
+       if (connection->daemon->notify_completed != NULL)
+         connection->daemon->notify_completed (connection->daemon->
+                                               notify_completed_cls,
+                                               connection,
+                                               &connection->client_context,
+                                               
MHD_REQUEST_TERMINATED_COMPLETED_OK);
+       end = MHD_lookup_connection_value (connection,
+                                          MHD_HEADER_KIND,
+                                          MHD_HTTP_HEADER_CONNECTION);
+       connection->client_context = NULL;
+       connection->continue_message_write_offset = 0;
+       connection->responseCode = 0;
+       connection->response = NULL;
+       connection->headers_received = NULL;
+       connection->response_write_position = 0;
+       connection->have_chunked_upload = MHD_NO;
+       connection->method = NULL;
+       connection->url = NULL;
+       connection->write_buffer = NULL;
+       connection->write_buffer_size = 0;
+       connection->write_buffer_send_offset = 0;
+       connection->write_buffer_append_offset = 0;
+       if ((end != NULL) && (0 == strcasecmp (end, "close")))
+         {
+           connection->read_closed = MHD_YES;    
+           connection->read_buffer_offset = 0;
+         }
+       if ( ( (MHD_YES == connection->read_closed) &&
+              (0 == connection->read_buffer_offset) ) ||
+            (connection->version == NULL) ||
+            (0 != strcasecmp (MHD_HTTP_VERSION_1_1, connection->version)) )
+         {
+           connection->state = MHD_CONNECTION_CLOSED;
+           MHD_pool_destroy (connection->pool);
+           connection->pool = NULL; 
+           connection->read_buffer = NULL;
+           connection->read_buffer_size = 0;
+           connection->read_buffer_offset = 0;
+         }
+       else
+         {             
+           connection->version = NULL;
+           connection->state = MHD_CONNECTION_INIT;
+           connection->read_buffer 
+             = MHD_pool_reset(connection->pool,
+                              connection->read_buffer,
+                              connection->read_buffer_size);
+         }
+       continue;
+      case MHD_CONNECTION_CLOSED:
+       if (connection->socket_fd != -1)
+         connection_close_error (connection);
+       break;
+      default:
+       EXTRA_CHECK(0);
+       break;
+      }
+    break;
+  } 
+  timeout = connection->daemon->connection_timeout;
+  if ( (timeout != 0) &&
+       (time(NULL) - timeout > connection->last_activity) )
     {
-      pthread_mutex_unlock (&response->mutex);
-      return MHD_YES;
-    }
-  /* transmit */
-  ret = SEND (connection->socket_fd,
-              &response->data[connection->response_write_position -
-                              response->data_start],
-              response->data_size - (connection->response_write_position -
-                                     response->data_start), MSG_NOSIGNAL);
-  if (response->crc != NULL)
-    pthread_mutex_unlock (&response->mutex);
-  if (ret < 0)
-    {
-      if (errno == EINTR)
-        return MHD_YES;
-#if HAVE_MESSAGES
-      MHD_DLOG (connection->daemon,
-                "Failed to send data: %s\n", STRERROR (errno));
-#endif
       connection_close_error (connection);
-      return MHD_YES;
+      return MHD_NO;  
     }
-#if DEBUG_SEND_DATA
-  fprintf (stderr,
-           "Sent DATA response: `%.*s'\n",
-           ret,
-           &response->data[connection->response_write_position -
-                           response->data_start]);
-#endif
-  connection->response_write_position += ret;
-  if (connection->response_write_position > response->total_size)
-    abort ();                   /* internal error */
-  if (connection->response_write_position == response->total_size)
-    {
-      if ((connection->have_received_body == MHD_NO) ||
-          (connection->have_received_headers == MHD_NO))
-        abort ();               /* internal error */
-      MHD_destroy_response (response);
-      if (connection->daemon->notify_completed != NULL)
-        connection->daemon->notify_completed (connection->daemon->
-                                              notify_completed_cls,
-                                              connection,
-                                              &connection->client_context,
-                                              
MHD_REQUEST_TERMINATED_COMPLETED_OK);
-      end = MHD_lookup_connection_value (connection,
-                                         MHD_HEADER_KIND,
-                                         MHD_HTTP_HEADER_CONNECTION);
-      connection->client_context = NULL;
-      connection->continue_message_write_offset = 0;
-      connection->responseCode = 0;
-      connection->response = NULL;
-      connection->headers_received = NULL;
-      connection->have_received_headers = MHD_NO;
-      connection->have_sent_headers = MHD_NO;
-      connection->have_received_body = MHD_NO;
-      connection->response_write_position = 0;
-      connection->have_chunked_upload = MHD_NO;
-      connection->method = NULL;
-      connection->url = NULL;
-      if ((end != NULL) && (0 == strcasecmp (end, "close")))
-        {
-          /* other side explicitly requested
-             that we close the connection after
-             this request */
-          connection->read_close = MHD_YES;
-        }
-      if ((connection->read_close == MHD_YES) ||
-          (0 != strcasecmp (MHD_HTTP_VERSION_1_1, connection->version)))
-        {
-          /* closed for reading => close for good! */
-          if (connection->socket_fd != -1)
-            {
-#if DEBUG_CLOSE
-#if HAVE_MESSAGES
-              MHD_DLOG (connection->daemon,
-                        "Closing connection (http 1.0 or end-of-stream for 
unknown content length)\n");
-#endif
-#endif
-              SHUTDOWN (connection->socket_fd, SHUT_RDWR);
-              CLOSE (connection->socket_fd);
-            }
-          connection->socket_fd = -1;
-        }
-      connection->version = NULL;
-      connection->read_buffer = NULL;
-      connection->write_buffer = NULL;
-      connection->read_buffer_size = 0;
-      connection->read_buffer_offset = 0;
-      connection->write_buffer_size = 0;
-      connection->write_buffer_send_offset = 0;
-      connection->write_buffer_append_offset = 0;
-      MHD_pool_destroy (connection->pool);
-      connection->pool = NULL;
-    }
   return MHD_YES;
+
 }
 
 /* end of connection.c */

Modified: libmicrohttpd/src/daemon/connection.h
===================================================================
--- libmicrohttpd/src/daemon/connection.h       2007-12-20 00:40:49 UTC (rev 
5979)
+++ libmicrohttpd/src/daemon/connection.h       2007-12-20 04:23:00 UTC (rev 
5980)
@@ -39,18 +39,14 @@
                           fd_set * write_fd_set,
                           fd_set * except_fd_set, int *max_fd);
 
-
 /**
- * Call the handler of the application for this
- * connection.
- */
-void MHD_call_connection_handler (struct MHD_Connection *connection);
-
-/**
  * This function handles a particular connection when it has been
  * determined that there is data to be read off a socket. All implementations
  * (multithreaded, external select, internal select) call this function
  * to handle reads.
+ * 
+ * @return MHD_YES if we should continue to process the
+ *         connection (not dead yet), MHD_NO if it died
  */
 int MHD_connection_handle_read (struct MHD_Connection *connection);
 
@@ -60,8 +56,23 @@
  * determined that the socket can be written to. If there is no data
  * to be written, however, the function call does nothing. All implementations
  * (multithreaded, external select, internal select) call this function
+ * 
+ * @return MHD_YES if we should continue to process the
+ *         connection (not dead yet), MHD_NO if it died
  */
 int MHD_connection_handle_write (struct MHD_Connection *connection);
 
 
+/**
+ * This function was created to handle per-connection processing that
+ * has to happen even if the socket cannot be read or written to.  All
+ * implementations (multithreaded, external select, internal select)
+ * call this function.
+ * 
+ * @return MHD_YES if we should continue to process the
+ *         connection (not dead yet), MHD_NO if it died
+ */
+int
+MHD_connection_handle_idle (struct MHD_Connection *connection);
+  
 #endif

Modified: libmicrohttpd/src/daemon/daemon.c
===================================================================
--- libmicrohttpd/src/daemon/daemon.c   2007-12-20 00:40:49 UTC (rev 5979)
+++ libmicrohttpd/src/daemon/daemon.c   2007-12-20 04:23:00 UTC (rev 5980)
@@ -52,87 +52,8 @@
  */
 #define DEBUG_CONNECT MHD_NO
 
-/**
- * Register an access handler for all URIs beginning with uri_prefix.
- *
- * @param uri_prefix
- * @return MRI_NO if a handler for this exact prefix
- *         already exists
- */
-int
-MHD_register_handler (struct MHD_Daemon *daemon,
-                      const char *uri_prefix,
-                      MHD_AccessHandlerCallback dh, void *dh_cls)
-{
-  struct MHD_Access_Handler *ah;
 
-  if ((daemon == NULL) || (uri_prefix == NULL) || (dh == NULL))
-    return MHD_NO;
-  ah = daemon->handlers;
-  while (ah != NULL)
-    {
-      if (0 == strcmp (uri_prefix, ah->uri_prefix))
-        return MHD_NO;
-      ah = ah->next;
-    }
-  ah = malloc (sizeof (struct MHD_Access_Handler));
-  if (ah == NULL)
-    {
-#if HAVE_MESSAGES
-      MHD_DLOG (daemon, "Error allocating memory: %s\n", STRERROR (errno));
-#endif
-      return MHD_NO;
-    }
-
-  ah->next = daemon->handlers;
-  ah->uri_prefix = strdup (uri_prefix);
-  ah->dh = dh;
-  ah->dh_cls = dh_cls;
-  daemon->handlers = ah;
-  return MHD_YES;
-}
-
-
 /**
- * Unregister an access handler for the URIs beginning with
- * uri_prefix.
- *
- * @param uri_prefix
- * @return MHD_NO if a handler for this exact prefix
- *         is not known for this daemon
- */
-int
-MHD_unregister_handler (struct MHD_Daemon *daemon,
-                        const char *uri_prefix,
-                        MHD_AccessHandlerCallback dh, void *dh_cls)
-{
-  struct MHD_Access_Handler *prev;
-  struct MHD_Access_Handler *pos;
-
-  if ((daemon == NULL) || (uri_prefix == NULL) || (dh == NULL))
-    return MHD_NO;
-  pos = daemon->handlers;
-  prev = NULL;
-  while (pos != NULL)
-    {
-      if ((dh == pos->dh) &&
-          (dh_cls == pos->dh_cls) &&
-          (0 == strcmp (uri_prefix, pos->uri_prefix)))
-        {
-          if (prev == NULL)
-            daemon->handlers = pos->next;
-          else
-            prev->next = pos->next;
-          free (pos);
-          return MHD_YES;
-        }
-      prev = pos;
-      pos = pos->next;
-    }
-  return MHD_NO;
-}
-
-/**
  * Obtain the select sets for this daemon.
  *
  * @return MHD_YES on success, MHD_NO if this
@@ -175,7 +96,6 @@
   return MHD_YES;
 }
 
-
 /**
  * Main function of the thread that handles an individual
  * connection.
@@ -191,26 +111,27 @@
   int max;
   struct timeval tv;
   unsigned int timeout;
-  time_t now;
+  unsigned int now;
 
   if (con == NULL)
     abort ();
   timeout = con->daemon->connection_timeout;
-  now = time (NULL);
-  while ((!con->daemon->shutdown) &&
-         (con->socket_fd != -1) &&
-         ((timeout == 0) || (now - timeout > con->last_activity)))
+  while ( (!con->daemon->shutdown) &&
+         (con->socket_fd != -1) )
     {
       FD_ZERO (&rs);
       FD_ZERO (&ws);
       FD_ZERO (&es);
       max = 0;
       MHD_connection_get_fdset (con, &rs, &ws, &es, &max);
+      now = time(NULL);      
       tv.tv_usec = 0;
-      tv.tv_sec = timeout - (now - con->last_activity);
+      if ( timeout > (now - con->last_activity) )
+       tv.tv_sec = timeout - (now - con->last_activity);
+      else
+       tv.tv_sec = 0;
       num_ready = SELECT (max + 1,
                           &rs, &ws, &es, (timeout != 0) ? &tv : NULL);
-      now = time (NULL);
       if (num_ready < 0)
         {
           if (errno == EINTR)
@@ -221,18 +142,13 @@
 #endif
           break;
         }
-      if (((FD_ISSET (con->socket_fd, &rs)) &&
-           (MHD_YES != MHD_connection_handle_read (con))) ||
-          ((con->socket_fd != -1) &&
-           (FD_ISSET (con->socket_fd, &ws)) &&
-           (MHD_YES != MHD_connection_handle_write (con))))
-        break;
-      if ((con->have_received_headers == MHD_YES) && (con->response == NULL))
-        MHD_call_connection_handler (con);
+      if (FD_ISSET (con->socket_fd, &rs))
+          MHD_connection_handle_read (con);
       if ((con->socket_fd != -1) &&
-          ((FD_ISSET (con->socket_fd, &rs)) ||
-           (FD_ISSET (con->socket_fd, &ws))))
-        con->last_activity = now;
+          (FD_ISSET (con->socket_fd, &ws)) )
+       MHD_connection_handle_write (con);
+      if (con->socket_fd != -1) 
+       MHD_connection_handle_idle (con);
     }
   if (con->socket_fd != -1)
     {
@@ -370,11 +286,6 @@
  * Free resources associated with all closed connections.
  * (destroy responses, free buffers, etc.).  A connection
  * is known to be closed if the socket_fd is -1.
- *
- * Also performs connection actions that need to be run
- * even if the connection is not selectable (such as
- * calling the application again with upload data when
- * the upload data buffer is full).
  */
 static void
 MHD_cleanup_connections (struct MHD_Daemon *daemon)
@@ -382,33 +293,11 @@
   struct MHD_Connection *pos;
   struct MHD_Connection *prev;
   void *unused;
-  time_t timeout;
 
-  timeout = time (NULL);
-  if (daemon->connection_timeout != 0)
-    timeout -= daemon->connection_timeout;
-  else
-    timeout = 0;
   pos = daemon->connections;
   prev = NULL;
   while (pos != NULL)
     {
-      if ((pos->last_activity < timeout) && (pos->socket_fd != -1))
-        {
-#if DEBUG_CLOSE
-#if HAVE_MESSAGES
-          MHD_DLOG (daemon, "Connection timed out, closing connection\n");
-#endif
-#endif
-          SHUTDOWN (pos->socket_fd, SHUT_RDWR);
-          CLOSE (pos->socket_fd);
-          pos->socket_fd = -1;
-          if (pos->daemon->notify_completed != NULL)
-            pos->daemon->notify_completed (pos->daemon->notify_completed_cls,
-                                           pos,
-                                           &pos->client_context,
-                                           
MHD_REQUEST_TERMINATED_TIMEOUT_REACHED);
-        }
       if (pos->socket_fd == -1)
         {
           if (prev == NULL)
@@ -420,8 +309,7 @@
               pthread_kill (pos->pid, SIGALRM);
               pthread_join (pos->pid, &unused);
             }
-          if (pos->response != NULL)
-            MHD_destroy_response (pos->response);
+         MHD_destroy_response (pos->response);
           MHD_pool_destroy (pos->pool);
           free (pos->addr);
           free (pos);
@@ -432,12 +320,6 @@
             pos = prev->next;
           continue;
         }
-
-      if ((0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
-          ((pos->have_received_headers == MHD_YES)
-           && (pos->response == NULL)))
-        MHD_call_connection_handler (pos);
-
       prev = pos;
       pos = pos->next;
     }
@@ -573,15 +455,12 @@
           if (ds != -1)
             {
               if (FD_ISSET (ds, &rs))
-                {
-                  pos->last_activity = now;
-                  MHD_connection_handle_read (pos);
-                }
-              if (FD_ISSET (ds, &ws))
-                {
-                  pos->last_activity = now;
-                  MHD_connection_handle_write (pos);
-                }
+               MHD_connection_handle_read (pos); 
+             if ( (pos->socket_fd != -1) &&
+                  (FD_ISSET (ds, &ws)) )
+               MHD_connection_handle_write (pos);
+             if (pos->socket_fd != -1)
+               MHD_connection_handle_idle(pos); 
             }
           pos = pos->next;
         }
@@ -726,10 +605,8 @@
   retVal->apc = apc;
   retVal->apc_cls = apc_cls;
   retVal->socket_fd = socket_fd;
-  retVal->default_handler.dh = dh;
-  retVal->default_handler.dh_cls = dh_cls;
-  retVal->default_handler.uri_prefix = "";
-  retVal->default_handler.next = NULL;
+  retVal->default_handler = dh;
+  retVal->default_handler_cls = dh_cls;
   retVal->max_connections = MHD_MAX_CONNECTIONS_DEFAULT;
   retVal->pool_size = MHD_POOL_SIZE_DEFAULT;
   retVal->connection_timeout = 0;       /* no timeout */

Modified: libmicrohttpd/src/daemon/daemontest_get.c
===================================================================
--- libmicrohttpd/src/daemon/daemontest_get.c   2007-12-20 00:40:49 UTC (rev 
5979)
+++ libmicrohttpd/src/daemon/daemontest_get.c   2007-12-20 04:23:00 UTC (rev 
5980)
@@ -66,16 +66,24 @@
           const char *upload_data, unsigned int *upload_data_size,
           void **unused)
 {
+  static int ptr;
   const char *me = cls;
   struct MHD_Response *response;
   int ret;
 
   if (0 != strcmp (me, method))
     return MHD_NO;              /* unexpected method */
+  if (&ptr != *unused) {
+    *unused = &ptr;
+    return MHD_YES;
+  }
+  *unused = NULL;
   response = MHD_create_response_from_data (strlen (url),
                                             (void *) url, MHD_NO, MHD_YES);
   ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
   MHD_destroy_response (response);
+  if (ret == MHD_NO)
+    abort();
   return ret;
 }
 
@@ -93,11 +101,11 @@
   cbc.size = 2048;
   cbc.pos = 0;
   d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
-                        1080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
+                        11080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
   if (d == NULL)
     return 1;
   c = curl_easy_init ();
-  curl_easy_setopt (c, CURLOPT_URL, "http://localhost:1080/hello_world";);
+  curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11080/hello_world";);
   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
@@ -150,12 +158,12 @@
   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
-  curl_easy_setopt (c, CURLOPT_TIMEOUT, 2L);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
   if (oneone)
     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
   else
     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
-  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 2L);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 15L);
   // NOTE: use of CONNECTTIMEOUT without also
   //   setting NOSIGNAL results in really weird
   //   crashes on my system!
@@ -214,8 +222,8 @@
     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
   else
     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
-  curl_easy_setopt (c, CURLOPT_TIMEOUT, 5L);
-  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 5L);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 15L);
   // NOTE: use of CONNECTTIMEOUT without also
   //   setting NOSIGNAL results in really weird
   //   crashes on my system!

Modified: libmicrohttpd/src/daemon/daemontest_large_put.c
===================================================================
--- libmicrohttpd/src/daemon/daemontest_large_put.c     2007-12-20 00:40:49 UTC 
(rev 5979)
+++ libmicrohttpd/src/daemon/daemontest_large_put.c     2007-12-20 04:23:00 UTC 
(rev 5980)
@@ -96,8 +96,16 @@
     return MHD_NO;              /* unexpected method */
   if ((*done) == 0)
     {
-      if (*upload_data_size != PUT_SIZE)
-        return MHD_YES;         /* not yet ready */
+      if (*upload_data_size != PUT_SIZE) 
+       {
+#if 0
+         fprintf(stderr,
+                 "Waiting for more data (%u/%u)...\n",
+                 *upload_data_size,
+                 PUT_SIZE);
+#endif
+         return MHD_YES;         /* not yet ready */
+       }
       if (0 == memcmp (upload_data, put_buffer, PUT_SIZE))
         {
           *upload_data_size = 0;
@@ -227,7 +235,13 @@
   curl_easy_cleanup (c);
   MHD_stop_daemon (d);
   if (cbc.pos != strlen ("/hello_world"))
-    return 64;
+    {
+      fprintf(stderr,
+             "Got invalid response `%.*s'\n",
+             cbc.pos,
+             cbc.buf);
+      return 64;
+    }
   if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
     return 128;
   return 0;
@@ -260,7 +274,10 @@
   multi = NULL;
   d = MHD_start_daemon (MHD_USE_DEBUG,
                         1082,
-                        NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END);
+                        NULL, NULL, &ahc_echo, &done_flag, 
+                       MHD_OPTION_CONNECTION_MEMORY_LIMIT,
+                       PUT_SIZE * 4,
+                       MHD_OPTION_END);
   if (d == NULL)
     return 256;
   c = curl_easy_init ();
@@ -357,9 +374,15 @@
     }
   MHD_stop_daemon (d);
   if (cbc.pos != strlen ("/hello_world"))
-    return 64;
+    {
+      fprintf(stderr,
+             "Got invalid response `%.*s'\n",
+             cbc.pos,
+             cbc.buf);
+      return 8192;
+    }
   if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
-    return 128;
+    return 16384;
   return 0;
 }
 
@@ -380,7 +403,7 @@
       errorCount += testInternalPut ();
       errorCount += testMultithreadedPut ();
     }
-  errorCount += testExternalPut ();
+  errorCount += testExternalPut (); 
   free (put_buffer);
   if (errorCount != 0)
     fprintf (stderr, "Error (code: %u)\n", errorCount);

Modified: libmicrohttpd/src/daemon/daemontest_postform.c
===================================================================
--- libmicrohttpd/src/daemon/daemontest_postform.c      2007-12-20 00:40:49 UTC 
(rev 5979)
+++ libmicrohttpd/src/daemon/daemontest_postform.c      2007-12-20 04:23:00 UTC 
(rev 5980)
@@ -72,6 +72,13 @@
 {
   int *eok = cls;
 
+#if 0
+  fprintf(stderr,
+         "PI sees %s-%.*s\n",
+         key,
+         size,
+         value);
+#endif
   if ((0 == strcmp (key, "name")) &&
       (size == strlen ("daniel")) && (0 == strncmp (value, "daniel", size)))
     (*eok) |= 1;
@@ -95,7 +102,7 @@
   struct MHD_PostProcessor *pp;
   int ret;
 
-  if (0 != strcmp ("POST", method))
+  if (0 != strcmp ("POST", method)) 
     {
       printf ("METHOD: %s\n", method);
       return MHD_NO;            /* unexpected method */

Modified: libmicrohttpd/src/daemon/daemontest_put_chunked.c
===================================================================
--- libmicrohttpd/src/daemon/daemontest_put_chunked.c   2007-12-20 00:40:49 UTC 
(rev 5979)
+++ libmicrohttpd/src/daemon/daemontest_put_chunked.c   2007-12-20 04:23:00 UTC 
(rev 5980)
@@ -105,6 +105,11 @@
           printf ("Invalid upload data `%8s'!\n", upload_data);
           return MHD_NO;
         }
+#if 0
+      fprintf(stderr,
+             "Not ready for response: %u/%u\n",
+             *done, 8);
+#endif
       return MHD_YES;
     }
   response = MHD_create_response_from_data (strlen (url),

Modified: libmicrohttpd/src/daemon/fileserver_example.c
===================================================================
--- libmicrohttpd/src/daemon/fileserver_example.c       2007-12-20 00:40:49 UTC 
(rev 5979)
+++ libmicrohttpd/src/daemon/fileserver_example.c       2007-12-20 04:23:00 UTC 
(rev 5980)
@@ -52,8 +52,9 @@
           const char *url,
           const char *method,
           const char *upload_data,
-          const char *version, unsigned int *upload_data_size, void **unused)
+          const char *version, unsigned int *upload_data_size, void **ptr)
 {
+  static int aptr;
   struct MHD_Response *response;
   int ret;
   FILE *file;
@@ -61,6 +62,13 @@
 
   if (0 != strcmp (method, "GET"))
     return MHD_NO;              /* unexpected method */
+   if (&aptr != *ptr) 
+    {
+      /* do never respond on first call */
+      *ptr = &aptr;
+      return MHD_YES;
+    }
+  *ptr = NULL; /* reset when done */
   file = fopen (&url[1], "r");
   if (file == NULL)
     {

Modified: libmicrohttpd/src/daemon/internal.h
===================================================================
--- libmicrohttpd/src/daemon/internal.h 2007-12-20 00:40:49 UTC (rev 5979)
+++ libmicrohttpd/src/daemon/internal.h 2007-12-20 04:23:00 UTC (rev 5980)
@@ -85,21 +85,9 @@
   char *value;
 
   enum MHD_ValueKind kind;
-};
 
-
-struct MHD_Access_Handler
-{
-  struct MHD_Access_Handler *next;
-
-  char *uri_prefix;
-
-  MHD_AccessHandlerCallback dh;
-
-  void *dh_cls;
 };
 
-
 /**
  * Representation of a response.
  */
@@ -172,8 +160,130 @@
 
 };
 
+/**
+ * States in a state machine for a connection.
+ *
+ * Transitions are any-state to CLOSED, any state to state+1,
+ * FOOTERS_SENT to INIT.  CLOSED is the terminal state and
+ * INIT the initial state.
+ *
+ * Note that transitions for *reading* happen only after
+ * the input has been processed; transitions for
+ * *writing* happen after the respective data has been
+ * put into the write buffer (the write does not have
+ * to be completed yet).  A transition to CLOSED or INIT
+ * requires the write to be complete.
+ */
+enum MHD_CONNECTION_STATE
+{
+  /**
+   * Connection just started (no headers received).
+   * Waiting for the line with the request type, URL and version.
+   */
+  MHD_CONNECTION_INIT = 0,
 
+  /**
+   * 1: We got the URL (and request type and version).  Wait for a header line.
+   */
+  MHD_CONNECTION_URL_RECEIVED = MHD_CONNECTION_INIT + 1,
 
+  /**
+   * 2: We got part of a multi-line request header.  Wait for the rest.
+   */
+  MHD_CONNECTION_HEADER_PART_RECEIVED = MHD_CONNECTION_URL_RECEIVED + 1,
+
+  /**
+   * 3: We got the request headers.  Process them.
+   */
+  MHD_CONNECTION_HEADERS_RECEIVED = MHD_CONNECTION_HEADER_PART_RECEIVED + 1,
+
+  /**
+   * 4: We have processed the request headers.  Send 100 continue.
+   */
+  MHD_CONNECTION_HEADERS_PROCESSED = MHD_CONNECTION_HEADERS_RECEIVED + 1,
+
+  /**
+   * 5: We have processed the headers and need to send 100 CONTINUE.
+   */
+  MHD_CONNECTION_CONTINUE_SENDING = MHD_CONNECTION_HEADERS_PROCESSED + 1,
+
+  /**
+   * 6: We have sent 100 CONTINUE (or do not need to).  Read the message body.
+   */
+  MHD_CONNECTION_CONTINUE_SENT = MHD_CONNECTION_CONTINUE_SENDING + 1,
+
+  /**
+   * 7: We got the request body.  Wait for a line of the footer.
+   */
+  MHD_CONNECTION_BODY_RECEIVED = MHD_CONNECTION_CONTINUE_SENT + 1,
+
+  /**
+   * 8: We got part of a line of the footer.  Wait for the
+   * rest.
+   */
+  MHD_CONNECTION_FOOTER_PART_RECEIVED = MHD_CONNECTION_BODY_RECEIVED + 1,
+
+  /**
+   * 9: We received the entire footer.  Wait for a response to be queued
+   * and prepare the response headers.
+   */
+  MHD_CONNECTION_FOOTERS_RECEIVED = MHD_CONNECTION_FOOTER_PART_RECEIVED + 1,
+
+  /**
+   * 10: We have prepared the response headers in the writ buffer.
+   * Send the response headers.
+   */
+  MHD_CONNECTION_HEADERS_SENDING = MHD_CONNECTION_FOOTERS_RECEIVED + 1,
+
+  /**
+   * 11: We have sent the response headers.  Get ready to send the body.
+   */
+  MHD_CONNECTION_HEADERS_SENT = MHD_CONNECTION_HEADERS_SENDING + 1,
+  
+  /**
+   * 12: We are ready to send a part of a non-chunked body.  Send it.
+   */
+  MHD_CONNECTION_NORMAL_BODY_READY = MHD_CONNECTION_HEADERS_SENT + 1,
+  
+  /**
+   * 13: We are waiting for the client to provide more
+   * data of a non-chunked body.
+   */
+  MHD_CONNECTION_NORMAL_BODY_UNREADY = MHD_CONNECTION_NORMAL_BODY_READY + 1,
+
+  /**
+   * 14: We are ready to send a chunk.
+   */
+  MHD_CONNECTION_CHUNKED_BODY_READY = MHD_CONNECTION_NORMAL_BODY_UNREADY + 1,
+
+  /**
+   * 15: We are waiting for the client to provide a chunk of the body.
+   */
+  MHD_CONNECTION_CHUNKED_BODY_UNREADY = MHD_CONNECTION_CHUNKED_BODY_READY + 1,
+  
+  /**
+   * 16: We have sent the response body. Prepare the footers.
+   */
+  MHD_CONNECTION_BODY_SENT = MHD_CONNECTION_CHUNKED_BODY_UNREADY + 1,
+  
+  /**
+   * 17: We have prepared the response footer.  Send it.
+   */
+  MHD_CONNECTION_FOOTERS_SENDING = MHD_CONNECTION_BODY_SENT + 1,  
+
+  /**
+   * 18: We have sent the response footer.  Shutdown or restart.
+   */
+  MHD_CONNECTION_FOOTERS_SENT = MHD_CONNECTION_FOOTERS_SENDING + 1,
+
+  /**
+   * 19: This connection is closed (no more activity
+   * allowed).
+   */
+  MHD_CONNECTION_CLOSED = MHD_CONNECTION_FOOTERS_SENT + 1,
+  
+};
+
 struct MHD_Connection
 {
 
@@ -250,6 +360,21 @@
   char *write_buffer;
 
   /**
+   * Last incomplete header line during parsing of headers.
+   * Allocated in pool.  Only valid if state is
+   * either HEADER_PART_RECEIVED or FOOTER_PART_RECEIVED.
+   */
+  char *last;
+
+  /**
+   * Position after the colon on the last incomplete header 
+   * line during parsing of headers.
+   * Allocated in pool.  Only valid if state is
+   * either HEADER_PART_RECEIVED or FOOTER_PART_RECEIVED.
+   */
+  char *colon;
+
+  /**
    * Foreign address (of length addr_len).  MALLOCED (not
    * in pool!).
    */
@@ -292,6 +417,12 @@
   size_t write_buffer_append_offset;
 
   /**
+   * How many more bytes of the body do we expect
+   * to read? "-1" for unknown.
+   */ 
+  size_t remaining_upload_size;
+
+  /**
    * Current write position in the actual response
    * (excluding headers, content only; should be 0
    * while sending headers).
@@ -299,13 +430,6 @@
   size_t response_write_position;
 
   /**
-   * Remaining (!) number of bytes in the upload.
-   * Set to -1 for unknown (connection will close
-   * to indicate end of upload).
-   */
-  size_t remaining_upload_size;
-
-  /**
    * Position in the 100 CONTINUE message that
    * we need to send when receiving http 1.1 requests.
    */
@@ -333,31 +457,17 @@
    * Has this socket been closed for reading (i.e.
    * other side closed the connection)?  If so,
    * we must completely close the connection once
-   * we are done sending our response.
+   * we are done sending our response (and stop
+   * trying to read from this socket).
    */
-  int read_close;
+  int read_closed;
 
   /**
-   * Have we finished receiving all of the headers yet?
-   * Set to 1 once we are done processing all of the
-   * headers.  Note that due to pipelining, it is
-   * possible that the NEXT request is already
-   * (partially) waiting in the read buffer.
+   * State in the FSM for this connection.
    */
-  int have_received_headers;
+  enum MHD_CONNECTION_STATE state;
 
   /**
-   * Have we finished receiving the data from a
-   * potential file-upload?
-   */
-  int have_received_body;
-
-  /**
-   * Have we finished sending all of the headers yet?
-   */
-  int have_sent_headers;
-
-  /**
    * HTTP response code.  Only valid if response object
    * is already set.
    */
@@ -373,6 +483,11 @@
   int response_unready;
 
   /**
+   * Are we sending with chunked encoding?
+   */
+  int have_chunked_response;
+
+  /**
    * Are we receiving with chunked encoding?  This will be set to
    * MHD_YES after we parse the headers and are processing the body
    * with chunks.  After we are done with the body and we are
@@ -402,9 +517,15 @@
 struct MHD_Daemon
 {
 
-  struct MHD_Access_Handler *handlers;
+  /**
+   * Callback function for all requests.
+   */
+  MHD_AccessHandlerCallback default_handler;
 
-  struct MHD_Access_Handler default_handler;
+  /**
+   * Closure argument to default_handler.
+   */
+  void * default_handler_cls;
 
   /**
    * Linked list of our current connections.

Modified: libmicrohttpd/src/daemon/memorypool.c
===================================================================
--- libmicrohttpd/src/daemon/memorypool.c       2007-12-20 00:40:49 UTC (rev 
5979)
+++ libmicrohttpd/src/daemon/memorypool.c       2007-12-20 04:23:00 UTC (rev 
5980)
@@ -188,4 +188,33 @@
   return NULL;
 }
 
+/**
+ * Clear all entries from the memory pool except
+ * for "keep" of the given "size".
+ * 
+ * @param keep pointer to the entry to keep (maybe NULL)
+ * @param size how many bytes need to be kept at this address
+ * @return addr new address of "keep" (if it had to change)
+ */
+void *MHD_pool_reset(struct MemoryPool * pool,
+                    void * keep,
+                    unsigned int size) 
+{
+  if (keep != NULL)
+    {
+      if (keep != pool->memory)
+       {
+         memmove(pool->memory,
+                 keep,
+                 size);
+         keep = pool->memory;
+       }
+      pool->pos = size;
+    }
+  pool->end = pool->size;
+  return keep;
+}
+
+
+
 /* end of memorypool.c */

Modified: libmicrohttpd/src/daemon/memorypool.h
===================================================================
--- libmicrohttpd/src/daemon/memorypool.h       2007-12-20 00:40:49 UTC (rev 
5979)
+++ libmicrohttpd/src/daemon/memorypool.h       2007-12-20 04:23:00 UTC (rev 
5980)
@@ -81,4 +81,16 @@
                            void *old,
                            unsigned int old_size, unsigned int new_size);
 
+/**
+ * Clear all entries from the memory pool except
+ * for "keep" of the given "size".
+ * 
+ * @param keep pointer to the entry to keep (maybe NULL)
+ * @param size how many bytes need to be kept at this address
+ * @return addr new address of "keep" (if it had to change)
+ */
+void *MHD_pool_reset(struct MemoryPool * pool,
+                    void * keep,
+                    unsigned int size);
+
 #endif

Modified: libmicrohttpd/src/daemon/minimal_example.c
===================================================================
--- libmicrohttpd/src/daemon/minimal_example.c  2007-12-20 00:40:49 UTC (rev 
5979)
+++ libmicrohttpd/src/daemon/minimal_example.c  2007-12-20 04:23:00 UTC (rev 
5980)
@@ -41,14 +41,22 @@
           const char *url,
           const char *method,
           const char *upload_data,
-          const char *version, unsigned int *upload_data_size, void **unused)
+          const char *version, unsigned int *upload_data_size, void **ptr)
 {
+  static int aptr;
   const char *me = cls;
   struct MHD_Response *response;
   int ret;
 
   if (0 != strcmp (method, "GET"))
     return MHD_NO;              /* unexpected method */
+  if (&aptr != *ptr) 
+    {
+      /* do never respond on first call */
+      *ptr = &aptr;
+      return MHD_YES;
+    }
+  *ptr = NULL; /* reset when done */
   response = MHD_create_response_from_data (strlen (me),
                                             (void *) me, MHD_NO, MHD_NO);
   ret = MHD_queue_response (connection, MHD_HTTP_OK, response);

Modified: libmicrohttpd/src/include/microhttpd.h
===================================================================
--- libmicrohttpd/src/include/microhttpd.h      2007-12-20 00:40:49 UTC (rev 
5979)
+++ libmicrohttpd/src/include/microhttpd.h      2007-12-20 04:23:00 UTC (rev 
5980)
@@ -84,7 +84,7 @@
 /**
  * Current version of the library.
  */
-#define MHD_VERSION 0x00000100
+#define MHD_VERSION 0x00000200
 
 /**
  * MHD-internal return codes.
@@ -373,6 +373,11 @@
    */
   MHD_GET_ARGUMENT_KIND = 8,
 
+  /**
+   * HTTP footer (only for http 1.1 chunked encodings).
+   */
+  MHD_FOOTER_KIND = 16,
+
 };
 
 /**
@@ -544,9 +549,15 @@
  *        libmicrohttpd guarantees that "pos" will be
  *        the sum of all non-negative return values
  *        obtained from the content reader so far.
- * @return -1 on error (libmicrohttpd will no longer
- *  try to read content and instead close the connection
- *  with the client).
+ * @return -1 for the end of transmission (or on error);
+ *  if a content transfer size was pre-set and the callback
+ *  has provided fewer than that amount of data, 
+ *  MHD will close the connection with the client;
+ *  if no content size was specified and this is an
+ *  http 1.1 connection using chunked encoding, MHD will 
+ *  interpret "-1" as the normal end of the transfer
+ *  (possibly allowing the client to perform additional
+ *  requests using the same TCP connection).
  */
 typedef int
   (*MHD_ContentReaderCallback) (void *cls, size_t pos, char *buf, int max);
@@ -596,7 +607,7 @@
  *        in which case connections from any IP will be
  *        accepted
  * @param apc_cls extra argument to apc
- * @param dh default handler for all URIs
+ * @param dh handler called for all requests (repeatedly)
  * @param dh_cls extra argument to dh
  * @param ... list of options (type-value pairs,
  *        terminated with MHD_OPTION_END).
@@ -655,33 +666,7 @@
  */
 int MHD_run (struct MHD_Daemon *daemon);
 
-
 /**
- * Register an access handler for all URIs beginning with uri_prefix.
- *
- * @param uri_prefix
- * @return MRI_NO if a handler for this exact prefix
- *         already exists
- */
-int
-MHD_register_handler (struct MHD_Daemon *daemon,
-                      const char *uri_prefix,
-                      MHD_AccessHandlerCallback dh, void *dh_cls);
-
-/**
- * Unregister an access handler for the URIs beginning with
- * uri_prefix.
- *
- * @param uri_prefix
- * @return MHD_NO if a handler for this exact prefix
- *         is not known for this daemon
- */
-int
-MHD_unregister_handler (struct MHD_Daemon *daemon,
-                        const char *uri_prefix,
-                        MHD_AccessHandlerCallback dh, void *dh_cls);
-
-/**
  * Get all of the headers from the request.
  *
  * @param iterator callback to call on each header;





reply via email to

[Prev in Thread] Current Thread [Next in Thread]