libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

commit af90b3cd5169fbc49d376f4b96df1f231a577fdc
parent 404538a7cf03d11bbb433e381744a6055bfb72ea
Author: Christian Grothoff <christian@grothoff.org>
Date:   Sun, 28 Aug 2016 20:51:54 +0000

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

Diffstat:
Msrc/microhttpd/daemon.c | 66+++++++++++++++++++++++++++++-------------------------------------
Msrc/microhttpd/internal.h | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/microhttpd/memorypool.c | 19+++++++++++++++++--
Msrc/microhttpd/memorypool.h | 13++++++++++++-
Msrc/microhttpd/response.c | 38+++++++++++++++++++++++++++++++++++++-
5 files changed, 146 insertions(+), 41 deletions(-)

diff --git 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 @@ -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 @@ -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 @@ -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 @@ -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);