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;
}

Reply via email to