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;

Reply via email to