Hello,
Although it's not easy to reproduce, it seems that using connection
option MHD_CONNECTION_OPTION_TIMEOUT when streaming data with
MHD_create_response_from_callback seems not to work, although.
In my application with MHD underneath where the problem occurs, I
provide a webradio, which is a http stream with unlimited size.
The problem I have is when the client uses a bad wifi connection, or
when the client moves too far from the wifi antenna, then the client
device detects the connection is wrong, therefore closes it, but on the
server side, the connection remains open and never closes, leading to
phantom connections.
If I use MHD_OPTION_CONNECTION_TIMEOUT in MHD_start_daemon, this
partially fixes the problem, the server automatically closes the
connection after the mentionned seconds of total inactivity from the
client (i.e. the client is not reading the stream anymore but keeps the
connection open), but it doesn't fix the problem for bad connections.
I'm attaching a simple example that I used to be able to reproduce the
problem, but I couldn't find a way to reproduce it with a client
program. Maybe by using netem but I'm not familiar with this command.
/Nicolas
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <microhttpd.h>
#define PORT 8080
#define SIZE 32
#define USLEEP 1000000
#define TIMEOUT 10
static void callback_streaming_close (void * stream_user_data) {
time_t rawtime;
struct tm * timeinfo;
time ( &rawtime );
timeinfo = localtime ( &rawtime );
fprintf(stdout, "end stream data at %s", asctime (timeinfo));
}
static ssize_t callback_streaming (void * stream_user_data, uint64_t offset, char * out_buf, size_t max) {
ssize_t my_size = max<SIZE?max:SIZE;
time_t rawtime;
struct tm * timeinfo;
usleep(USLEEP);
time ( &rawtime );
timeinfo = localtime ( &rawtime );
snprintf(out_buf, my_size, "%s<br/>", asctime (timeinfo));
fprintf(stdout, "stream %zu bytes at %s", strlen(out_buf), asctime (timeinfo));
return strlen(out_buf);
}
static int callback_function (void * cls, struct MHD_Connection * connection, const char * url, const char * method, const char * version, const char * upload_data, size_t * upload_data_size, void ** con_cls) {
int ret;
struct MHD_Response * mhd_response = MHD_create_response_from_callback(-1, SIZE, &callback_streaming, NULL, &callback_streaming_close);
if (mhd_response == NULL) {
fprintf(stderr, "Error MHD_create_response_from_callback\n");
mhd_response = MHD_create_response_from_buffer (0, "", MHD_RESPMEM_PERSISTENT);
} else {
MHD_add_response_header (mhd_response, "Content-Type", "text/html; charset=utf-8");
MHD_set_connection_option(connection, MHD_CONNECTION_OPTION_TIMEOUT, TIMEOUT);
}
ret = MHD_queue_response(connection, 200, mhd_response);
MHD_destroy_response (mhd_response);
return ret;
}
int main(void) {
struct MHD_Daemon *d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG | MHD_USE_INTERNAL_POLLING_THREAD, PORT, NULL, NULL, &callback_function, NULL,
#if 0
MHD_OPTION_CONNECTION_TIMEOUT, TIMEOUT,
#endif
MHD_OPTION_END);
if (d != NULL) {
fprintf(stdout, "Daemon started on port %d\n", PORT);
getchar();
MHD_stop_daemon (d);
} else {
fprintf(stderr, "Error starting MHD daemon\n");
}
return 0;
}