summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2016-08-28 20:51:54 +0000
committerChristian Grothoff <christian@grothoff.org>2016-08-28 20:51:54 +0000
commitaf90b3cd5169fbc49d376f4b96df1f231a577fdc (patch)
treea29ce09b6676ba7c7175178a99780397b3d4d090
parent404538a7cf03d11bbb433e381744a6055bfb72ea (diff)
-setup IO buffers for upgraded connections from memory pool - if possible
-rw-r--r--src/microhttpd/daemon.c66
-rw-r--r--src/microhttpd/internal.h51
-rw-r--r--src/microhttpd/memorypool.c19
-rw-r--r--src/microhttpd/memorypool.h13
-rw-r--r--src/microhttpd/response.c38
5 files changed, 146 insertions, 41 deletions
diff --git a/src/microhttpd/daemon.c b/src/microhttpd/daemon.c
index dbf21e18..a25fe358 100644
--- a/src/microhttpd/daemon.c
+++ b/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
diff --git a/src/microhttpd/internal.h b/src/microhttpd/internal.h
index 01d72950..9f0faba7 100644
--- a/src/microhttpd/internal.h
+++ b/src/microhttpd/internal.h
@@ -888,6 +888,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"
* the HTTP protocol (i.e. to WebSockets).
@@ -913,6 +924,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).
*/
MHD_socket app_socket;
@@ -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
};
diff --git a/src/microhttpd/memorypool.c b/src/microhttpd/memorypool.c
index 037cbf57..7cc4f286 100644
--- a/src/microhttpd/memorypool.c
+++ b/src/microhttpd/memorypool.c
@@ -151,6 +151,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.
*
* @param pool memory pool to use for the operation
@@ -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)
{
diff --git a/src/microhttpd/memorypool.h b/src/microhttpd/memorypool.h
index 84135c37..36136af8 100644
--- a/src/microhttpd/memorypool.h
+++ b/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);
/**
@@ -98,6 +99,16 @@ MHD_pool_reallocate (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);
+
+
+/**
* Clear all entries from the memory pool except
* for @a keep of the given @a copy_bytes. The pointer
* returned should be a buffer of @a new_size where
diff --git a/src/microhttpd/response.c b/src/microhttpd/response.c
index 5b67b983..e778ed7f 100644
--- a/src/microhttpd/response.c
+++ b/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);