View Issue Details
ID | Project | Category | View Status | Date Submitted | Last Update |
---|---|---|---|---|---|
0003924 | libmicrohttpd | external even loop | public | 2015-07-29 12:12 | 2021-09-02 17:54 |
Reporter | slimp | Assigned To | Christian Grothoff | ||
Priority | normal | Severity | minor | Reproducibility | always |
Status | closed | Resolution | fixed | ||
Platform | x86_64 | OS | CentOS Linux | OS Version | 7.1.1503 |
Product Version | 0.9.42 | ||||
Target Version | 0.9.43 | Fixed in Version | 0.9.43 | ||
Summary | 0003924: MHD_get_timeout began always return 0 | ||||
Description | If you will not call MHD_add_connection libmicrohttpd the previous complete response will not drop and after MHD_OPTION_CONNECTION_TIMEOUT seconds you will get MHD_get_timeout() == 0 No one future API call can stop it It doesn't reproduced on debian 8 with default library set. I can see that response callback destroyed normally on it | ||||
Steps To Reproduce | Build with default library set 0. I'm using external accept and use MHD_add_connection to push it to daemon 1. Process single connection with response: MHD_create_response_from_callback( -1, 4096, &ConnectionReader::Read, (void *)new ConnectionReader(m_self), &ConnectionReader::Free); Then I have see that connection was destroyed (MHD_OPTION_NOTIFY_COMPLETED called) but ConnectionReader::Free was not called. 2. Wait for timeout given by MHD_OPTION_CONNECTION_TIMEOUT (i have 600 seconds). You should not do MHD_add_connection calls Now you will always recieve MHD_get_timeout() == 0 | ||||
Tags | No tags attached. | ||||
|
I have used ssl with ipv4: opts.push_back(Option(MHD_OPTION_HTTPS_MEM_KEY, 0, key.c_str())); opts.push_back(Option(MHD_OPTION_HTTPS_MEM_CERT, 0, cert.c_str())); opts.push_back(Option(MHD_OPTION_HTTPS_PRIORITIES, 0, "NORMAL:-VERS-SSL3.0")); opts.push_back(Option(MHD_OPTION_NOTIFY_COMPLETED, (intptr_t)&ConnectionData::Free)); opts.push_back(Option(MHD_OPTION_CONNECTION_TIMEOUT, binding.connection_timeout)); //opts.push_back(Option(MHD_OPTION_LISTEN_SOCKET, 0)); //opts.push_back(Option(MHD_OPTION_CONNECTION_LIMIT, 256 + 128)); opts.push_back(Option(MHD_OPTION_CONNECTION_MEMORY_LIMIT, 1048576)); opts.push_back(Option(MHD_OPTION_END, 0, nullptr)); m_daemon = MHD_start_daemon( MHD_USE_NO_LISTEN_SOCKET | MHD_USE_SUSPEND_RESUME | (ssl ? MHD_USE_SSL : MHD_NO_FLAG) | (binding.ip.find(':') == string::npos ? MHD_NO_FLAG : MHD_USE_IPv6), binding.port, nullptr, nullptr, (!ssl && binding.redirect) ? &Daemon::HTTP_Redirect : &Daemon::HTTP_Main, this, MHD_OPTION_ARRAY, &opts[0], MHD_OPTION_END); |
|
Reproduced only inside openvz container ... |
|
Why do you think this is incorrect behavior? MHD_get_timeout() returns MHD_NO (0) if there is no timeout. If there is no active connection (which is correct, as you said, you got the completion callback), then no timeout is the correct response. Note that the reason why your ::Free handler was not called is probably that you failed to call MHD_response_destroy() *after* MHD_queue_response(), so the reference counter is still > 0 (as you are free to queue the same response object with multiple connections!). Finally, you should not pass '-1' to create_response_from_callback, but use MHD_SIZE_UNKNOWN. I'll change this bug to 'feedback': please either clarify the report or confirm that you just didn't quite use (or understand) the API correctly. |
|
I have managed to reproduce it. Simple example (it was written only to investigate the problem): #include <sys/types.h> #include <sys/select.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #include <microhttpd.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #define PORT 8888 ssize_t Reader(void *cls, uint64_t pos, char *buf, size_t max) { printf("Reader(%llu)\n", pos); ssize_t res; switch (*(int *)cls) { case 0: printf("call reader\n"); strcpy(buf, "test"); res = 4; break; case 1: printf("call reader. No result\n"); res = 0; break; case 2: printf("call reader\n"); strcpy(buf, "test"); res = 4; break; case 3: case 4: printf("delay before done\n"); res = 0; break; default: printf("call reader. finish\n"); res = MHD_CONTENT_READER_END_OF_STREAM; } ++(*((int *)cls)); return res; } void Deleter(void *cls) { printf("~DROP READER\n"); free(cls); } void DropConnection(void *cls, struct MHD_Connection *connection, void **con_cls, enum MHD_RequestTerminationCode toe) { printf("~DROP CONNECTION\n"); } int answer_to_connection (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) { const char *page = "<html><body>Hello, browser!</body></html>"; struct MHD_Response *response; int ret; printf("request\n"); void *arg = malloc(sizeof(int)); *((int *)arg) = 0; response = MHD_create_response_from_callback (-1, 4096, Reader, arg, Deleter); ret = MHD_queue_response (connection, MHD_HTTP_OK, response); MHD_destroy_response (response); return ret; } void Logger(void * arg, const char * fmt, va_list ap) { vprintf(fmt, ap); } int main () { struct MHD_Daemon *daemon; daemon = MHD_start_daemon (MHD_USE_NO_LISTEN_SOCKET|MHD_USE_DEBUG, PORT, NULL, NULL, &answer_to_connection, NULL, MHD_OPTION_NOTIFY_COMPLETED, (intptr_t)&DropConnection, (void *)0, MHD_OPTION_EXTERNAL_LOGGER, (intptr_t)&Logger, (void *)0, MHD_OPTION_CONNECTION_TIMEOUT, 30, (void *)0, MHD_OPTION_END); if (NULL == daemon) err(1, "daemon error"); struct sockaddr_in addr; bzero(&addr, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(PORT); addr.sin_addr.s_addr = inet_addr("0.0.0.0"); int sock = socket(AF_INET, SOCK_STREAM|SOCK_NONBLOCK, 0); int opt = 1; if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) err(1, "sockopt error"); if (bind(sock, (struct sockaddr *)&addr, sizeof(addr))) err(1, "bind error"); if (listen(sock, 5)) err(1, "listen error"); do { int m_max = 0; fd_set m_read, m_write, m_except; FD_ZERO(&m_read); FD_ZERO(&m_write); FD_ZERO(&m_except); if (MHD_YES != MHD_get_fdset(daemon, &m_read, &m_write, &m_except, &m_max)) err(1, "fdset"); if (m_max) ++m_max; printf("daemon max %d\n", m_max); unsigned long long mhd_timeout; struct timeval timeout; if (MHD_YES == MHD_get_timeout(daemon, &mhd_timeout)) { timeout.tv_sec = mhd_timeout / 1000; timeout.tv_usec = mhd_timeout % 1000; printf("select with timeout %llu\n", mhd_timeout); } else { timeout.tv_sec = 30; timeout.tv_usec = 0; printf("select with default timeout 30\n"); } FD_SET(sock, &m_read); if (sock + 1 > m_max) m_max = sock + 1; printf("select max = %d\n", m_max); select(m_max, &m_read, &m_write, &m_except, &timeout); printf("done\n"); socklen_t addr_len = sizeof(addr); int client = accept(sock, (struct sockaddr *)&addr, &addr_len); if (client != -1) { printf("new client\n"); MHD_add_connection(daemon, client, (struct sockaddr *)&addr, addr_len); } else printf("new data\n"); } while ( MHD_YES == MHD_run(daemon)); MHD_stop_daemon (daemon); return 0; } start it and do request. than you should wait 30 seconds - it will hang. I know that I should use MHD_suspend_connection. But it bring me another problems - i had to control timeout for this connections manually... |
|
Problem is that you check suspend (was it suspend when I have return 0 in reader callback ?) connections in MHD_get_timeout. But doesn't drop it if timeout happens |
|
Ah, I finally even understand what the issue is and when it occurs. Should be fixed in SVN 36201. Thanks for persisting & writing a test. |
|
Fix committed to master branch. |
Date Modified | Username | Field | Change |
---|---|---|---|
2015-07-29 12:12 | slimp | New Issue | |
2015-07-29 12:15 | slimp | Note Added: 0009507 | |
2015-07-30 04:05 | slimp | Note Added: 0009508 | |
2015-08-02 22:00 | Christian Grothoff | Note Added: 0009517 | |
2015-08-02 22:00 | Christian Grothoff | Assigned To | => Christian Grothoff |
2015-08-02 22:00 | Christian Grothoff | Status | new => feedback |
2015-08-04 04:10 | slimp | Note Added: 0009527 | |
2015-08-04 04:10 | slimp | Status | feedback => assigned |
2015-08-04 04:15 | slimp | Note Added: 0009528 | |
2015-08-04 13:52 | Christian Grothoff | Note Added: 0009529 | |
2015-08-04 13:52 | Christian Grothoff | Status | assigned => resolved |
2015-08-04 13:52 | Christian Grothoff | Fixed in Version | => 0.9.43 |
2015-08-04 13:52 | Christian Grothoff | Resolution | open => fixed |
2015-08-04 13:53 | Christian Grothoff | Target Version | => 0.9.43 |
2015-09-16 11:00 | Christian Grothoff | Status | resolved => closed |
2021-09-02 17:54 | Christian Grothoff | Changeset attached | => libmicrohttpd master d5048c22 |
2021-09-02 17:54 | Christian Grothoff | Note Added: 0018192 | |
2024-01-21 13:24 | Christian Grothoff | Category | libmicrohttpd external select => external even loop |