Hi All.
First of all, congrats for this excellent library! I think it does
have a clear interface and it's very usable.
However, I have found two issues. I don't know if I'm using bad the
library, so please correct me if I'm wrong.
I'm trying to send, using one connection, a lot of small (~2K) POST
messages (in the real case, JSON text). If I use not chunked encoding
messages (i.e., specifying Content-Length header), all is OK.
However, I've found that if I try to send the messages in chunks, MHD
drops the connection, and the client needs to reconnect again. Testing
in deep, I've found that:
* If I increase MHD_OPTION_CONNECTION_MEMORY_LIMIT, the connections
are dropped slower (i.e., the same connection last longer).
* If I send smaller messages that the sent in http-post.c, MHD does not drop.
* If I change URL length, drops rate changes:
* ./http-post "http://10.0.70.175:8080/aaaaaaaaaaaaaaaaaaaaaaaaaaaa"
causes drops
* ./http-post
"http://10.0.70.175:8080/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" no drops
* If I apply the patch attached, MHD does not drop my connection
anymore. However, I don't know if it affects some other MHD internals.
All of this happens with MHD 0.9.44. I've seen that you just announced
0.9.45. However, if I update is even worse: connections are being
dropped almost every packet or 2 packet! If I bisect, the commit that
introduce the issue is r36568 (with message "-fix assertion failure
from race on shutdown and buffer shrinkage with pipelining").
I attach client and server I'm using to test situation. In order to
detect reconnection, I just use:
tcpdump -nni any "port 8080 and tcp[tcpflags] & (tcp-syn) != 0"
Where 8080 is the post I'm using to testing.
Am I using the bad options? Is the patch valid to fix the issue? if
not, what could I do to keep debugging this?
Thanks and regards.
#include <stdio.h>
#include <string.h>
#include <curl/curl.h>
#define ABUFSIZ (856+1)
int main(int argc, char **argv)
{
CURL *curl;
CURLcode res;
char buf[ABUFSIZ];
memset(buf,'A',sizeof(buf)-1);
/* In windows, this will init the winsock stuff */
curl_global_init(CURL_GLOBAL_ALL);
/* get a curl handle */
curl = curl_easy_init();
struct curl_slist *slist= curl_slist_append(slist, "Transfer-Encoding: chunked");
slist= curl_slist_append(slist, "Connection: Keep-Alive");
slist= curl_slist_append(slist, "Content-Type: application/json");
if(curl) {
/* First set the URL that is about to receive our POST. This URL can
just as well be a https:// URL if that is what should receive the
data. */
curl_easy_setopt(curl, CURLOPT_URL, "http://eugeniodev:2057/rbdata/abc/rb_flow");
/* Now specify the POST data */
curl_easy_setopt(curl, CURLOPT_POSTFIELDS,buf);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist);
}
while(curl) {
/* Perform the request, res will get the return code */
res = curl_easy_perform(curl);
/* Check for errors */
if(res != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
usleep(1000);
}
/* always cleanup */
curl_easy_cleanup(curl);
curl_global_cleanup();
return 0;
}
/*
** Copyright (C) 2015 Eneo Tecnologia S.L.
** Author: Eugenio Perez <[email protected]>
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU Affero General Public License as
** published by the Free Software Foundation, either version 3 of the
** License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU Affero General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define HTTP_UNUSED __attribute__((unused))
#define POSTBUFFERSIZE (10*1024)
#define MODE_THREAD_PER_CONNECTION "thread_per_connection"
#define MODE_SELECT "select"
#define MODE_POLL "poll"
#define MODE_EPOLL "epoll"
#include "platform.h"
#include <microhttpd.h>
#include <arpa/inet.h>
#include <assert.h>
#include <jansson.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
static int post_handle(void *_cls,
struct MHD_Connection *connection,
const char *url,
const char *method,
const char *version HTTP_UNUSED,
const char *upload_data,
size_t *upload_data_size,
void **ptr) {
static int a;
if (0 != strcmp(method, MHD_HTTP_METHOD_POST)) {
return MHD_NO;
}
if ( NULL == ptr) {
return MHD_NO;
}
if ( NULL == *ptr ) {
*ptr = &a;
return MHD_YES;
} else if ( *upload_data_size > 0 ) {
/* middle calls, process string sent */
*upload_data_size = 0;
return MHD_YES;
} else {
struct MHD_Response *http_response = NULL;
http_response = MHD_create_response_from_buffer(0,NULL,MHD_RESPMEM_PERSISTENT);
const int ret = MHD_queue_response(connection,MHD_HTTP_OK,http_response);
MHD_destroy_response(http_response);
return ret;
}
}
int main(int argc, char **argv) {
struct MHD_OptionItem opts[] = {
/* Memory limit per connection */
{MHD_OPTION_CONNECTION_MEMORY_LIMIT, 10*1024, NULL},
{ MHD_OPTION_END, 0, NULL }
};
struct MHD_Daemon *d = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
atoi(argv[1]),
NULL, /* Auth callback */
NULL, /* Auth callback parameter */
post_handle, /* Request handler */
NULL, /* Request handler parameter */
MHD_OPTION_ARRAY, opts,
MHD_OPTION_END);
(void)getc(stdin);
MHD_stop_daemon(d);
}
diff --git a/src/microhttpd/connection.c b/src/microhttpd/connection.c
index 626cba2..2a05e23 100644
--- a/src/microhttpd/connection.c
+++ b/src/microhttpd/connection.c
@@ -2296,7 +2296,7 @@ MHD_connection_handle_idle (struct MHD_Connection *connection)
{
case MHD_CONNECTION_INIT:
line = get_next_header_line (connection);
- if (NULL == line)
+ if (NULL == line || strlen(line)==0)
{
if (MHD_CONNECTION_INIT != connection->state)
continue;