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 ;-).
This commit is contained in:
Christian Grothoff
2015-03-31 08:38:33 +00:00
parent 5fe98c7529
commit a05803df4f
7 changed files with 207 additions and 34 deletions
+1
View File
@@ -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:
+9
View File
@@ -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
+27
View File
@@ -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
+79 -3
View File
@@ -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
};
@@ -1005,6 +1017,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.
*/
@@ -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
+1 -1
View File
@@ -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
+39 -7
View File
@@ -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:
+51 -23
View File
@@ -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,13 +582,22 @@ 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);
/**
@@ -999,6 +1016,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.