View Issue Details
| ID | Project | Category | View Status | Date Submitted | Last Update |
|---|---|---|---|---|---|
| 0006036 | libmicrohttpd | external API | public | 2020-01-07 20:08 | 2021-09-02 17:54 |
| Reporter | medoc | Assigned To | Christian Grothoff | ||
| Priority | normal | Severity | major | Reproducibility | always |
| Status | closed | Resolution | fixed | ||
| Product Version | Git master | ||||
| Target Version | 0.9.70 | Fixed in Version | 0.9.70 | ||
| Summary | 0006036: MHD_USE_NO_LISTEN_SOCKET | MHD_USE_THREAD_PER_CONNECTION always overflows the connection count | ||||
| Description | When 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 Reproduce | Attaching a sample program, just loop wget on it. | ||||
| Tags | No tags attached. | ||||
| Attached Files | 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);
}
| ||||
|
|
Fixed in 94e8d665..6da31a1c by adding a call to MHD_cleanup_connections() inside MHD_add_connection(). Thanks for reporting! |
|
|
Fix committed to master branch. |
| 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 | => Git master |
| 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 |
| 2021-09-02 17:54 | Christian Grothoff | Changeset attached | => libmicrohttpd master 6da31a1c |
| 2021-09-02 17:54 | Christian Grothoff | Note Added: 0018171 | |
| 2024-01-21 13:24 | Christian Grothoff | Category | libmicrohttpd API => external API |