View Issue Details

IDProjectCategoryView StatusLast Update
0005295libmicrohttpdinternal event looppublic2018-11-06 19:46
Reporterghaderer Assigned ToChristian Grothoff  
PrioritynormalSeveritymajorReproducibilityalways
Status closedResolutionduplicate 
Platformamd64OSLinuxOS Version16.04.4 LTS
Product Version0.9.59 
Target Version0.9.60Fixed in Version0.9.60 
Summary0005295: connection forcibly closed when response reader returns no data
DescriptionI'm using the daemon with the internal polling mode and one thread per connection.

It sometimes happens my response reader callback has no data to return. But I don't want to block in the callback itself as it breaks connection shutdown detection. Indeed, it won't be detected unless the callback returns and MHD has the opportunity to read/write/poll on the socket again.

So my callback returns 0 in this case to indicate that no data was read. But the connection is immediately closed instead of calling the response reader again as I was expecting.
Steps To ReproduceCompile the attached example program and start it. Then run:

$ curl -v http://127.0.0.1:8081

You should get the following output:

<<<
* Rebuilt URL to: http://127.0.0.1:8081/
* Trying 127.0.0.1...
* Connected to 127.0.0.1 (127.0.0.1) port 8081 (#0)
> GET / HTTP/1.1
> Host: 127.0.0.1:8081
> User-Agent: curl/7.47.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Connection: Keep-Alive
< Transfer-Encoding: chunked
< Date: Fri, 09 Mar 2018 08:45:58 GMT
<
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
* transfer closed with outstanding read data remaining
* Closing connection 0
curl: (18) transfer closed with outstanding read data remaining
>>>

But I was expecting:

<<<
* Rebuilt URL to: http://127.0.0.1:8081/
* Trying 127.0.0.1...
* Connected to 127.0.0.1 (127.0.0.1) port 8081 (#0)
> GET / HTTP/1.1
> Host: 127.0.0.1:8081
> User-Agent: curl/7.47.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Connection: Keep-Alive
< Transfer-Encoding: chunked
< Date: Fri, 09 Mar 2018 08:45:15 GMT
<
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World! <----- 2 second pause here
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
* Connection #0 to host 127.0.0.1 left intact
>>>
Additional InformationThe following change fixes this issue:

<<<
diff --git a/src/microhttpd/daemon.c b/src/microhttpd/daemon.c
index 56cd453..61ede77 100644
--- a/src/microhttpd/daemon.c
+++ b/src/microhttpd/daemon.c
@@ -1937,7 +1937,7 @@ thread_main_handle_connection (void *data)
          num_ready = MHD_SYS_select_ (maxsock + 1,
                                        &rs,
                                        &ws,
- NULL,
+ &es,
                                        tvp);
          if (num_ready < 0)
            {
>>>
TagsNo tags attached.
Attached Files
example.c (1,793 bytes)   
#include <sys/types.h>
#include <sys/stat.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

#include <microhttpd.h>

typedef struct state_s {
  int paused;
  int count;
} state_t;

static ssize_t content_reader(
    void * data,
    uint64_t pos,
    char * buf,
    size_t max
) {
  state_t * state = (state_t *)data;

  /* We send 20 greetings with a pause at the 10th one. */
  if (state->count >= 20)
    return MHD_CONTENT_READER_END_OF_STREAM;

  if (state->count == 10 && !state->paused) {
    sleep(2);
    state->paused = 1;
    return 0;
  }

  state->count++;
  strcpy(buf, "Hello World!\n");
  return strlen("Hello World!\n");
}

static int access_handler(
    void * data,
    struct MHD_Connection * connection,
    const char * url,
    const char * method,
    const char * version,
    const char * upload_data,
    size_t * upload_data_size,
    void ** connection_data
) {
  struct MHD_Response * response;
  int result;
  state_t * state;

  state = malloc(sizeof(state_t));
  assert(state != NULL);
  state->paused = 0;
  state->count = 0;

  response = MHD_create_response_from_callback(MHD_SIZE_UNKNOWN, 2048,
      content_reader, state, free);
  assert(response != NULL);

  result = MHD_queue_response(connection, 200, response);
  assert(result == MHD_YES);

  *connection_data = response;

  return MHD_YES;
}

int main(
    int argc,
    char ** argv
) {
  struct MHD_Daemon * daemon;

  daemon = MHD_start_daemon(MHD_USE_INTERNAL_POLLING_THREAD |
      MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG,
      8081,
      NULL, NULL,
      access_handler, NULL,
      MHD_OPTION_END);
  assert(daemon != NULL);

  while (1)
    sleep(1000);

  return 0;
}
example.c (1,793 bytes)   

Relationships

duplicate of 0005278 closedChristian Grothoff Long running connections are aborted due to incomplete logic change 

Activities

Christian Grothoff

2018-03-23 22:22

manager   ~0012893

This is a duplicate, the exact same fix was already applied (on Feb 16th).

Issue History

Date Modified Username Field Change
2018-03-09 10:17 ghaderer New Issue
2018-03-09 10:17 ghaderer File Added: example.c
2018-03-23 22:22 Christian Grothoff Assigned To => Christian Grothoff
2018-03-23 22:22 Christian Grothoff Status new => resolved
2018-03-23 22:22 Christian Grothoff Resolution open => duplicate
2018-03-23 22:22 Christian Grothoff Fixed in Version => 0.9.60
2018-03-23 22:22 Christian Grothoff Note Added: 0012893
2018-03-23 22:22 Christian Grothoff Relationship added duplicate of 0005278
2018-03-23 22:22 Christian Grothoff Target Version => 0.9.60
2018-11-06 19:46 Christian Grothoff Status resolved => closed
2024-01-21 13:24 Christian Grothoff Category libmicrohttpd internal select => internal event loop