View Issue Details
ID | Project | Category | View Status | Date Submitted | Last Update |
---|---|---|---|---|---|
0005295 | libmicrohttpd | internal event loop | public | 2018-03-09 10:17 | 2018-11-06 19:46 |
Reporter | ghaderer | Assigned To | Christian Grothoff | ||
Priority | normal | Severity | major | Reproducibility | always |
Status | closed | Resolution | duplicate | ||
Platform | amd64 | OS | Linux | OS Version | 16.04.4 LTS |
Product Version | 0.9.59 | ||||
Target Version | 0.9.60 | Fixed in Version | 0.9.60 | ||
Summary | 0005295: connection forcibly closed when response reader returns no data | ||||
Description | I'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 Reproduce | Compile 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 Information | The 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) { >>> | ||||
Tags | No 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; } | ||||
duplicate of | 0005278 | closed | Christian Grothoff | Long running connections are aborted due to incomplete logic change |
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 |