gnunet-svn
[Top][All Lists]
Advanced

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

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


From: gnunet
Subject: [GNUnet-SVN] r5422 - in libmicrohttpd: . src/daemon src/include
Date: Wed, 8 Aug 2007 02:07:59 -0600 (MDT)

Author: grothoff
Date: 2007-08-08 02:07:49 -0600 (Wed, 08 Aug 2007)
New Revision: 5422

Added:
   libmicrohttpd/src/daemon/memorypool.c
   libmicrohttpd/src/daemon/memorypool.h
Modified:
   libmicrohttpd/ChangeLog
   libmicrohttpd/README
   libmicrohttpd/src/daemon/Makefile.am
   libmicrohttpd/src/daemon/connection.c
   libmicrohttpd/src/daemon/daemon.c
   libmicrohttpd/src/daemon/daemontest_post.c
   libmicrohttpd/src/daemon/daemontest_put.c
   libmicrohttpd/src/daemon/internal.h
   libmicrohttpd/src/include/microhttpd.h
Log:
updates

Modified: libmicrohttpd/ChangeLog
===================================================================
--- libmicrohttpd/ChangeLog     2007-08-07 15:57:09 UTC (rev 5421)
+++ libmicrohttpd/ChangeLog     2007-08-08 08:07:49 UTC (rev 5422)
@@ -1,2 +1,6 @@
+Wed Aug  8 01:46:06 MDT 2007
+        Added pool allocation and connection limitations (total
+        number and memory size).
+
 Tue Jan  9 20:52:48 MST 2007
         Created project build files and updated API. - CG

Modified: libmicrohttpd/README
===================================================================
--- libmicrohttpd/README        2007-08-07 15:57:09 UTC (rev 5421)
+++ libmicrohttpd/README        2007-08-08 08:07:49 UTC (rev 5422)
@@ -1,46 +1,34 @@
-Run "autoreconf -f -i" to create configure.
+Run "autoreconf -fi" to create configure.
 
-This is still pre-alpha software.  The following
-things need to be implemented (in list of importance)
-before certain features can be used at all:
+This is still pre-alpha software.  The following things should be
+implemented (in order of importance) before we can claim to be
+reasonably complete:
 
 
-For POST:
-=========
-- Decoding of POST data, testing thereof
-- POST testcase currently fails (blocks!)
-
-For http-compliance:
-====================
-session.c:
+For http/1.1-compliance:
+========================
+connection.c:
+- support responding immediately with "100 CONTINUE" (http 1.1)!
 - send proper error code back if headers are too long
-  (investigate what we should do with those headers,
-   read?  give user control?) 
-  ALSO: should this limit be per-line or for the
-  entire header? (currently,  we enforce per-line,
-  but the entire header might make more sense!)
-- http 1.0 compatibility (if 1.0, force connection
-  close at the end!)
+  (currently, we just close the connection)
+- support chunked requests from clients
+- send proper error code back if client forgot the "Host" header (?)
+- automatically add MHD_HTTP_HEADER_DATE if client "forgot" to add one (?)
+- automatically drop body from responses to "HEAD" requests?
 
-For IPv6:
+For POST:
 =========
-daemon.c:
-- fix start daemon and accept handlers
+- find better way to handle POST data that does not fit into memory
+- add support to decode multipart/form-data 
 
-
 For SSL:
 ========
 microhttpd.h:
 - define appropriate MHD_OPTIONs
 - actual implementation
 
-
-Other:
-======
-- allow client to control size of input/output
-  buffers (add MHD_OPTION)
-- allow client to limit total number of connections
-  (add MHD_OPTION)
-
-
-
+Missing Testcases:
+==================
+- add testcases for http/1.1 pipelining (need
+  to figure out how to ensure curl pipelines)
+- add testcases for resource limit enforcement

Modified: libmicrohttpd/src/daemon/Makefile.am
===================================================================
--- libmicrohttpd/src/daemon/Makefile.am        2007-08-07 15:57:09 UTC (rev 
5421)
+++ libmicrohttpd/src/daemon/Makefile.am        2007-08-08 08:07:49 UTC (rev 
5422)
@@ -11,6 +11,7 @@
   connection.c connection.h \
   daemon.c  \
   internal.c internal.h \
+  memorypool.c memorypool.h \
   plibc.h \
   response.c response.h 
 
@@ -31,7 +32,9 @@
   daemontest \
   daemontest_get \
   daemontest_post \
-  daemontest_put
+  daemontest_put \
+  daemontest_post11 \
+  daemontest_put11
 
 TESTS = $(check_PROGRAMS)
 
@@ -46,18 +49,29 @@
   $(top_builddir)/src/daemon/libmicrohttpd.la \
   @LIBCURL@ 
 
-
 daemontest_post_SOURCES = \
   daemontest_post.c
 daemontest_post_LDADD = \
   $(top_builddir)/src/daemon/libmicrohttpd.la \
   @LIBCURL@ 
 
-
 daemontest_put_SOURCES = \
   daemontest_put.c
 daemontest_put_LDADD = \
   $(top_builddir)/src/daemon/libmicrohttpd.la \
   @LIBCURL@ 
 
+daemontest_post11_SOURCES = \
+  daemontest_post.c
+daemontest_post11_LDADD = \
+  $(top_builddir)/src/daemon/libmicrohttpd.la \
+  @LIBCURL@ 
+
+
+daemontest_put11_SOURCES = \
+  daemontest_put.c
+daemontest_put11_LDADD = \
+  $(top_builddir)/src/daemon/libmicrohttpd.la \
+  @LIBCURL@ 
+
 endif
\ No newline at end of file

Modified: libmicrohttpd/src/daemon/connection.c
===================================================================
--- libmicrohttpd/src/daemon/connection.c       2007-08-07 15:57:09 UTC (rev 
5421)
+++ libmicrohttpd/src/daemon/connection.c       2007-08-08 08:07:49 UTC (rev 
5422)
@@ -27,9 +27,15 @@
 
 #include "internal.h"
 #include "connection.h"
+#include "memorypool.h"
 #include "response.h"
 
+/**
+ * Size by which MHD usually tries to increment read/write buffers.
+ */
+#define MHD_BUF_INC_SIZE 2048
 
+
 /**
  * Get all of the headers from the request.
  *
@@ -40,9 +46,9 @@
  */
 int
 MHD_get_connection_values(struct MHD_Connection * connection,
-                      enum MHD_ValueKind kind,
-                      MHD_KeyValueIterator iterator,
-                      void * iterator_cls) {
+                         enum MHD_ValueKind kind,
+                         MHD_KeyValueIterator iterator,
+                         void * iterator_cls) {
   int ret;
   struct MHD_HTTP_Header * pos;
 
@@ -131,41 +137,77 @@
                      fd_set * except_fd_set,
                      int * max_fd) {
   int fd;
+  void * buf;
 
   fd = connection->socket_fd;
   if (fd == -1)
     return MHD_YES;
   if ( (connection->read_close == 0) &&
        ( (connection->headersReceived == 0) ||
-        (connection->readLoc < connection->read_buffer_size) ) )
+        (connection->readLoc < connection->read_buffer_size) ) ) {  
     FD_SET(fd, read_fd_set);
-  if (connection->response != NULL)
+    if (fd > *max_fd) 
+      *max_fd = fd;
+  } else {
+
+
+    if ( (connection->read_close == 0) &&
+        ( (connection->headersReceived == 1) &&
+          (connection->post_processed == MHD_NO) &&
+          (connection->readLoc == 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;
+      }
+    }
+  } 
+  if (connection->response != NULL) {
     FD_SET(fd, write_fd_set);
-  if ( (fd > *max_fd) &&
-       ( (connection->headersReceived == 0) ||
-        (connection->readLoc < connection->read_buffer_size) ||
-        (connection->response != NULL) ) )
-    *max_fd = fd;
+    if (fd > *max_fd) 
+      *max_fd = fd;
+  }
   return MHD_YES;
 }
 
 /**
- * Parse a single line of the HTTP header.  Remove it
- * from the read buffer.  If the current line does not
+ * We ran out of memory processing the
+ * header.  Handle it properly.
+ */
+static void
+MHD_excessive_header_handler(struct MHD_Connection * connection) {
+  /* die, header far too long to be reasonable;
+     FIXME: send proper response to client
+     (stop reading, queue proper response) */
+  MHD_DLOG(connection->daemon,
+          "Received excessively long header line, closing connection.\n");
+  CLOSE(connection->socket_fd);
+  connection->socket_fd = -1;
+}
+
+/**
+ * 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
  * far too long, close the connection.  If no line is
  * found (incomplete, buffer too small, line too long),
- * return NULL.  Otherwise return a copy of the line.
+ * return NULL.  Otherwise return a pointer to the line.
  */
 static char *
 MHD_get_next_header_line(struct MHD_Connection * connection) {
   char * rbuf;
   size_t pos;
-  size_t start;
 
   if (connection->readLoc == 0)
     return NULL;
-  start = 0;
   pos = 0;
   rbuf = connection->read_buffer;
   while ( (pos < connection->readLoc - 1) &&
@@ -175,56 +217,55 @@
   if (pos == connection->readLoc - 1) {
     /* not found, consider growing... */
     if (connection->readLoc == connection->read_buffer_size) {
-      /* grow buffer to read larger header or die... */
-      if (connection->read_buffer_size < 4 * MHD_MAX_BUF_SIZE) {
-       rbuf = malloc(connection->read_buffer_size * 2);
-       memcpy(rbuf,
-              connection->read_buffer,
-              connection->readLoc);
-       free(connection->read_buffer);
+      rbuf = MHD_pool_reallocate(connection->pool,
+                                connection->read_buffer,
+                                connection->read_buffer_size,
+                                connection->read_buffer_size * 2 + 
MHD_BUF_INC_SIZE);
+      if (rbuf == NULL) {
+       MHD_excessive_header_handler(connection);
+      } else {
+       connection->read_buffer_size = connection->read_buffer_size * 2 + 
MHD_BUF_INC_SIZE;
        connection->read_buffer = rbuf;
-       connection->read_buffer_size *= 2;
-      } else {
-       /* die, header far too long to be reasonable */
-       MHD_DLOG(connection->daemon,
-                "Received excessively long header line (>%u), closing 
connection.\n",
-                4 * MHD_MAX_BUF_SIZE);
-       CLOSE(connection->socket_fd);
-       connection->socket_fd = -1;
       }
     }
     return NULL;
   }
   /* found, check if we have proper CRLF */
-  rbuf = malloc(pos + 1);
-  memcpy(rbuf,
-        connection->read_buffer,
-        pos);
-  rbuf[pos] = '\0';
-  if ( (connection->read_buffer[pos] == '\r') &&
-       (connection->read_buffer[pos+1] == '\n') )
-    pos++; /* skip both r and n */
-  pos++;
-  memmove(connection->read_buffer,
-         &connection->read_buffer[pos],
-         connection->readLoc - pos);
+  if ( (rbuf[pos] == '\r') &&
+       (rbuf[pos+1] == '\n') ) 
+    rbuf[pos++] = '\0'; /* skip both r and n */
+  rbuf[pos++] = '\0';
+  connection->read_buffer += pos;
+  connection->read_buffer_size -= pos;
   connection->readLoc -= pos;
   return rbuf;
 }
 
-static void
+/**
+ * @return MHD_NO on failure (out of memory), MHD_YES for success
+ */
+static int
 MHD_connection_add_header(struct MHD_Connection * connection,
-                      const char * key,
-                      const char * value,
-                      enum MHD_ValueKind kind) {
+                         char * key,
+                         char * value,
+                         enum MHD_ValueKind kind) {
   struct MHD_HTTP_Header * hdr;
 
-  hdr = malloc(sizeof(struct MHD_HTTP_Header));
+  hdr = MHD_pool_allocate(connection->pool,
+                         sizeof(struct MHD_HTTP_Header),
+                         MHD_YES);
+  if (hdr == NULL) {
+    MHD_DLOG(connection->daemon,
+            "Not enough memory to allocate header record!\n");
+    MHD_excessive_header_handler(connection);
+    return MHD_NO;
+  }
   hdr->next = connection->headers_received;
-  hdr->header = strdup(key);
-  hdr->value = strdup(value);
+  hdr->header = key;
+  hdr->value = value;
   hdr->kind = kind;
   connection->headers_received = hdr;
+  return MHD_YES;
 }
 
 /**
@@ -253,7 +294,10 @@
   }
 }
 
-static void
+/**
+ * @return MHD_NO on failure (out of memory), MHD_YES for success
+ */
+static int
 parse_arguments(enum MHD_ValueKind kind,
                struct MHD_Connection * connection,
                char * args) {
@@ -263,7 +307,7 @@
   while (args != NULL) {
     equals = strstr(args, "=");
     if (equals == NULL)
-      return; /* invalid, ignore */
+      return MHD_NO; /* invalid, ignore */
     equals[0] = '\0';
     equals++;
     amper = strstr(equals, "&");
@@ -273,18 +317,22 @@
     }
     MHD_http_unescape(args);
     MHD_http_unescape(equals);
-    MHD_connection_add_header(connection,
-                             args,
-                             equals,
-                             kind);
+    if (MHD_NO == MHD_connection_add_header(connection,
+                                           args,
+                                           equals,
+                                           kind))
+      return MHD_NO;
     args = amper;
   }
+  return MHD_YES;
 }
 
 /**
  * Parse the cookie header (see RFC 2109).
+ * 
+ * @return MHD_YES for success, MHD_NO for failure (malformed, out of memory)
  */
-static void
+static int
 MHD_parse_cookie_header(struct MHD_Connection * connection) {
   const char * hdr;
   char * cpy;
@@ -294,11 +342,22 @@
   int quotes;
 
   hdr = MHD_lookup_connection_value(connection,
-                                MHD_HEADER_KIND,
-                                "Cookie");
+                                   MHD_HEADER_KIND,
+                                   "Cookie");
   if (hdr == NULL)
-    return;
-  cpy = strdup(hdr);
+    return MHD_YES;
+  cpy = MHD_pool_allocate(connection->pool,
+                         strlen(hdr)+1,
+                         MHD_YES);
+  if (cpy == NULL) {
+    MHD_DLOG(connection->daemon,
+            "Not enough memory to parse cookies!\n");
+    MHD_excessive_header_handler(connection);
+    return MHD_NO;
+  }
+  memcpy(cpy,
+        hdr,
+        strlen(hdr)+1);
   pos = cpy;
   while (pos != NULL) {
     equals = strstr(pos, "=");
@@ -328,13 +387,14 @@
       equals[strlen(equals)-1] = '\0';
       equals++;
     }
-    MHD_connection_add_header(connection,
-                          pos,
-                          equals,
-                          MHD_COOKIE_KIND);
+    if (MHD_NO == MHD_connection_add_header(connection,
+                                           pos,
+                                           equals,
+                                           MHD_COOKIE_KIND)) 
+      return MHD_NO;
     pos = semicolon;
   }
-  free(cpy);
+  return MHD_YES;
 }
 
 /**
@@ -355,7 +415,7 @@
   if (uri == NULL)
     return MHD_NO; /* serious error */
   uri[0] = '\0';
-  connection->method = strdup(line);
+  connection->method = line;
   uri++;
   while (uri[0] == ' ')
     uri++;
@@ -372,11 +432,11 @@
                    connection,
                    args);
   }
-  connection->url = strdup(uri);
+  connection->url = uri;
   if (httpVersion == NULL)
-    connection->version = strdup("");
+    connection->version = "";
   else
-    connection->version = strdup(httpVersion);
+    connection->version = httpVersion;
   return MHD_YES;
 }
 
@@ -398,6 +458,7 @@
   char * colon;
   char * tmp;
   const char * clen;
+  const char * end;
   unsigned long long cval;
 
   if (connection->bodyReceived == 1)
@@ -409,51 +470,38 @@
           (line[0] == '\t') ) {
        /* value was continued on the next line, see
           http://www.jmarshall.com/easy/http/ */
-       if ( (strlen(line) + strlen(last) >
-             4 * MHD_MAX_BUF_SIZE) ) {
-         free(line);
-         free(last);
-         last = NULL;
-         MHD_DLOG(connection->daemon,
-                  "Received excessively long header line (>%u), closing 
connection.\n",
-                  4 * MHD_MAX_BUF_SIZE);
-         CLOSE(connection->socket_fd);
-         connection->socket_fd = -1;
+       last = MHD_pool_reallocate(connection->pool,
+                                  last,
+                                  strlen(last)+1,
+                                  strlen(line) + strlen(last) + 1);
+       if (last == NULL) {
+         MHD_excessive_header_handler(connection);
          break;
        }
-       tmp = malloc(strlen(line) + strlen(last) + 1);
-       strcpy(tmp, last);
-       free(last);
-       last = tmp;
        tmp = line;
        while ( (tmp[0] == ' ') ||
                (tmp[0] == '\t') )
          tmp++; /* skip whitespace at start of 2nd line */
-       strcat(last, tmp);
-       free(line);
+       strcat(last, tmp);      
        continue; /* possibly more than 2 lines... */
       } else {
-       MHD_connection_add_header(connection,
-                              last,
-                              colon,
-                              MHD_HEADER_KIND);
-       free(last);
+       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)) {
-       free(line);
-       goto DIE;
-      }
-      free(line);
+                                              line)) 
+       goto DIE;      
       continue;
     }
     /* check if this is the end of the header */
     if (strlen(line) == 0) {
-      free(line);
       /* end of header */
       connection->headersReceived = 1;
       clen = MHD_lookup_connection_value(connection,
@@ -483,6 +531,17 @@
          connection->bodyReceived = 0;
        }
       }
+      end = MHD_lookup_connection_value(connection,
+                                       MHD_HEADER_KIND,
+                                       MHD_HTTP_HEADER_CONNECTION);
+      if ( (end != NULL) &&
+          (0 == strcasecmp(end,
+                           "close")) ) {
+       /* other side explicitly requested 
+          that we close the connection after
+          this request */
+       connection->read_close = MHD_YES;
+      }
       break;
     }
     /* line should be normal header line, find colon */
@@ -507,13 +566,12 @@
        with a space...) */
     last = line;
   }
-  if (last != NULL) {
-    MHD_connection_add_header(connection,
-                             last,
-                             colon,
-                             MHD_HEADER_KIND);
-    free(last);
-  }
+  if ( (last != NULL) &&
+       (MHD_NO == MHD_connection_add_header(connection,
+                                           last,
+                                           colon,
+                                           MHD_HEADER_KIND)) )
+    return; /* error */
   MHD_parse_cookie_header(connection);
   return;
  DIE:
@@ -540,6 +598,103 @@
 }
 
 /**
+ * Test if we are able to process the POST data.
+ * This depends on available memory (enough to load
+ * all of the POST data into the pool) and the
+ * content encoding of the POST data.  And of course,
+ * this requires that the request is actually a
+ * POST request.
+ * 
+ * @return MHD_YES if so
+ */
+static int
+MHD_test_post_data(struct MHD_Connection * connection) {
+  const char * encoding;
+  void * buf;
+
+  if (0 != strcasecmp(connection->method,
+                     MHD_HTTP_METHOD_POST))
+    return MHD_NO;
+  encoding = MHD_lookup_connection_value(connection,
+                                        MHD_HEADER_KIND,
+                                        MHD_HTTP_HEADER_CONTENT_TYPE);
+  if (encoding == NULL) 
+    return MHD_NO;   
+  if ( (0 == strcasecmp(MHD_HTTP_POST_ENCODING_FORM_URLENCODED,
+                       encoding)) &&
+       (connection->uploadSize != -1) ) {
+    buf = MHD_pool_reallocate(connection->pool,
+                             connection->read_buffer,
+                             connection->read_buffer_size,
+                             connection->uploadSize + 1);
+    if (buf == NULL)
+      return MHD_NO;
+    connection->read_buffer_size = connection->uploadSize + 1;
+    connection->read_buffer = buf;
+    return MHD_YES;    
+  }
+  return MHD_NO;
+}
+
+/**
+ * Process the POST data here (adding to headers).
+ *
+ * Needs to first check POST encoding and then do
+ * the right thing (TM).  The POST data is in the
+ * connection's post_data buffer between the postPos 
+ * and postLoc offsets.  The POST message maybe
+ * incomplete.  The existing buffer (allocated from
+ * the pool) can be used and modified but must then
+ * be properly removed from the struct.
+ * 
+ * @return MHD_YES on success, MHD_NO on error (i.e. out of
+ *         memory).
+ */
+static int
+MHD_parse_post_data(struct MHD_Connection * connection) {
+  const char * encoding;
+  int ret;
+
+  encoding = MHD_lookup_connection_value(connection,
+                                        MHD_HEADER_KIND,
+                                        MHD_HTTP_HEADER_CONTENT_TYPE);
+  if (encoding == NULL) 
+    return MHD_NO;   
+  if (0 == strcasecmp(MHD_HTTP_POST_ENCODING_FORM_URLENCODED,
+                     encoding)) {
+    ret = parse_arguments(MHD_POSTDATA_KIND,
+                         connection,
+                         connection->read_buffer);
+    /* invalidate read buffer for other uses --
+       in particular, do not give it to the
+       client; if this were to be needed, we would
+       have to make a copy, which would double memory 
+       requirements */
+    connection->read_buffer_size = 0;
+    connection->readLoc = 0;
+    connection->uploadSize = 0;
+    connection->read_buffer = NULL;
+    return ret;
+  }
+  if (0 == strcasecmp(MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA,
+                     encoding)) {
+    /* this code should never been reached right now,
+       since the test_post_data function would already
+       return MHD_NO; code is here only for future
+       extensions... */
+    /* see http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4 */
+    MHD_DLOG(connection->daemon,
+            "Unsupported multipart encoding of POST data specified, not 
processing POST data.\n");
+    return MHD_NO;
+  }
+  /* this should never be reached, just here for
+     error checking */
+  MHD_DLOG(connection->daemon,
+          "Unknown encoding of POST data specified, not processing POST 
data.\n");
+  return MHD_NO;   
+}
+
+/**
  * Call the handler of the application for this
  * connection.
  */
@@ -552,9 +707,6 @@
     abort(); /* bad timing... */
   ah = MHD_find_access_handler(connection);
   processed = connection->readLoc;
-  /* FIXME: in case of POST, we need to
-     process the POST data here as well
-     (adding to the header list! */
   if (MHD_NO == ah->dh(ah->dh_cls,
                       connection,
                       connection->url,
@@ -564,7 +716,7 @@
                       &processed)) {
     /* serios internal error, close connection */
     MHD_DLOG(connection->daemon,
-            "Internal application error, closing connection.");
+            "Internal application error, closing connection.\n");
     CLOSE(connection->socket_fd);
     connection->socket_fd = -1;
     return;
@@ -583,7 +735,6 @@
     connection->bodyReceived = 1;
     connection->readLoc = 0;
     connection->read_buffer_size = 0;
-    free(connection->read_buffer);
     connection->read_buffer = NULL;
   }
 }
@@ -600,17 +751,30 @@
   int bytes_read;
   void * tmp;
 
+  if (connection->pool == NULL)
+    connection->pool = MHD_pool_create(connection->daemon->pool_size);
+  if (connection->pool == NULL) {
+    MHD_DLOG(connection->daemon,
+            "Failed to create memory pool!\n");      
+    CLOSE(connection->socket_fd);
+    connection->socket_fd = -1;
+    return MHD_NO;
+  }
   if ( (connection->readLoc >= connection->read_buffer_size) &&
        (connection->headersReceived == 0) ) {
     /* need to grow read buffer */
-    tmp = malloc(connection->read_buffer_size * 2 + MHD_MAX_BUF_SIZE);
-    memcpy(tmp,
-          connection->read_buffer,
-          connection->read_buffer_size);
-    connection->read_buffer_size = connection->read_buffer_size * 2 + 
MHD_MAX_BUF_SIZE;
-    if (connection->read_buffer != NULL)
-      free(connection->read_buffer);
+    tmp = MHD_pool_reallocate(connection->pool,
+                             connection->read_buffer,
+                             connection->read_buffer_size,
+                             connection->read_buffer_size * 2 + 
MHD_BUF_INC_SIZE);
+    if (tmp == NULL) {
+      MHD_DLOG(connection->daemon,
+              "Not enough memory for reading headers!\n");
+      MHD_excessive_header_handler(connection);
+      return MHD_NO;
+    }
     connection->read_buffer = tmp;
+    connection->read_buffer_size = connection->read_buffer_size * 2 + 
MHD_BUF_INC_SIZE;
   }
   if (connection->readLoc >= connection->read_buffer_size) {
     MHD_DLOG(connection->daemon,
@@ -634,16 +798,28 @@
   }
   if (bytes_read == 0) {
     /* other side closed connection */
+    connection->read_close = MHD_YES;
     if (connection->readLoc > 0)
       MHD_call_connection_handler(connection);
     shutdown(connection->socket_fd, SHUT_RD);
     return MHD_YES;
   }
   connection->readLoc += bytes_read;
-  if (connection->headersReceived == 0)
+  if (connection->headersReceived == 0) {
     MHD_parse_connection_headers(connection);
-  if (connection->headersReceived == 1)
-    MHD_call_connection_handler(connection);
+    if (connection->headersReceived == 1) {
+      connection->post_processed = MHD_test_post_data(connection);
+    }
+  }
+  if (connection->headersReceived == 1) {    
+    if ( (connection->post_processed == MHD_YES) &&
+        (connection->uploadSize == connection->readLoc) )
+      if (MHD_NO == MHD_parse_post_data(connection)) 
+       connection->post_processed = MHD_NO;      
+    if ( (connection->post_processed == MHD_NO) ||
+        (connection->read_buffer_size == connection->readLoc) )
+      MHD_call_connection_handler(connection);
+  }
   return MHD_YES;
 }
 
@@ -666,9 +842,9 @@
   } else if (NULL == MHD_get_response_header(connection->response,
                                             MHD_HTTP_HEADER_CONTENT_LENGTH)) {
     _REAL_SNPRINTF(buf,
-            128,
-            "%llu",
-            (unsigned long long) connection->response->total_size);
+                  128,
+                  "%llu",
+                  (unsigned long long) connection->response->total_size);
     MHD_add_response_header(connection->response,
                            MHD_HTTP_HEADER_CONTENT_LENGTH,
                            buf);
@@ -680,7 +856,7 @@
  * fill it with all of the headers from the
  * HTTPd's response.
  */
-static void
+static int
 MHD_build_header_response(struct MHD_Connection * connection) {
   size_t size;
   size_t off;
@@ -702,7 +878,14 @@
     pos = pos->next;
   }
   /* produce data */
-  data = malloc(size + 1);
+  data = MHD_pool_allocate(connection->pool,
+                          size + 1,
+                          MHD_YES);
+  if (data == NULL) {
+    MHD_DLOG(connection->daemon,
+            "Not enough memory for write!\n");       
+    return MHD_NO;
+  }
   memcpy(data,
         code,
         off);
@@ -721,7 +904,10 @@
   if (off != size)
     abort();
   connection->write_buffer = data;
-  connection->write_buffer_size = size;
+  connection->writeLoc = size;
+  connection->writePos = 0;
+  connection->write_buffer_size = size + 1;
+  return MHD_YES;
 }
 
 /**
@@ -743,11 +929,16 @@
     return MHD_NO;
   }
   if (! connection->headersSent) {
-    if (connection->write_buffer == NULL)
-      MHD_build_header_response(connection);
+    if ( (connection->write_buffer == NULL) &&
+        (MHD_NO == MHD_build_header_response(connection)) ) {
+      /* oops - close! */
+      CLOSE(connection->socket_fd);
+      connection->socket_fd = -1;
+      return MHD_NO; 
+    }
     ret = SEND(connection->socket_fd,
-              &connection->write_buffer[connection->writeLoc],
-              connection->write_buffer_size - connection->writeLoc,
+              &connection->write_buffer[connection->writePos],
+              connection->writeLoc - connection->writePos,
               0);
     if (ret < 0) {
       if (errno == EINTR)
@@ -759,13 +950,17 @@
       connection->socket_fd = -1;
       return MHD_YES;
     }
-    connection->writeLoc += ret;
-    if (connection->writeLoc == connection->write_buffer_size) {
+    connection->writePos += ret;
+    if (connection->writeLoc == connection->writePos) {
       connection->writeLoc = 0;
-      free(connection->write_buffer);
+      connection->writePos = 0;
+      connection->headersSent = 1;
+      MHD_pool_reallocate(connection->pool,
+                         connection->write_buffer,
+                         connection->write_buffer_size,
+                         0);
       connection->write_buffer = NULL;
       connection->write_buffer_size = 0;
-      connection->headersSent = 1;
     }
     return MHD_YES;
   }
@@ -781,13 +976,13 @@
     if (response->data_size == 0) {
       if (response->data != NULL)
        free(response->data);
-      response->data = malloc(MHD_MAX_BUF_SIZE);
-      response->data_size = MHD_MAX_BUF_SIZE;
+      response->data = malloc(MHD_BUF_INC_SIZE);
+      response->data_size = MHD_BUF_INC_SIZE;
     }
     ret = response->crc(response->crc_cls,
                        connection->messagePos,
                        response->data,
-                       MAX(MHD_MAX_BUF_SIZE,
+                       MAX(MHD_BUF_INC_SIZE,
                            response->data_size - connection->messagePos));
     if (ret == -1) {
       /* end of message, signal other side by closing! */
@@ -838,22 +1033,26 @@
     connection->headersSent = 0;
     connection->bodyReceived = 0;
     connection->messagePos = 0;
-    free(connection->method);
     connection->method = NULL;
-    free(connection->url);
     connection->url = NULL;
-    free(connection->write_buffer);
-    connection->write_buffer = NULL;
-    connection->write_buffer_size = 0;
     if ( (connection->read_close != 0) ||
         (0 != strcasecmp(MHD_HTTP_VERSION_1_1,
                          connection->version)) ) {
       /* closed for reading => close for good! */
-      CLOSE(connection->socket_fd);
+      if (connection->socket_fd != -1)
+       CLOSE(connection->socket_fd);
       connection->socket_fd = -1;
     }
-    free(connection->version);
     connection->version = NULL;
+    connection->read_buffer = NULL;
+    connection->write_buffer = NULL;
+    connection->read_buffer_size = 0;
+    connection->readLoc = 0;
+    connection->write_buffer_size = 0;
+    connection->writePos = 0;
+    connection->writeLoc = 0;
+    MHD_pool_destroy(connection->pool);
+    connection->pool = NULL;
   }
   return MHD_YES;
 }

Modified: libmicrohttpd/src/daemon/daemon.c
===================================================================
--- libmicrohttpd/src/daemon/daemon.c   2007-08-07 15:57:09 UTC (rev 5421)
+++ libmicrohttpd/src/daemon/daemon.c   2007-08-08 08:07:49 UTC (rev 5422)
@@ -28,9 +28,17 @@
 #include "internal.h"
 #include "response.h"
 #include "connection.h"
+#include "memorypool.h"
 
-#define MHD_MAX_CONNECTIONS FD_SETSIZE -4
+/**
+ * Default connection limit.
+ */
+#define MHD_MAX_CONNECTIONS_DEFAULT FD_SETSIZE -4
 
+/**
+ * Default memory allowed per connection.
+ */
+#define MHD_POOL_SIZE_DEFAULT (1024 * 1024)
 
 /**
  * Register an access handler for all URIs beginning with uri_prefix.
@@ -229,8 +237,15 @@
     MHD_DLOG(daemon,
             "Error accepting connection: %s\n",
             STRERROR(errno));
+    if (s != -1) 
+      CLOSE(s); /* just in case */    
     return MHD_NO;
   }
+  if (daemon->max_connections == 0) {
+    /* above connection limit - reject */
+    CLOSE(s);
+    return MHD_NO;
+  }
   if (MHD_NO == daemon->apc(daemon->apc_cls,
                            addr,
                            addrlen)) {
@@ -241,7 +256,13 @@
   memset(connection,
         0,
         sizeof(struct MHD_Connection));
+  connection->pool = NULL;
   connection->addr = malloc(addrlen);
+  if (connection->addr == NULL) {
+    CLOSE(s);
+    free(connection);
+    return MHD_NO;
+  }
   memcpy(connection->addr,
         addr,
         addrlen);
@@ -258,11 +279,13 @@
             STRERROR(errno));
     free(connection->addr);
     CLOSE(s);
+    free(connection->addr);
     free(connection);
     return MHD_NO;
   }
   connection->next = daemon->connections;
   daemon->connections = connection;
+  daemon->max_connections--;    
   return MHD_YES;
 }
 
@@ -281,7 +304,6 @@
 MHD_cleanup_connections(struct MHD_Daemon * daemon) {
   struct MHD_Connection * pos;
   struct MHD_Connection * prev;
-  struct MHD_HTTP_Header * hpos;
   void * unused;
 
   pos = daemon->connections;
@@ -296,25 +318,12 @@
        pthread_kill(pos->pid, SIGALRM);
        pthread_join(pos->pid, &unused);
       }
-      free(pos->addr);
-      if (pos->url != NULL)
-       free(pos->url);
-      if (pos->method != NULL)
-       free(pos->method);
-      if (pos->write_buffer != NULL)
-       free(pos->write_buffer);
-      if (pos->read_buffer != NULL)
-       free(pos->read_buffer);
-      while (pos->headers_received != NULL) {
-       hpos = pos->headers_received;
-       pos->headers_received = hpos->next;
-       free(hpos->header);
-       free(hpos->value);
-       free(hpos);
-      }
       if (pos->response != NULL)
        MHD_destroy_response(pos->response);
+      MHD_pool_destroy(pos->pool);
+      free(pos->addr);
       free(pos);
+      daemon->max_connections++;
       if (prev == NULL)
        pos = daemon->connections;
       else
@@ -474,6 +483,8 @@
   struct sockaddr_in6 servaddr6;       
   const struct sockaddr * servaddr;
   socklen_t addrlen;
+  va_list ap;
+  enum MHD_OPTION opt;
  
   if ((options & MHD_USE_SSL) != 0)
     return NULL;
@@ -549,6 +560,24 @@
   retVal->default_handler.dh_cls = dh_cls;
   retVal->default_handler.uri_prefix = "";
   retVal->default_handler.next = NULL;
+  retVal->max_connections = MHD_MAX_CONNECTIONS_DEFAULT;
+  retVal->pool_size = MHD_POOL_SIZE_DEFAULT;
+  va_start(ap, dh_cls);
+  while (MHD_OPTION_END != (opt = va_arg(ap, enum MHD_OPTION))) {
+    switch (opt) {
+    case MHD_OPTION_CONNECTION_MEMORY_LIMIT:
+      retVal->pool_size = va_arg(ap, unsigned int);
+      break;
+    case MHD_OPTION_CONNECTION_LIMIT:
+      retVal->max_connections = va_arg(ap, unsigned int);
+      break;
+    default:
+      fprintf(stderr,
+             "Invalid MHD_OPTION argument! (Did you terminate the list with 
MHD_OPTION_END?)\n");
+      abort();
+    }
+  }
+  va_end(ap);
   if ( ( (0 != (options & MHD_USE_THREAD_PER_CONNECTION)) ||
         (0 != (options & MHD_USE_SELECT_INTERNALLY)) ) &&
        (0 != pthread_create(&retVal->pid,

Modified: libmicrohttpd/src/daemon/daemontest_post.c
===================================================================
--- libmicrohttpd/src/daemon/daemontest_post.c  2007-08-07 15:57:09 UTC (rev 
5421)
+++ libmicrohttpd/src/daemon/daemontest_post.c  2007-08-08 08:07:49 UTC (rev 
5422)
@@ -39,6 +39,7 @@
 
 #define POST_DATA "name=daniel&project=curl"
 
+static int oneone;
 
 static int apc_all(void * cls,
                   const struct sockaddr * addr,
@@ -76,40 +77,36 @@
                    unsigned int * upload_data_size) {
   struct MHD_Response * response;
   int ret;
+  const char * r1;
+  const char * r2;
 
   if (0 != strcmp("POST", method)) {
     printf("METHOD: %s\n", method);
     return MHD_NO; /* unexpected method */
   }
-  if ( (*upload_data_size < 24) &&
-       (*upload_data_size > 0) ) 
-    return MHD_YES; /* continue */
-  if (*upload_data_size == 24) {
-    *upload_data_size = 0;
-    if ( (0 != strcmp("daniel",
-                     MHD_lookup_connection_value(connection,
-                                                 MHD_POSTDATA_KIND,
-                                                 "name"))) ||
-        (0 != strcmp("curl",
-                     MHD_lookup_connection_value(connection,
-                                                 MHD_POSTDATA_KIND,
-                                                 "project"))) ) {
-      printf("POST DATA not processed correctly!\n");
-      return MHD_NO;
-    }   
-        
-    return MHD_YES; /* continue */
+  r1 = MHD_lookup_connection_value(connection,
+                                  MHD_POSTDATA_KIND,
+                                  "name");
+  r2 = MHD_lookup_connection_value(connection,
+                                  MHD_POSTDATA_KIND,
+                                  "project");
+  if ( (r1 != NULL) &&
+       (r2 != NULL) &&
+       (0 == strcmp("daniel",
+                   r1)) &&
+       (0 == strcmp("curl",
+                   r2)) ) {
+    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);
+    return MHD_YES; /* done */
   }
-  /* FIXME: check connection headers... */
-  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);
-  return ret;
+  return MHD_YES;
 }
 
 
@@ -118,7 +115,7 @@
   CURL * c;
   char buf[2048];
   struct CBC cbc;
-
+ 
   cbc.buf = buf;
   cbc.size = 2048;
   cbc.pos = 0;
@@ -156,10 +153,15 @@
   curl_easy_setopt(c,
                   CURLOPT_TIMEOUT,
                   2L);
+  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_HTTP_VERSION,
-                  CURL_HTTP_VERSION_1_0);
-  curl_easy_setopt(c,
                   CURLOPT_CONNECTTIMEOUT,
                   2L);
   // NOTE: use of CONNECTTIMEOUT without also
@@ -233,10 +235,15 @@
   curl_easy_setopt(c,
                   CURLOPT_TIMEOUT,
                   2L);
+  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_HTTP_VERSION,
-                  CURL_HTTP_VERSION_1_0);
-  curl_easy_setopt(c,
                   CURLOPT_CONNECTTIMEOUT,
                   2L);
   // NOTE: use of CONNECTTIMEOUT without also
@@ -321,10 +328,15 @@
   curl_easy_setopt(c,
                   CURLOPT_TIMEOUT,
                   5L);
+  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_HTTP_VERSION,
-                  CURL_HTTP_VERSION_1_0);
-  curl_easy_setopt(c,
                   CURLOPT_CONNECTTIMEOUT,
                   5L);
   // NOTE: use of CONNECTTIMEOUT without also
@@ -429,11 +441,12 @@
         char * const * argv) {
   unsigned int errorCount = 0;
 
+  oneone = NULL != strstr(argv[0], "11");
   if (0 != curl_global_init(CURL_GLOBAL_WIN32))
     return 2;
   errorCount += testInternalPost();
   errorCount += testMultithreadedPost();
-  errorCount += testExternalPost();  
+  errorCount += testExternalPost();    
   if (errorCount != 0)
     fprintf(stderr,
            "Error (code: %u)\n",

Modified: libmicrohttpd/src/daemon/daemontest_put.c
===================================================================
--- libmicrohttpd/src/daemon/daemontest_put.c   2007-08-07 15:57:09 UTC (rev 
5421)
+++ libmicrohttpd/src/daemon/daemontest_put.c   2007-08-08 08:07:49 UTC (rev 
5422)
@@ -32,6 +32,8 @@
 #include <string.h>
 #include <time.h>
 
+static int oneone;
+
 static int apc_all(void * cls,
                   const struct sockaddr * addr,
                   socklen_t addrlen) {
@@ -164,10 +166,15 @@
   curl_easy_setopt(c,
                   CURLOPT_TIMEOUT,
                   15L);
+  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_HTTP_VERSION,
-                  CURL_HTTP_VERSION_1_0);
-  curl_easy_setopt(c,
                   CURLOPT_CONNECTTIMEOUT,
                   15L);
   // NOTE: use of CONNECTTIMEOUT without also
@@ -246,10 +253,15 @@
   curl_easy_setopt(c,
                   CURLOPT_TIMEOUT,
                   15L);
+  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_HTTP_VERSION,
-                  CURL_HTTP_VERSION_1_0);
-  curl_easy_setopt(c,
                   CURLOPT_CONNECTTIMEOUT,
                   15L);
   // NOTE: use of CONNECTTIMEOUT without also
@@ -339,10 +351,15 @@
   curl_easy_setopt(c,
                   CURLOPT_TIMEOUT,
                   15L);
+  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_HTTP_VERSION,
-                  CURL_HTTP_VERSION_1_0);
-  curl_easy_setopt(c,
                   CURLOPT_CONNECTTIMEOUT,
                   15L);
   // NOTE: use of CONNECTTIMEOUT without also
@@ -447,6 +464,7 @@
         char * const * argv) {
   unsigned int errorCount = 0;
 
+  oneone = NULL != strstr(argv[0], "11");
   if (0 != curl_global_init(CURL_GLOBAL_WIN32))
     return 2;
   errorCount += testInternalPut();

Modified: libmicrohttpd/src/daemon/internal.h
===================================================================
--- libmicrohttpd/src/daemon/internal.h 2007-08-07 15:57:09 UTC (rev 5421)
+++ libmicrohttpd/src/daemon/internal.h 2007-08-08 08:07:49 UTC (rev 5422)
@@ -37,6 +37,7 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <signal.h>
+#include <sys/mman.h>
 
 #include "config.h"
 #include "plibc.h"
@@ -49,8 +50,6 @@
 
 #include <pthread.h>
 
-#define MHD_MAX_BUF_SIZE 2048
-
 #define MAX(a,b) ((a)<(b)) ? (b) : (a)
 
 
@@ -157,41 +156,72 @@
 
 
 struct MHD_Connection {
+
+  /**
+   * This is a linked list.
+   */
   struct MHD_Connection * next;
 
+  /**
+   * Reference to the MHD_Daemon struct.
+   */
   struct MHD_Daemon * daemon;
 
+  /**
+   * Linked list of parsed headers.
+   */
   struct MHD_HTTP_Header * headers_received;
 
+  /**
+   * Response to transmit (initially NULL).
+   */
   struct MHD_Response * response;
 
   /**
-   * Request method.  Should be GET/POST/etc.
+   * The memory pool is created whenever we first read
+   * from the TCP stream and destroyed at the end of
+   * each request (and re-created for the next request).
+   * In the meantime, this pointer is NULL.  The
+   * pool is used for all connection-related data
+   * except for the response (which maybe shared between
+   * connections) and the IP address (which persists
+   * across individual requests).
    */
+  struct MemoryPool * pool;
+
+  /**
+   * Request method.  Should be GET/POST/etc.  Allocated
+   * in pool.
+   */
   char * method;
 
   /**
-   * Requested URL (everything after "GET" only).
+   * Requested URL (everything after "GET" only).  Allocated
+   * in pool.
    */
   char * url;
 
   /**
-   * HTTP version string (i.e. http/1.1)
+   * HTTP version string (i.e. http/1.1).  Allocated
+   * in pool.
    */
   char * version;
 
   /**
-   * Buffer for reading requests.
+   * Buffer for reading requests.   Allocated
+   * in pool.
    */
   char * read_buffer;
 
   /**
-   * Buffer for writing response.
+   * Buffer for writing response (headers only).  Allocated
+   * in pool.
    */
   char * write_buffer;
 
   /**
-   * Foreign address (of length addr_len).
+   * Foreign address (of length addr_len).  MALLOCED (not
+   * in pool!).
    */
   struct sockaddr_in * addr;
 
@@ -201,12 +231,30 @@
    */
   pthread_t pid;
 
+  /**
+   * Size of read_buffer (in bytes).
+   */
   size_t read_buffer_size;
 
+  /**
+   * Position where we currently append data in
+   * read_buffer (last valid position).
+   */
   size_t readLoc;
 
+  /**
+   * Size of write_buffer (in bytes).
+   */
   size_t write_buffer_size;
 
+  /**
+   * Offset where we are with sending from write_buffer.
+   */
+  size_t writePos;
+
+  /**
+   * Last valid location in write_buffer.
+   */
   size_t writeLoc;
 
   /**
@@ -264,6 +312,11 @@
   int headersSent;
 
   /**
+   * Are we processing the POST data?
+   */
+  int post_processed;
+
+  /**
    * HTTP response code.  Only valid if response object
    * is already set.
    */
@@ -279,6 +332,9 @@
 
   struct MHD_Access_Handler default_handler;
 
+  /**
+   * Linked list of our current connections.
+   */
   struct MHD_Connection * connections;
 
   MHD_AcceptPolicyCallback apc;
@@ -301,11 +357,24 @@
   int shutdown;
 
   /**
+   * Size of the per-connection memory pools.
+   */
+  unsigned int pool_size;
+
+  /**
+   * Limit on the number of parallel connections.
+   */
+  unsigned int max_connections;
+
+  /**
    * Daemon's options.
    */
   enum MHD_OPTION options;
 
-  unsigned short port;
+  /**
+   * Listen port.
+   */
+  unsigned short port;  
 
 };
 

Added: libmicrohttpd/src/daemon/memorypool.c
===================================================================
--- libmicrohttpd/src/daemon/memorypool.c                               (rev 0)
+++ libmicrohttpd/src/daemon/memorypool.c       2007-08-08 08:07:49 UTC (rev 
5422)
@@ -0,0 +1,180 @@
+/*
+     This file is part of libmicrohttpd
+     (C) 2007 Daniel Pittman
+
+     libmicrohttpd is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     libmicrohttpd is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with libmicrohttpd; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file memorypool.c
+ * @brief memory pool
+ * @author Christian Grothoff
+ */
+
+#include "memorypool.h"
+
+struct MemoryPool {
+
+  /**
+   * Pointer to the pool's memory
+   */ 
+  char * memory;
+
+  /**
+   * Size of the pool.
+   */
+  unsigned int size;
+
+  /**
+   * Offset of the first unallocated byte.
+   */
+  unsigned int pos;
+
+  /**
+   * Offset of the last unallocated byte.
+   */
+  unsigned int end;
+
+  /**
+   * 0 if pool was malloc'ed, 1 if mmapped.
+   */
+  int is_mmap;
+};
+
+/**
+ * Create a memory pool.
+ * 
+ * @param max maximum size of the pool
+ */
+struct MemoryPool * MHD_pool_create(unsigned int max) {
+  struct MemoryPool * pool;
+
+  pool = malloc(sizeof(struct MemoryPool));
+  if (pool == NULL)
+    return NULL;
+  pool->memory = MMAP(NULL, max, PROT_READ | PROT_WRITE,
+                     MAP_ANONYMOUS, -1, 0);
+  if ( (pool->memory == MAP_FAILED) ||
+       (pool->memory == NULL) ) {
+    pool->memory = malloc(max);
+    if (pool->memory == NULL) {
+      free(pool);
+      return NULL;
+    }
+    pool->is_mmap = 0;
+  } else {
+    pool->is_mmap = 1;
+  }       
+  pool->pos = 0;
+  pool->end = max;
+  pool->size = max;
+  return pool;
+}
+
+/**
+ * Destroy a memory pool.
+ */
+void MHD_pool_destroy(struct MemoryPool * pool) {
+  if (pool == NULL)
+    return;
+  if (pool->is_mmap == 0)
+    free(pool->memory);
+  else
+    MUNMAP(pool->memory, pool->size);
+  free(pool);
+}
+
+/**
+ * Allocate size bytes from the pool.
+ * @return NULL if the pool cannot support size more
+ *         bytes
+ */
+void * MHD_pool_allocate(struct MemoryPool * pool,
+                        unsigned int size,
+                        int from_end) {
+  void * ret;
+
+  if ( (pool->pos + size > pool->end) ||
+       (pool->pos + size < pool->pos) )
+    return NULL;
+  if (from_end == MHD_YES) {
+    ret = &pool->memory[pool->end - size];
+    pool->end -= size;
+  } else {
+    ret = &pool->memory[pool->pos];
+    pool->pos += size;
+  }
+  return ret;
+}
+
+/**
+ * Reallocate a block of memory obtained from the pool.
+ * This is particularly efficient when growing or
+ * shrinking the block that was last (re)allocated.
+ * If the given block is not the most recenlty 
+ * (re)allocated block, the memory of the previous
+ * allocation may be leaked until the pool is 
+ * destroyed (and copying the data maybe required).
+ *
+ * @param old the existing block
+ * @param old_size the size of the existing block
+ * @param new_size the new size of the block
+ * @return new address of the block, or 
+ *         NULL if the pool cannot support new_size 
+ *         bytes (old continues to be valid for old_size)
+ */
+void * MHD_pool_reallocate(struct MemoryPool * pool,
+                          void * old,
+                          unsigned int old_size,
+                          unsigned int new_size) {  
+  void * ret;
+
+  if ( (pool->end < old_size) ||
+       (pool->end < new_size) ) 
+    return NULL; /* unsatisfiable or bogus request */
+
+  if ( (pool->pos >= old_size) &&
+       (&pool->memory[pool->pos - old_size] == old) ) {
+    /* was the previous allocation - optimize! */
+    if (pool->pos + new_size - old_size <= pool->end) {
+      /* fits */
+      pool->pos += new_size - old_size;
+      if (new_size < old_size) /* shrinking - zero again! */
+       memset(&pool->memory[pool->pos],
+              0,
+              old_size - new_size);
+      return old;
+    }
+    /* does not fit */
+    return NULL;    
+  }
+  if (new_size <= old_size)
+    return old; /* cannot shrink, no need to move */
+  if ( (pool->pos + new_size >= pool->pos) &&
+       (pool->pos + new_size <= pool->end) ) {
+    /* fits */
+    ret = &pool->memory[pool->pos];
+    memcpy(ret,
+          old,
+          old_size);
+    pool->pos += new_size;
+    return ret;
+  }
+  /* does not fit */
+  return NULL;
+}
+
+/* end of memorypool.c */


Property changes on: libmicrohttpd/src/daemon/memorypool.c
___________________________________________________________________
Name: svn:eol-style
   + native

Added: libmicrohttpd/src/daemon/memorypool.h
===================================================================
--- libmicrohttpd/src/daemon/memorypool.h                               (rev 0)
+++ libmicrohttpd/src/daemon/memorypool.h       2007-08-08 08:07:49 UTC (rev 
5422)
@@ -0,0 +1,87 @@
+/*
+     This file is part of libmicrohttpd
+     (C) 2007 Daniel Pittman
+
+     libmicrohttpd is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     libmicrohttpd is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with libmicrohttpd; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file memorypool.h
+ * @brief memory pool; mostly used for efficient (de)allocation
+ *        for each connection and bounding memory use for each
+ *        request
+ * @author Christian Grothoff
+ */
+
+#ifndef MEMORYPOOL_H
+#define MEMORYPOOL_H
+
+#include "internal.h"
+
+/**
+ * Opaque handle for a memory pool.
+ * Pools are not reentrant and must not be used
+ * by multiple threads.
+ */
+struct MemoryPool;
+
+/**
+ * Create a memory pool.
+ * 
+ * @param max maximum size of the pool
+ */
+struct MemoryPool * MHD_pool_create(unsigned int max);
+
+/**
+ * Destroy a memory pool.
+ */
+void MHD_pool_destroy(struct MemoryPool * pool);
+
+/**
+ * Allocate size bytes from the pool.
+ *
+ * @param from_end allocate from end of pool (set to MHD_YES);
+ *        use this for small, persistent allocations that
+ *        will never be reallocated
+ * @return NULL if the pool cannot support size more
+ *         bytes
+ */
+void * MHD_pool_allocate(struct MemoryPool * pool,
+                        unsigned int size,
+                        int from_end);
+
+/**
+ * Reallocate a block of memory obtained from the pool.
+ * This is particularly efficient when growing or
+ * shrinking the block that was last (re)allocated.
+ * If the given block is not the most recenlty 
+ * (re)allocated block, the memory of the previous
+ * allocation may be leaked until the pool is 
+ * destroyed (and copying the data maybe required).
+ *
+ * @param old the existing block
+ * @param old_size the size of the existing block
+ * @param new_size the new size of the block
+ * @return new address of the block, or 
+ *         NULL if the pool cannot support new_size 
+ *         bytes (old continues to be valid for old_size)
+ */
+void * MHD_pool_reallocate(struct MemoryPool * pool,
+                          void * old,
+                          unsigned int old_size,
+                          unsigned int new_size);
+
+#endif


Property changes on: libmicrohttpd/src/daemon/memorypool.h
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: libmicrohttpd/src/include/microhttpd.h
===================================================================
--- libmicrohttpd/src/include/microhttpd.h      2007-08-07 15:57:09 UTC (rev 
5421)
+++ libmicrohttpd/src/include/microhttpd.h      2007-08-08 08:07:49 UTC (rev 
5422)
@@ -187,7 +187,24 @@
 #define MHD_HTTP_VERSION_1_0 "HTTP/1.0"
 #define MHD_HTTP_VERSION_1_1 "HTTP/1.1"
 
+/**
+ * HTTP methods
+ */
+#define MHD_HTTP_METHOD_CONNECT "CONNECT"
+#define MHD_HTTP_METHOD_DELETE "DELETE"
+#define MHD_HTTP_METHOD_GET "GET"
+#define MHD_HTTP_METHOD_HEAD "HEAD"
+#define MHD_HTTP_METHOD_OPTIONS "OPTIONS"
+#define MHD_HTTP_METHOD_POST "POST"
+#define MHD_HTTP_METHOD_PUT "PUT"
+#define MHD_HTTP_METHOD_TRACE "TRACE"
 
+/**
+ * HTTP POST encodings, see also
+ * http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4
+ */
+#define MHD_HTTP_POST_ENCODING_FORM_URLENCODED 
"application/x-www-form-urlencoded"
+#define MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA "multipart/form-data"
 
 /**
  * Options for the MHD daemon.  Note that if neither
@@ -248,9 +265,17 @@
   MHD_OPTION_END = 0,
 
   /**
-   * FIXME: add options for buffer sizes here...
+   * Maximum memory size per connection (followed by an
+   * unsigned int).
    */
+  MHD_OPTION_CONNECTION_MEMORY_LIMIT = 1,
 
+  /**
+   * Maximum number of concurrenct connections to
+   * accept (followed by an unsigned int).
+   */
+  MHD_OPTION_CONNECTION_LIMIT = 2,
+
 };
 
 /**
@@ -265,22 +290,28 @@
   MHD_RESPONSE_HEADER_KIND = 0,
 
   /**
-   * HTTP header
+   * HTTP header.
    */
   MHD_HEADER_KIND = 1,
 
   /**
-   * Cookies
+   * Cookies.  Note that the original HTTP header containing
+   * the cookie(s) will still be available and intact.
    */
   MHD_COOKIE_KIND = 2,
 
   /**
-   * POST data
+   * POST data.  This is available only if a content encoding
+   * supported by MHD is used (currently only URL encoding),
+   * and only if the posted content fits within the available
+   * memory pool.  Note that in that case, the upload data
+   * given to the MHD_AccessHandlerCallback will be
+   * empty (since it has already been processed).
    */
   MHD_POSTDATA_KIND = 4,
 
   /**
-   * GET (URI) arguments
+   * GET (URI) arguments.
    */
   MHD_GET_ARGUMENT_KIND = 8,
 
@@ -326,9 +357,16 @@
  * @param url the requested url
  * @param method the HTTP method used ("GET", "PUT", etc.)
  * @param version the HTTP version string (i.e. "HTTP/1.1")
+ * @param upload_data the data being uploaded (excluding HEADERS,
+ *        for a POST that fits into memory and that is encoded
+ *        with a supported encoding, the POST data will NOT be
+ *        given in upload_data and is instead available as 
+ *        part of MHD_get_connection_values; very large POST
+ *        data *will* be made available incrementally in
+ *        upload_data)
  * @param upload_data_size set initially to the size of the
  *        upload_data provided; the method must update this
- *        value to the number of bytes NOT processed
+ *        value to the number of bytes NOT processed;
  * @return MHS_YES if the connection was handled successfully,
  *         MHS_NO if the socket must be closed due to a serios
  *         error while handling the request





reply via email to

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