View Issue Details

IDProjectCategoryView StatusLast Update
0002886libmicrohttpdperformancepublic2013-07-19 13:36
Reportergiovanni.li Assigned ToChristian Grothoff  
PrioritynormalSeverityfeatureReproducibilityalways
Status closedResolutionfixed 
Product Version0.9.26 
Target Version0.9.28Fixed in Version0.9.28 
Summary0002886: delay in transmission in the function MHD_ContentReaderCallback
Descriptionif in the function MHD_ContentReaderCallback i block the function on busy waiting i have a delay of 20 ms from when the server receives the request when responding to. I upload an example to show the problem in a simple way.
Steps To Reproducerun wireshark and apply the filter "port eq <port>" and then run minimal_example_comet.c
TagsNo tags attached.
Attached Files
comet_delay.tar.gz (112,425 bytes)
minimal_example_comet.c (3,414 bytes)   
/*
     This file is part of libmicrohttpd
     (C) 2007, 2008 Christian Grothoff (and other contributing authors)

     This library is free software; you can redistribute it and/or
     modify it under the terms of the GNU Lesser General Public
     License as published by the Free Software Foundation; either
     version 2.1 of the License, or (at your option) any later version.

     This library 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
     Lesser General Public License for more details.

     You should have received a copy of the GNU Lesser General Public
     License along with this library; if not, write to the Free Software
     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/
/**
 * @file minimal_example.c
 * @brief minimal example for how to generate an infinite stream with libmicrohttpd
 * @author Christian Grothoff
 */
#include "platform.h"
#include <microhttpd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>


int i=0;

static ssize_t
data_generator (void *cls, uint64_t pos, char *buf, size_t max)
{
  struct MHD_Connection *connection = cls;

  /* starting header send, disable TCP cork */
  {
    const int val = 0;
    int fd;

    fd = MHD_get_connection_info (connection,
				  MHD_CONNECTION_INFO_CONNECTION_FD)->connect_fd;
    setsockopt (fd, IPPROTO_TCP, TCP_CORK, &val,
		sizeof (val));
    fprintf (stderr, "SSOPT (%d): %s\n", fd, strerror (errno));
  }
  /* now set nodelay as well */
  {
    const int val = 1;
    int fd;

    fd = MHD_get_connection_info (connection,
				  MHD_CONNECTION_INFO_CONNECTION_FD)->connect_fd;
    setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, &val,
		sizeof (val));
    fprintf (stderr, "SSOPT (%d): %s\n", fd, strerror (errno));
  }


	printf(" if-while \n");
if (i > 2){
	printf("Wait on while\n Kill process\n");
	while(1);
}
  if (max < 80)
    return 0;
  memset (buf, 'A', max - 1);
  buf[79] = '\n';
  i++;
  return 80;
}

static int
ahc_echo (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 **ptr)
{
  static int aptr;
  struct MHD_Response *response;
  int ret;

  if (0 != strcmp (method, "GET"))
    return MHD_NO;              /* unexpected method */
  if (&aptr != *ptr)
    {
      /* do never respond on first call */
      *ptr = &aptr;
      return MHD_YES;
    }
  *ptr = NULL;                  /* reset when done */
  response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN,
                                                80,
                                                &data_generator, connection, NULL);
  ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
  MHD_destroy_response (response);
  return ret;
}

int
main (int argc, char *const *argv)
{
  struct MHD_Daemon *d;

  if (argc != 2)
    {
      printf ("%s PORT\n", argv[0]);
      return 1;
    }
  d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG,
                        atoi (argv[1]),
                        NULL, NULL, &ahc_echo, NULL, MHD_OPTION_END);
  if (d == NULL)
    return 1;
  (void) getc (stdin);
  MHD_stop_daemon (d);
  return 0;
}
minimal_example_comet.c (3,414 bytes)   
patch.diff (2,788 bytes)   
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 27206)
+++ ChangeLog	(working copy)
@@ -1,3 +1,8 @@
+Mon May 20 12:29:35 CEST 2013
+	Added MHD_CONNECTION_INFO_CONNECTION_FD to allow clients
+	direct access to connection socket; useful for COMET
+	applications that need to disable NAGLE (#2886). -CG
+
 Mon May 15 12:49:01 CEST 2013
 	Fixing #2859. -CG
 
Index: doc/libmicrohttpd.texi
===================================================================
--- doc/libmicrohttpd.texi	(revision 27206)
+++ doc/libmicrohttpd.texi	(working copy)
@@ -2138,6 +2138,18 @@
 Returns information about @code{struct MHD_Daemon} which manages
 this connection.
 
+@item MHD_CONNECTION_INFO_CONNECTION_FD
+Returns the file descriptor (usually a TCP socket) associated with
+this connection (in the ``connect-fd'' member of the returned struct).
+Note that manipulating the descriptor directly can have problematic
+consequences (as in, break HTTP).  Applications might use this access
+to manipulate TCP options, for example to set the ``TCP-NODELAY''
+option for COMET-like applications.  Note that MHD will set TCP-CORK
+after sending the HTTP header and clear it after finishing the footers
+automatically (if the platform supports it).  As the connection
+callbacks are invoked in between, those might be used to set different
+values for TCP-CORK and TCP-NODELAY in the meantime.
+
 @end table
 @end deftp
 
Index: src/include/microhttpd.h
===================================================================
--- src/include/microhttpd.h	(revision 27206)
+++ src/include/microhttpd.h	(working copy)
@@ -791,8 +791,15 @@
   /**
    * Get the 'struct MHD_Daemon' responsible for managing this connection.
    */
-  MHD_CONNECTION_INFO_DAEMON
+  MHD_CONNECTION_INFO_DAEMON,
 
+
+  /**
+   * Request the file descriptor for the listening socket.
+   * No extra arguments should be passed.
+   */
+  MHD_CONNECTION_INFO_CONNECTION_FD
+
 };
 
 
@@ -1852,6 +1859,11 @@
   int /* enum gnutls_protocol */ protocol;
 
   /**
+   * Connect socket 
+   */
+  int connect_fd;
+
+  /**
    * GNUtls session handle, of type "gnutls_session_t".
    */
   void * /* gnutls_session_t */ tls_session;
Index: src/microhttpd/connection.c
===================================================================
--- src/microhttpd/connection.c	(revision 27206)
+++ src/microhttpd/connection.c	(working copy)
@@ -2555,6 +2555,8 @@
       return (const union MHD_ConnectionInfo *) &connection->addr;
     case MHD_CONNECTION_INFO_DAEMON:
       return (const union MHD_ConnectionInfo *) &connection->daemon;
+    case MHD_CONNECTION_INFO_CONNECTION_FD:
+      return (const union MHD_ConnectionInfo *) &connection->socket_fd;
     default:
       return NULL;
     };
patch.diff (2,788 bytes)   

Activities

giovanni.li

2013-05-19 19:41

reporter   ~0007107

the delay is not due to the while loop

Christian Grothoff

2013-05-20 12:32

manager   ~0007108

Ok, the 200 ms are caused by TCP NAGLE (read http://www.techrepublic.com/article/tcpip-options-for-high-performance-data-transmission/1050878 for an explanation). I've now (SVN 27208) extended the MHD API to allow you direct access to the socket so you can disable TCP NAGLE and get rid of those 200 ms.
This needs to be done in a rather specific way; I've attached a version of your example that does it and updated the manual (read the DIFF!).

Issue History

Date Modified Username Field Change
2013-05-19 19:15 giovanni.li New Issue
2013-05-19 19:15 giovanni.li File Added: comet_delay.tar.gz
2013-05-19 19:41 giovanni.li Note Added: 0007107
2013-05-20 12:30 Christian Grothoff File Added: minimal_example_comet.c
2013-05-20 12:31 Christian Grothoff File Added: patch.diff
2013-05-20 12:32 Christian Grothoff Note Added: 0007108
2013-05-20 12:32 Christian Grothoff Status new => resolved
2013-05-20 12:32 Christian Grothoff Fixed in Version => 0.9.28
2013-05-20 12:32 Christian Grothoff Resolution open => fixed
2013-05-20 12:32 Christian Grothoff Assigned To => Christian Grothoff
2013-05-20 12:33 Christian Grothoff Severity minor => feature
2013-05-20 12:33 Christian Grothoff Target Version => 0.9.28
2013-07-19 13:36 Christian Grothoff Status resolved => closed