View Issue Details

IDProjectCategoryView StatusLast Update
0006036libmicrohttpdlibmicrohttpd APIpublic2020-02-08 22:03
Reportermedoc Assigned ToChristian Grothoff  
PrioritynormalSeveritymajorReproducibilityalways
Status closedResolutionfixed 
Product Versioncurrent SVN 
Target Version0.9.70Fixed in Version0.9.70 
Summary0006036: MHD_USE_NO_LISTEN_SOCKET | MHD_USE_THREAD_PER_CONNECTION always overflows the connection count
DescriptionWhen starting the daemon with the above flags (and MHD_USE_SELECT_INTERNALLY/MHD_USE_INTERNAL_POLLING_THREAD), then using add_connection() to process incoming connections, the connection count always ends up overflowing, and add_connection() fails with "Server reached connection limit (closing inbound connection)", or an out of descriptors error, depending on the configuration and version.

As far as I can see, the reason is that nobody in this configuration actually calls MHD_cleanup_connections() (MHD_polling_thread is not running because of NO_LISTEN_SOCKET).

With microhttpd 0.9.51, this can be worked around by calling MHD_get_daemon_info(daemon MHD_DAEMON_INFO_CURRENT_CONNECTIONS), which apparently triggers a cleanup.

With 0.9.59 and current git code, I found no workaround, it always ends up in overflow.
Steps To ReproduceAttaching a sample program, just loop wget on it.
TagsNo tags attached.

Activities

medoc

2020-01-07 20:08

reporter  

trmhttp.c (4,403 bytes)   
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>

#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/time.h>
#include <microhttpd.h>

static char usage [] =
" port\n"
"  \n\n"
;
char *thisprog;
static void
Usage(void)
{
    fprintf(stderr, "%s: usage:\n%s", thisprog, usage);
    exit(1);
}

static const int one = 1;
int getcon(int port, struct sockaddr *addr, socklen_t *addrlen)
{
	static int listenfd = -1;

	if (listenfd < 0) {
		struct sockaddr_in	ipaddr;
		if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
			perror("socket");
			return -1;
		}
		(void) setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (char *)&one,
						  sizeof(one));
		memset(&ipaddr, 0, sizeof(ipaddr));
		ipaddr.sin_family = AF_INET;
		ipaddr.sin_addr.s_addr = htonl(INADDR_ANY);
		ipaddr.sin_port = htons(port);
		if (bind(listenfd, (struct sockaddr *)&ipaddr, sizeof(ipaddr)) < 0) {
			perror("bind");
			return -1;
		}
		fprintf(stderr, "Listening on port %d\n",(int)port);
		if (listen(listenfd, 5) < 0) {
			perror("listen");
			return -1;
		}
	}
	int confd = accept(listenfd, addr, addrlen);
	if (confd < 0) {	
		perror("accept");
		return -1;
	}
	fprintf(stderr, "Accept returned %d\n", confd);
	return confd;
}


static int print_out_key (void *cls, enum MHD_ValueKind kind, 
                          const char *key, const char *value)
{
    fprintf(stderr, "kind %d : %s -> %s\n", (int)kind, key, value);
    return MHD_YES;
}

static int answer_to_connection(
    void *cls, struct MHD_Connection *conn, 
    const char *url, const char *method, const char *version, 
    const char *upload_data, size_t *upload_data_size,
    void **con_cls)
{
	fprintf(stderr, "answer_to_connection\n");
    if (NULL == *con_cls) {
        // First call, look at headers, method etc.
		fprintf(stderr, "METHOD: %s\n", method);
        MHD_get_connection_values(conn,MHD_HEADER_KIND,&print_out_key,0);
		*con_cls = (void *)1;
		return MHD_YES;
	}
	struct MHD_Response *response =
		MHD_create_response_from_buffer(0, 0, MHD_RESPMEM_PERSISTENT);
	if (NULL == response ) {
		fprintf(stderr, "create_response failed\n");
		return MHD_YES;
	}
	MHD_add_response_header (response, "Location", "http://go.to.hell/index.html");
	MHD_add_response_header (response, "Connection", "close");
	int ret = MHD_queue_response(conn, 302, response);
	MHD_destroy_response(response);
	return ret;
}

static void notify_connection_callback(
	void *cls,
	struct MHD_Connection *conn,
	void **socket_context,
	enum MHD_ConnectionNotificationCode toe)
{
	switch (toe) {
	case MHD_CONNECTION_NOTIFY_STARTED:
		fprintf(stderr, "notify_connection_callback: started\n");
		break;
	case MHD_CONNECTION_NOTIFY_CLOSED:
		fprintf(stderr, "notify_connection_callback: closed\n");
		break;
	}
}

static void request_completed_callback(
    void *cls, struct MHD_Connection *conn,
    void **con_cls, enum MHD_RequestTerminationCode toe)
{
	fprintf(stderr, "Request completed\n");
}

static int     op_flags;
#define OPT_MOINS 0x1

int main(int argc, char **argv)
{
    int fd;
    int port;
	struct MHD_Daemon *mhd;
	
    thisprog = argv[0];
    argc--; argv++;

    if (argc != 1)
		Usage();
    port = atoi(*argv++);argc --;
	
	mhd = MHD_start_daemon(
		MHD_USE_NO_LISTEN_SOCKET | MHD_USE_THREAD_PER_CONNECTION 
		| MHD_USE_DEBUG,
        -1, 
        /* Accept policy callback and arg */
        NULL, NULL, 
        /* handler and arg */
        &answer_to_connection, NULL,
		MHD_OPTION_CONNECTION_TIMEOUT, 1,
        MHD_OPTION_NOTIFY_COMPLETED, request_completed_callback, NULL,
		MHD_OPTION_NOTIFY_CONNECTION, notify_connection_callback, NULL,
        MHD_OPTION_END);
    if (NULL == mhd) {
        fprintf(stderr, "MHD_start_daemon failed\n");
        return 1;
    }

	struct sockaddr addr;
	socklen_t addrlen=sizeof(addr);
	for (;;) {
		if ((fd = getcon(port, &addr, &addrlen)) < 0)
			exit(1);

		if (MHD_add_connection(mhd, fd, &addr, addrlen) != MHD_YES) {
			fprintf(stderr, "Add_connection failed\n");
			return 0;
		}

#if 0
		const union MHD_DaemonInfo *info = 
			MHD_get_daemon_info(mhd, MHD_DAEMON_INFO_CURRENT_CONNECTIONS);
		if (info) {
			fprintf(stderr, "Connection count: %d\n", info->num_connections);
		} else {
			fprintf(stderr, "MHD_get_daemon_info returned null\n");
		}
#endif
    }
    exit(0);
}
trmhttp.c (4,403 bytes)   

Christian Grothoff

2020-02-07 14:28

manager   ~0015314

Fixed in 94e8d665..6da31a1c by adding a call to MHD_cleanup_connections() inside MHD_add_connection().
Thanks for reporting!

Issue History

Date Modified Username Field Change
2020-01-07 20:08 medoc New Issue
2020-01-07 20:08 medoc File Added: trmhttp.c
2020-02-07 14:16 Christian Grothoff Assigned To => Christian Grothoff
2020-02-07 14:16 Christian Grothoff Status new => assigned
2020-02-07 14:24 Christian Grothoff Product Version => current SVN
2020-02-07 14:24 Christian Grothoff Target Version => 0.9.70
2020-02-07 14:28 Christian Grothoff Note Added: 0015314
2020-02-07 14:28 Christian Grothoff Status assigned => resolved
2020-02-07 14:28 Christian Grothoff Resolution open => fixed
2020-02-07 14:28 Christian Grothoff Fixed in Version => 0.9.70
2020-02-08 22:03 Christian Grothoff Status resolved => closed