瀏覽代碼

-setup IO buffers for upgraded connections from memory pool - if possible

Christian Grothoff 9 年之前
父節點
當前提交
af90b3cd51
共有 5 個文件被更改,包括 146 次插入41 次删除
  1. 29 37
      src/microhttpd/daemon.c
  2. 51 0
      src/microhttpd/internal.h
  3. 17 2
      src/microhttpd/memorypool.c
  4. 12 1
      src/microhttpd/memorypool.h
  5. 37 1
      src/microhttpd/response.c

+ 29 - 37
src/microhttpd/daemon.c

@@ -2174,22 +2174,15 @@ MHD_get_timeout (struct MHD_Daemon *daemon,
 static void
 process_urh (struct MHD_UpgradeResponseHandle *urh)
 {
-#if FIXME_BUFFERS
-  // FIXME: we need buffer/buffer_size/buffer_off for
-  // both directions to be somehow stored within urh.
-  // (Note that despite using the same variable names
-  // below, we need actually different buffers for each
-  // direction.)
-
-  /* handle reading from HTTPS client and writing to application */
+  /* handle reading from TLS client and writing to application */
   if ( (0 != (MHD_EPOLL_STATE_READ_READY & urh->celi_client)) &&
-       (buffer_off < buffer_size) )
+       (urh->in_buffer_off < urh->in_buffer_size) )
     {
       ssize_t res;
 
-      res = gnutls_record_recv (uri->connection->tls_session,
-                                &buffer[buffer_off],
-                                buffer_size - buffer_off);
+      res = gnutls_record_recv (urh->connection->tls_session,
+                                &urh->in_buffer[urh->in_buffer_off],
+                                urh->in_buffer_size - urh->in_buffer_off);
       if ( (GNUTLS_E_AGAIN == res) ||
            (GNUTLS_E_INTERRUPTED == res) )
         {
@@ -2197,17 +2190,17 @@ process_urh (struct MHD_UpgradeResponseHandle *urh)
         }
       else if (res > 0)
         {
-          buffer_off += res;
+          urh->in_buffer_off += res;
         }
     }
   if ( (0 != (MHD_EPOLL_STATE_WRITE_READY & urh->celi_mhd)) &&
-       (buffer_off > 0) )
+       (urh->in_buffer_off > 0) )
     {
       size_t res;
 
       res = write (urh->mhd_socket,
-                   buffer,
-                   buffer_off);
+                   urh->in_buffer,
+                   urh->in_buffer_off);
       if (-1 == res)
         {
           /* FIXME: differenciate by errno? */
@@ -2215,29 +2208,29 @@ process_urh (struct MHD_UpgradeResponseHandle *urh)
         }
       else
         {
-          if (buffer_off != res)
+          if (urh->in_buffer_off != res)
             {
-              memmove (buffer,
-                       &buffer[res],
-                       buffer_off - res);
-              buffer_off -= res;
+              memmove (urh->in_buffer,
+                       &urh->in_buffer[res],
+                       urh->in_buffer_off - res);
+              urh->in_buffer_off -= res;
             }
           else
             {
-              buffer_off = 0;
+              urh->in_buffer_off = 0;
             }
         }
     }
 
   /* handle reading from application and writing to HTTPS client */
   if ( (0 != (MHD_EPOLL_STATE_READ_READY & urh->celi_mhd)) &&
-       (buffer_off < buffer_size) )
+       (urh->out_buffer_off < urh->out_buffer_size) )
     {
       size_t res;
 
       res = read (urh->mhd_socket,
-                  &buffer[buffer_off],
-                  buffer_size - buffer_off);
+                  &urh->out_buffer[urh->out_buffer_off],
+                  urh->out_buffer_size - urh->out_buffer_off);
       if (-1 == res)
         {
           /* FIXME: differenciate by errno? */
@@ -2245,17 +2238,17 @@ process_urh (struct MHD_UpgradeResponseHandle *urh)
         }
       else
         {
-          buffer_off += res;
+          urh->out_buffer_off += res;
         }
     }
   if ( (0 != (MHD_EPOLL_STATE_WRITE_READY & urh->celi_client)) &&
-       (buffer_off > 0) )
+       (urh->out_buffer_off > 0) )
     {
       ssize_t res;
 
-      res = gnutls_record_send (uri->connection->tls_session,
-                                buffer,
-                                buffer_off);
+      res = gnutls_record_send (urh->connection->tls_session,
+                                urh->out_buffer,
+                                urh->out_buffer_off);
       if ( (GNUTLS_E_AGAIN == res) ||
            (GNUTLS_E_INTERRUPTED == res) )
         {
@@ -2263,20 +2256,19 @@ process_urh (struct MHD_UpgradeResponseHandle *urh)
         }
       else if (res > 0)
         {
-          if (buffer_off != res)
+          if (urh->out_buffer_off != res)
             {
-              memmove (buffer,
-                       &buffer[res],
-                       buffer_off - res);
-              buffer_off -= res;
+              memmove (urh->out_buffer,
+                       &urh->out_buffer[res],
+                       urh->out_buffer_off - res);
+              urh->out_buffer_off -= res;
             }
           else
             {
-              buffer_off = 0;
+              urh->out_buffer_off = 0;
             }
         }
     }
-#endif
 }
 #endif
 

+ 51 - 0
src/microhttpd/internal.h

@@ -887,6 +887,17 @@ struct MHD_Connection
 };
 
 
+/**
+ * Buffer we use for upgrade response handling in the unlikely
+ * case where the memory pool was so small it had no buffer
+ * capacity left.  Note that we don't expect to _ever_ use this
+ * buffer, so it's mostly wasted memory (except that it allows
+ * us to handle a tricky error condition nicely). So no need to
+ * make this one big.  Applications that want to perform well
+ * should just pick an adequate size for the memory pools.
+ */
+#define RESERVE_EBUF_SIZE 8
+
 /**
  * Handle given to the application to manage special
  * actions relating to MHD responses that "upgrade"
@@ -912,6 +923,40 @@ struct MHD_UpgradeResponseHandle
    */
   struct MHD_UpgradeResponseHandle *prev;
 
+  /**
+   * The buffer for receiving data from TLS to
+   * be passed to the application.  Contains @e in_buffer_size
+   * bytes. Do not free!
+   */
+  char *in_buffer;
+
+  /**
+   * The buffer for receiving data from the application to
+   * be passed to TLS.  Contains @e out_buffer_size
+   * bytes. Do not free!
+   */
+  char *out_buffer;
+
+  /**
+   * Size of the @e in_buffer
+   */
+  size_t in_buffer_size;
+
+  /**
+   * Size of the @e out_buffer
+   */
+  size_t out_buffer_size;
+
+  /**
+   * Number of bytes actually in use in the @e in_buffer
+   */
+  size_t in_buffer_off;
+
+  /**
+   * Number of bytes actually in use in the @e out_buffer
+   */
+  size_t out_buffer_off;
+
   /**
    * The socket we gave to the application (r/w).
    */
@@ -932,6 +977,12 @@ struct MHD_UpgradeResponseHandle
    * IO-state of the @e connection's socket.
    */
   enum MHD_EpollState celi_client;
+
+  /**
+   * Emergency IO buffer we use in case the memory pool has literally
+   * nothing left.
+   */
+  char e_buf[RESERVE_EBUF_SIZE];
 #endif
 
 };

+ 17 - 2
src/microhttpd/memorypool.c

@@ -150,6 +150,19 @@ MHD_pool_destroy (struct MemoryPool *pool)
 }
 
 
+/**
+ * Check how much memory is left in the @a pool
+ *
+ * @param pool pool to check
+ * @return number of bytes still available in @a pool
+ */
+size_t
+MHD_pool_get_free (struct MemoryPool *pool)
+{
+  return (pool->end - pool->pos);
+}
+
+
 /**
  * Allocate size bytes from the pool.
  *
@@ -163,7 +176,8 @@ MHD_pool_destroy (struct MemoryPool *pool)
  */
 void *
 MHD_pool_allocate (struct MemoryPool *pool,
-		   size_t size, int from_end)
+		   size_t size,
+                   int from_end)
 {
   void *ret;
   size_t asize;
@@ -171,7 +185,8 @@ MHD_pool_allocate (struct MemoryPool *pool,
   asize = ROUND_TO_ALIGN (size);
   if ( (0 == asize) && (0 != size) )
     return NULL; /* size too close to SIZE_MAX */
-  if ((pool->pos + asize > pool->end) || (pool->pos + asize < pool->pos))
+  if ( (pool->pos + asize > pool->end) ||
+       (pool->pos + asize < pool->pos))
     return NULL;
   if (from_end == MHD_YES)
     {

+ 12 - 1
src/microhttpd/memorypool.h

@@ -70,7 +70,8 @@ MHD_pool_destroy (struct MemoryPool *pool);
  */
 void *
 MHD_pool_allocate (struct MemoryPool *pool,
-		   size_t size, int from_end);
+		   size_t size,
+                   int from_end);
 
 
 /**
@@ -97,6 +98,16 @@ MHD_pool_reallocate (struct MemoryPool *pool,
 		     size_t new_size);
 
 
+/**
+ * Check how much memory is left in the @a pool
+ *
+ * @param pool pool to check
+ * @return number of bytes still available in @a pool
+ */
+size_t
+MHD_pool_get_free (struct MemoryPool *pool);
+
+
 /**
  * Clear all entries from the memory pool except
  * for @a keep of the given @a copy_bytes.  The pointer

+ 37 - 1
src/microhttpd/response.c

@@ -32,6 +32,8 @@
 #include "mhd_sockets.h"
 #include "mhd_itc.h"
 #include "connection.h"
+#include "memorypool.h"
+
 
 #if defined(_WIN32) && defined(MHD_W32_MUTEX_)
 #ifndef WIN32_LEAN_AND_MEAN
@@ -667,12 +669,19 @@ MHD_response_execute_upgrade_ (struct MHD_Response *response,
   urh = malloc (sizeof (struct MHD_UpgradeResponseHandle));
   if (NULL == urh)
     return MHD_NO;
+  memset (urh,
+          0,
+          sizeof (struct MHD_UpgradeResponseHandle));
   urh->connection = connection;
   rbo = connection->read_buffer_offset;
   connection->read_buffer_offset = 0;
 #if HTTPS_SUPPORT
   if (0 != (daemon->options & MHD_USE_SSL) )
   {
+    struct MemoryPool *pool;
+    size_t avail;
+    char *buf;
+
     /* FIXME: this is non-portable for now; W32 port pending... */
     if (0 != socketpair (AF_UNIX,
                          SOCK_STREAM,
@@ -698,9 +707,34 @@ MHD_response_execute_upgrade_ (struct MHD_Response *response,
         free (urh);
         return MHD_NO;
       }
-
     urh->app_socket = sv[0];
     urh->mhd_socket = sv[1];
+    pool = connection->pool;
+    avail = MHD_pool_get_free (pool);
+    if (avail < 8)
+      {
+        /* connection's pool is totally at the limit,
+           use our 'emergency' buffer of #RESERVE_EBUF_SIZE bytes. */
+        avail = RESERVE_EBUF_SIZE;
+        buf = urh->e_buf;
+      }
+    else
+      {
+        /* Normal case: grab all remaining memory from the
+           connection's pool for the IO buffers; the connection
+           certainly won't need it anymore as we've upgraded
+           to another protocol. */
+        buf = MHD_pool_allocate (pool,
+                                 avail,
+                                 MHD_NO);
+      }
+    /* use half the buffer for inbound, half for outbound */
+    avail /= 2;
+    urh->in_buffer_size = avail;
+    urh->out_buffer_size = avail;
+    urh->in_buffer = buf;
+    urh->out_buffer = &buf[avail];
+    /* hand over internal socket to application */
     response->upgrade_handler (response->upgrade_handler_cls,
                                connection,
                                connection->client_context,
@@ -717,6 +751,8 @@ MHD_response_execute_upgrade_ (struct MHD_Response *response,
     /* FIXME: is it possible we did not fully drain the client
        socket yet and are thus read-ready already? This may
        matter if we are in epoll() edge triggered mode... */
+    /* Launch IO processing by the event loop */
+    /* FIXME: this will not work (yet) for thread-per-connection processing */
     DLL_insert (connection->daemon->urh_head,
                 connection->daemon->urh_tail,
                 urh);