libmicrohttpd

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

commit a05803df4f24075bd708d7367502a658a76ecf21
parent 5fe98c752902b97abb2011411b5ca73a1b216c74
Author: Christian Grothoff <christian@grothoff.org>
Date:   Tue, 31 Mar 2015 08:38:33 +0000

Robert Gronenberg wrote:


I am using libmicrohttpd in a multithreaded application in the
thread-per-connection mode. In order to maintain statistics and such on
the various clients, the application should be notified about
connections being started and stopped. Something like the
MHD_OPTION_NOTIFY_COMPLETED for requests, but then for connections. As
far as I could find libmicrohttpd does not (yet) provide that functionality.

Attached is a patch that does add this functionality by registering a
connection notification callback function: MHD_OPTION_NOTIFY_CONNECTION
(inspired by the MHD_OPTION_NOTIFY_COMPLETED option).

Could this be included in libmicrohttpd?

=>

The patch was only working for multithreaded apps, so I adjusted it
to cover other threading models, fixed a merge issue and added the
ability to associate a void* with the callbacks (and obtain it via
MHD connection info).  And I updated the manual & ChangeLog. Now
I think it can be included ;-).



Diffstat:
MAUTHORS | 1+
MChangeLog | 9+++++++++
Mdoc/libmicrohttpd.texi | 27+++++++++++++++++++++++++++
Msrc/include/microhttpd.h | 82++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Msrc/microhttpd/connection.c | 2+-
Msrc/microhttpd/daemon.c | 46+++++++++++++++++++++++++++++++++++++++-------
Msrc/microhttpd/internal.h | 74+++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
7 files changed, 207 insertions(+), 34 deletions(-)

diff --git a/AUTHORS b/AUTHORS @@ -51,6 +51,7 @@ Luke-Jr <luke@dashjr.org> Sree Harsha Totakura <sreeharsha@totakura.in> Hani Benhabiles <kroosec@gmail.com> Guy Martin <gmsoft@tuxicoman.be> +Robert Groenenberg <robert.groenenberg@broadforward.com> Documentation contributions also came from: diff --git a/ChangeLog b/ChangeLog @@ -1,3 +1,12 @@ +Tue Mar 31 10:28:26 CEST 2015 + Adding MHD_OPTION_NOTIFY_CONNECTION, + MHD_CONNECTION_NOTIFY_STARTED, + MHD_CONNECTION_NOTIFY_CLOSED and + MHD_CONNECTION_INFO_SOCKET_CONTEXT to allow + applications to trigger operations when TCP + connections start or end, instead of just + exposing HTTP requests starting and ending. -RG/CG + Thu Feb 26 09:55:43 CET 2015 Fixing bug that prevented MHD_OPTION_HTTPS_MEM_DHPARAMS from working within a MHD_OPTION_ARRAY. -DD diff --git a/doc/libmicrohttpd.texi b/doc/libmicrohttpd.texi @@ -631,6 +631,25 @@ pointer to a function of type @code{MHD_RequestCompletedCallback()} and second a pointer to a closure to pass to the request completed callback. The second pointer maybe @code{NULL}. +@item MHD_OPTION_NOTIFY_CONNECTION +Register a function that should be called when the TCP connection to a +client is opened or closed. Note that +@code{MHD_OPTION_NOTIFY_COMPLETED} and the @code{con_cls} argument to +the @code{MHD_AccessHandlerCallback} are per HTTP request (and there +can be multiple HTTP requests per TCP connection). The registered +callback is called twice per TCP connection, with +@code{MHD_CONNECTION_NOTIFY_STARTED} and +@code{MHD_CONNECTION_NOTIFY_CLOSED} respectively. An additional +argument can be used to store TCP connection specific information, +which can be retrieved using @code{MHD_CONNECTION_INFO_SOCKET_CONTEXT} +during the lifetime of the TCP connection. The respective location is +not the same as the HTTP-request-specific @code{con_cls} from the +@code{MHD_AccessHandlerCallback}. + +This option should be followed by @strong{TWO} pointers. First a +pointer to a function of type @code{MHD_NotifyConnectionCallback()} +and second a pointer to a closure to pass to the request completed +callback. The second pointer maybe @code{NULL}. @item MHD_OPTION_PER_IP_CONNECTION_LIMIT Limit on the number of (concurrent) connections made to the @@ -2579,6 +2598,14 @@ automatically (if the platform supports it). As the connection callbacks are invoked in between, those might be used to set different values for TCP-CORK and TCP-NODELAY in the meantime. +@item MHD_CONNECTION_INFO_SOCKET_CONTEXT +Returns the client-specific pointer to a @code{void *} that was +(possibly) set during a @code{MHD_NotifyConnectionCallback} when the +socket was first accepted. Note that this is NOT the same as the +@code{con_cls} argument of the @code{MHD_AccessHandlerCallback}. The +@code{con_cls} is fresh for each HTTP request, while the +@code{socket_context} is fresh for each socket. + @end table @end deftp diff --git a/src/include/microhttpd.h b/src/include/microhttpd.h @@ -130,7 +130,7 @@ typedef intptr_t ssize_t; * Current version of the library. * 0x01093001 = 1.9.30-1. */ -#define MHD_VERSION 0x00093903 +#define MHD_VERSION 0x00093904 /** * MHD-internal return code for "YES". @@ -871,7 +871,19 @@ enum MHD_OPTION * `const char *` argument. * This should be used in conjunction with #MHD_OPTION_HTTPS_MEM_KEY. */ - MHD_OPTION_HTTPS_KEY_PASSWORD = 26 + MHD_OPTION_HTTPS_KEY_PASSWORD = 26, + + /** + * Register a function that should be called whenever a connection is + * started or closed. + * + * This option should be followed by TWO pointers. First a pointer + * to a function of type #MHD_NotifyConnectionCallback and second a + * pointer to a closure to pass to the request completed callback. + * The second pointer maybe NULL. + */ + MHD_OPTION_NOTIFY_CONNECTION = 27 + }; @@ -1006,6 +1018,29 @@ enum MHD_RequestTerminationCode /** + * The `enum MHD_ConnectionNotificationCode` specifies types + * of connection notifications. + * @ingroup request + */ +enum MHD_ConnectionNotificationCode +{ + + /** + * A new connection has been started. + * @ingroup request + */ + MHD_CONNECTION_NOTIFY_STARTED = 0, + + /** + * A connection is closed. + * @ingroup request + */ + MHD_CONNECTION_NOTIFY_CLOSED = 1 + +}; + + +/** * Information about a connection. */ union MHD_ConnectionInfo @@ -1046,6 +1081,12 @@ union MHD_ConnectionInfo * daemons running). */ struct MHD_Daemon *daemon; + + /** + * Socket-specific client context. Points to the same address as + * the "socket_context" of the #MHD_NotifyConnectionCallback. + */ + void **socket_context; }; @@ -1104,7 +1145,17 @@ enum MHD_ConnectionInfoType * No extra arguments should be passed. * @ingroup request */ - MHD_CONNECTION_INFO_CONNECTION_FD + MHD_CONNECTION_INFO_CONNECTION_FD, + + /** + * Returns the client-specific pointer to a `void *` that was (possibly) + * set during a #MHD_NotifyConnectionCallback when the socket was + * first accepted. Note that this is NOT the same as the "con_cls" + * argument of the #MHD_AccessHandlerCallback. The "con_cls" is + * fresh for each HTTP request, while the "socket_context" is fresh + * for each socket. + */ + MHD_CONNECTION_INFO_SOCKET_CONTEXT }; @@ -1243,6 +1294,31 @@ typedef void void **con_cls, enum MHD_RequestTerminationCode toe); +/** + * Signature of the callback used by MHD to notify the + * application about started/stopped connections + * + * @param cls client-defined closure + * @param connection connection handle + * @param socket_context socket-specific pointer where the + * client can associate some state specific + * to the TCP connection; note that this is + * different from the "con_cls" which is per + * HTTP request. The client can initialize + * during #MHD_CONNECTION_NOTIFY_STARTED and + * cleanup during #MHD_CONNECTION_NOTIFY_CLOSED + * and access in the meantime using + * #MHD_CONNECTION_INFO_SOCKET_CONTEXT. + * @param toe reason for connection notification + * @see #MHD_OPTION_NOTIFY_CONNECTION + * @ingroup request + */ +typedef void +(*MHD_NotifyConnectionCallback) (void *cls, + struct MHD_Connection *connection, + void **socket_context, + enum MHD_ConnectionNotificationCode toe); + /** * Iterator over key-value pairs. This iterator diff --git a/src/microhttpd/connection.c b/src/microhttpd/connection.c @@ -1,6 +1,6 @@ /* This file is part of libmicrohttpd - Copyright (C) 2007-2013 Daniel Pittman and Christian Grothoff + Copyright (C) 2007-2015 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 diff --git a/src/microhttpd/daemon.c b/src/microhttpd/daemon.c @@ -796,6 +796,7 @@ MHD_handle_connection (void *data) struct pollfd p[1]; #endif + timeout = con->daemon->connection_timeout; while ( (MHD_YES != con->daemon->shutdown) && (MHD_CONNECTION_CLOSED != con->state) ) @@ -958,6 +959,13 @@ exit: MHD_destroy_response (con->response); con->response = NULL; } + + if (NULL != con->daemon->notify_connection) + con->daemon->notify_connection (con->daemon->notify_connection_cls, + con, + &con->socket_context, + MHD_CONNECTION_NOTIFY_CLOSED); + return (MHD_THRD_RTRN_TYPE_)0; } @@ -1301,7 +1309,9 @@ internal_add_connection (struct MHD_Daemon *daemon, errno = eno; return MHD_NO; } - memset (connection, 0, sizeof (struct MHD_Connection)); + memset (connection, + 0, + sizeof (struct MHD_Connection)); connection->pool = MHD_pool_create (daemon->pool_size); if (NULL == connection->pool) { @@ -1445,11 +1455,19 @@ internal_add_connection (struct MHD_Daemon *daemon, (MHD_YES != MHD_mutex_unlock_ (&daemon->cleanup_connection_mutex)) ) MHD_PANIC ("Failed to release cleanup mutex\n"); + if (NULL != daemon->notify_connection) + daemon->notify_connection (daemon->notify_connection_cls, + connection, + &connection->socket_context, + MHD_CONNECTION_NOTIFY_STARTED); + /* attempt to create handler thread */ if (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) { - res_thread_create = create_thread (&connection->pid, daemon, - &MHD_handle_connection, connection); + res_thread_create = create_thread (&connection->pid, + daemon, + &MHD_handle_connection, + connection); if (0 != res_thread_create) { eno = errno; @@ -1508,6 +1526,11 @@ internal_add_connection (struct MHD_Daemon *daemon, daemon->connections++; return MHD_YES; cleanup: + if (NULL != daemon->notify_connection) + daemon->notify_connection (daemon->notify_connection_cls, + connection, + &connection->socket_context, + MHD_CONNECTION_NOTIFY_CLOSED); if (0 != MHD_socket_close_ (client_socket)) MHD_PANIC ("close failed\n"); MHD_ip_limit_del (daemon, addr, addrlen); @@ -1921,12 +1944,15 @@ MHD_cleanup_connections (struct MHD_Daemon *daemon) } MHD_pool_destroy (pos->pool); #if HTTPS_SUPPORT - if (pos->tls_session != NULL) + if (NULL != pos->tls_session) gnutls_deinit (pos->tls_session); #endif - MHD_ip_limit_del (daemon, - (struct sockaddr *) pos->addr, - pos->addr_len); + if (NULL != daemon->notify_connection) + daemon->notify_connection (daemon->notify_connection_cls, + pos, + &pos->socket_context, + MHD_CONNECTION_NOTIFY_CLOSED); + MHD_ip_limit_del (daemon, pos->addr, pos->addr_len); #if EPOLL_SUPPORT if (0 != (pos->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL)) { @@ -2993,6 +3019,11 @@ parse_options_va (struct MHD_Daemon *daemon, va_arg (ap, MHD_RequestCompletedCallback); daemon->notify_completed_cls = va_arg (ap, void *); break; + case MHD_OPTION_NOTIFY_CONNECTION: + daemon->notify_connection = + va_arg (ap, MHD_NotifyConnectionCallback); + daemon->notify_connection_cls = va_arg (ap, void *); + break; case MHD_OPTION_PER_IP_CONNECTION_LIMIT: daemon->per_ip_connection_limit = va_arg (ap, unsigned int); break; @@ -3234,6 +3265,7 @@ parse_options_va (struct MHD_Daemon *daemon, break; /* all options taking two pointers */ case MHD_OPTION_NOTIFY_COMPLETED: + case MHD_OPTION_NOTIFY_CONNECTION: case MHD_OPTION_URI_LOG_CALLBACK: case MHD_OPTION_EXTERNAL_LOGGER: case MHD_OPTION_UNESCAPE_CALLBACK: diff --git a/src/microhttpd/internal.h b/src/microhttpd/internal.h @@ -1,6 +1,6 @@ /* This file is part of libmicrohttpd - Copyright (C) 2007-2013 Daniel Pittman and Christian Grothoff + Copyright (C) 2007-2015 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 @@ -487,8 +487,10 @@ MHD_state_to_string (enum MHD_CONNECTION_STATE state); * @param max_bytes maximum number of bytes to receive * @return number of bytes written to write_to */ -typedef ssize_t (*ReceiveCallback) (struct MHD_Connection * conn, - void *write_to, size_t max_bytes); +typedef ssize_t +(*ReceiveCallback) (struct MHD_Connection *conn, + void *write_to, + size_t max_bytes); /** @@ -499,8 +501,10 @@ typedef ssize_t (*ReceiveCallback) (struct MHD_Connection * conn, * @param max_bytes maximum number of bytes to transmit * @return number of bytes transmitted */ -typedef ssize_t (*TransmitCallback) (struct MHD_Connection * conn, - const void *write_to, size_t max_bytes); +typedef ssize_t +(*TransmitCallback) (struct MHD_Connection *conn, + const void *write_to, + size_t max_bytes); /** @@ -578,14 +582,23 @@ struct MHD_Connection struct MemoryPool *pool; /** - * We allow the main application to associate some - * pointer with the connection. Here is where we - * store it. (MHD does not know or care what it - * is). + * We allow the main application to associate some pointer with the + * HTTP request, which is passed to each #MHD_AccessHandlerCallback + * and some other API calls. Here is where we store it. (MHD does + * not know or care what it is). */ void *client_context; /** + * We allow the main application to associate some pointer with the + * TCP connection (which may span multiple HTTP requests). Here is + * where we store it. (MHD does not know or care what it is). + * The location is given to the #MHD_NotifyConnectionCallback and + * also accessible via #MHD_CONNECTION_INFO_SOCKET_CONTEXT. + */ + void *socket_context; + + /** * Request method. Should be GET/POST/etc. Allocated * in pool. */ @@ -606,7 +619,7 @@ struct MHD_Connection /** * Buffer for reading requests. Allocated * in pool. Actually one byte larger than - * read_buffer_size (if non-NULL) to allow for + * @e read_buffer_size (if non-NULL) to allow for * 0-termination. */ char *read_buffer; @@ -620,7 +633,8 @@ struct MHD_Connection /** * Last incomplete header line during parsing of headers. * Allocated in pool. Only valid if state is - * either HEADER_PART_RECEIVED or FOOTER_PART_RECEIVED. + * either #MHD_CONNECTION_HEADER_PART_RECEIVED or + * #MHD_CONNECTION_FOOTER_PART_RECEIVED. */ char *last; @@ -628,12 +642,13 @@ struct MHD_Connection * 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. + * either #MHD_CONNECTION_HEADER_PART_RECEIVED or + * #MHD_CONNECTION_FOOTER_PART_RECEIVED. */ char *colon; /** - * Foreign address (of length addr_len). MALLOCED (not + * Foreign address (of length @e addr_len). MALLOCED (not * in pool!). */ struct sockaddr *addr; @@ -676,7 +691,7 @@ struct MHD_Connection /** * How many more bytes of the body do we expect - * to read? MHD_SIZE_UNKNOWN for unknown. + * to read? #MHD_SIZE_UNKNOWN for unknown. */ uint64_t remaining_upload_size; @@ -800,17 +815,17 @@ struct MHD_Connection /** * Handler used for processing read connection operations */ - int (*read_handler) (struct MHD_Connection * connection); + int (*read_handler) (struct MHD_Connection *connection); /** * Handler used for processing write connection operations */ - int (*write_handler) (struct MHD_Connection * connection); + int (*write_handler) (struct MHD_Connection *connection); /** * Handler used for processing idle connection operations */ - int (*idle_handler) (struct MHD_Connection * connection); + int (*idle_handler) (struct MHD_Connection *connection); /** * Function used for reading HTTP request stream. @@ -864,9 +879,10 @@ struct MHD_Connection * @param con connection handle * @return new closure */ -typedef void * (*LogCallback)(void * cls, - const char * uri, - struct MHD_Connection *con); +typedef void * +(*LogCallback)(void * cls, + const char * uri, + struct MHD_Connection *con); /** * Signature of function called to unescape URIs. See also @@ -877,9 +893,10 @@ typedef void * (*LogCallback)(void * cls, * @param uri 0-terminated string to unescape (should be updated) * @return length of the resulting string */ -typedef size_t (*UnescapeCallback)(void *cls, - struct MHD_Connection *conn, - char *uri); +typedef size_t +(*UnescapeCallback)(void *cls, + struct MHD_Connection *conn, + char *uri); /** @@ -1000,6 +1017,17 @@ struct MHD_Daemon void *notify_completed_cls; /** + * Function to call when we are starting/stopping + * a connection. May be NULL. + */ + MHD_NotifyConnectionCallback notify_connection; + + /** + * Closure argument to notify_connection. + */ + void *notify_connection_cls; + + /** * Function to call with the full URI at the * beginning of request processing. May be NULL. * <p>