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 |