gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] r33743 - in libmicrohttpd: . src/include src/microhttpd


From: gnunet
Subject: [GNUnet-SVN] r33743 - in libmicrohttpd: . src/include src/microhttpd
Date: Sun, 22 Jun 2014 12:25:47 +0200

Author: grothoff
Date: 2014-06-22 12:25:46 +0200 (Sun, 22 Jun 2014)
New Revision: 33743

Modified:
   libmicrohttpd/ChangeLog
   libmicrohttpd/src/include/microhttpd.h
   libmicrohttpd/src/microhttpd/connection.c
Log:
better fix for the concurrent response modification issue: do not modify 
response to add extra headers, only add them into the connection output buffer

Modified: libmicrohttpd/ChangeLog
===================================================================
--- libmicrohttpd/ChangeLog     2014-06-21 22:22:55 UTC (rev 33742)
+++ libmicrohttpd/ChangeLog     2014-06-22 10:25:46 UTC (rev 33743)
@@ -1,3 +1,11 @@
+Sun Jun 22 12:22:08 CEST 2014
+       Actually, avoid locking on response as responses must
+       not be modified in a connection-specific way; instead
+       modify the connection's data buffer to add missing
+       responses headers.  If we are forced to add
+       "Connection: close", suppress output of conflicting
+       application-provided "Connection: Keep-Alive" header. -CG
+
 Sun Jun 22 00:22:08 CEST 2014
        Lock on response if adding headers, needed if response
        object is shared across threads and connections. -CG

Modified: libmicrohttpd/src/include/microhttpd.h
===================================================================
--- libmicrohttpd/src/include/microhttpd.h      2014-06-21 22:22:55 UTC (rev 
33742)
+++ libmicrohttpd/src/include/microhttpd.h      2014-06-22 10:25:46 UTC (rev 
33743)
@@ -130,7 +130,7 @@
  * Current version of the library.
  * 0x01093001 = 1.9.30-1.
  */
-#define MHD_VERSION 0x00093702
+#define MHD_VERSION 0x00093703
 
 /**
  * MHD-internal return code for "YES".

Modified: libmicrohttpd/src/microhttpd/connection.c
===================================================================
--- libmicrohttpd/src/microhttpd/connection.c   2014-06-21 22:22:55 UTC (rev 
33742)
+++ libmicrohttpd/src/microhttpd/connection.c   2014-06-22 10:25:46 UTC (rev 
33743)
@@ -557,132 +557,6 @@
 
 
 /**
- * Check if we need to set some additional headers
- * for HTTP-compiliance.
- *
- * @param connection connection to check (and possibly modify)
- */
-static void
-add_extra_headers (struct MHD_Connection *connection)
-{
-  const char *have_close;
-  const char *have_keepalive;
-  const char *client_close;
-  const char *have_encoding;
-  char buf[128];
-  int add_close;
-
-  client_close = MHD_lookup_connection_value (connection,
-                                             MHD_HEADER_KIND,
-                                             MHD_HTTP_HEADER_CONNECTION);
-  /* we only care about 'close', everything else is ignored */
-  if ( (NULL != client_close) &&
-       (0 != strcasecmp (client_close, "close")) )
-    client_close = NULL;
-  have_close = MHD_get_response_header (connection->response,
-                                       MHD_HTTP_HEADER_CONNECTION);
-  have_keepalive = have_close;
-  if ( (NULL != have_close) &&
-       (0 != strcasecmp (have_close, "close")) )
-    have_close = NULL;
-  if ( (NULL != have_keepalive) &&
-       (0 != strcasecmp (have_keepalive, "Keep-Alive")) )
-    have_keepalive = NULL;
-  connection->have_chunked_upload = MHD_NO;
-  add_close = MHD_NO;
-  if (MHD_SIZE_UNKNOWN == connection->response->total_size)
-    {
-      /* size is unknown, need to either to HTTP 1.1 chunked encoding or
-        close the connection */
-      if (NULL == have_close)
-        {
-         /* 'close' header doesn't exist yet, see if we need to add one;
-            if the client asked for a close, no need to start chunk'ing */
-          if ( (NULL == client_close) &&
-               (0 == (connection->response->flags & 
MHD_RF_HTTP_VERSION_1_0_ONLY) ) &&
-               (MHD_YES == keepalive_possible (connection)) &&
-               (0 == strcasecmp (connection->version,
-                                 MHD_HTTP_VERSION_1_1)) )
-            {
-              have_encoding = MHD_get_response_header (connection->response,
-                                                      
MHD_HTTP_HEADER_TRANSFER_ENCODING);
-              if (NULL == have_encoding)
-              {
-                MHD_add_response_header (connection->response,
-                                         MHD_HTTP_HEADER_TRANSFER_ENCODING,
-                                         "chunked");
-                connection->have_chunked_upload = MHD_YES;
-              }
-             else if (0 != strcasecmp (have_encoding, "chunked"))
-              {
-               add_close = MHD_YES; /* application already set some strange 
encoding, can't do 'chunked' */
-              }
-              else
-              {
-                connection->have_chunked_upload = MHD_YES;
-              }
-            }
-          else
-            {
-             /* HTTP not 1.1 or client asked for close => set close header */
-             add_close = MHD_YES;
-            }
-        }
-    }
-  else
-    {
-      /* check if we should add a 'close' anyway */
-      if ( (NULL != client_close) &&
-          (NULL == have_close) )
-       add_close = MHD_YES; /* client asked for it, so add it */
-
-      /* if not present, add content length */
-      if ( (NULL == MHD_get_response_header (connection->response,
-                                            MHD_HTTP_HEADER_CONTENT_LENGTH)) &&
-          ( (NULL == connection->method) ||
-            (0 != strcasecmp (connection->method,
-                              MHD_HTTP_METHOD_CONNECT)) ||
-            (MHD_SIZE_UNKNOWN != connection->response->total_size) ) )
-       {
-         /*
-            Here we add a content-length if one is missing; however,
-            for 'connect' methods, the responses MUST NOT include a
-            content-length header *if* the response code is 2xx (in
-            which case we expect there to be no body).  Still,
-            as we don't know the response code here in some cases, we
-            simply only force adding a content-length header if this
-            is not a 'connect' or if the response is not empty
-            (which is kind of more sane, because if some crazy
-            application did return content with a 2xx status code,
-            then having a content-length might again be a good idea).
-
-            Note that the change from 'SHOULD NOT' to 'MUST NOT' is
-            a recent development of the HTTP 1.1 specification.
-         */
-         sprintf (buf,
-                  MHD_UNSIGNED_LONG_LONG_PRINTF,
-                  (MHD_UNSIGNED_LONG_LONG) connection->response->total_size);
-         MHD_add_response_header (connection->response,
-                                  MHD_HTTP_HEADER_CONTENT_LENGTH, buf);
-       }
-    }
-  if ( (MHD_YES == add_close) &&
-       (0 == (connection->response->flags & MHD_RF_HTTP_VERSION_1_0_ONLY) ) )
-    MHD_add_response_header (connection->response,
-                            MHD_HTTP_HEADER_CONNECTION,
-                             "close");
-  if ( (NULL == have_keepalive) &&
-       (NULL == have_close) &&
-       (MHD_NO == add_close) &&
-       (0 == (connection->response->flags & MHD_RF_HTTP_VERSION_1_0_ONLY) ) &&
-       (MHD_YES == keepalive_possible (connection)) )
-    MHD_add_response_header (connection->response,
-                            MHD_HTTP_HEADER_CONNECTION,
-                             "Keep-Alive");
-}
-
-
-/**
  * Produce HTTP "Date:" header.
  *
  * @param date where to write the header, with
@@ -770,7 +644,8 @@
 /**
  * Allocate the connection's write buffer and fill it with all of the
  * headers (or footers, if we have already sent the body) from the
- * HTTPd's response.
+ * HTTPd's response.  If headers are missing in the response supplied
+ * by the application, additional headers may be added here.
  *
  * @param connection the connection
  * @return #MHD_YES on success, #MHD_NO on failure (out of memory)
@@ -783,12 +658,21 @@
   struct MHD_HTTP_Header *pos;
   char code[256];
   char date[128];
+  char content_length_buf[128];
+  size_t content_length_len;
   char *data;
   enum MHD_ValueKind kind;
   const char *reason_phrase;
   uint32_t rc;
+  const char *client_requested_close;
+  const char *response_has_close;
+  const char *response_has_keepalive;
+  const char *have_encoding;
+  const char *have_content_length;
   int must_add_close;
-  const char *end;
+  int must_add_chunked_encoding;
+  int must_add_keep_alive;
+  int must_add_content_length;
 
   EXTRA_CHECK (NULL != connection->version);
   if (0 == strlen (connection->version))
@@ -802,7 +686,6 @@
     }
   if (MHD_CONNECTION_FOOTERS_RECEIVED == connection->state)
     {
-      add_extra_headers (connection);
       rc = connection->responseCode & (~MHD_ICY_FLAG);
       reason_phrase = MHD_get_reason_phrase_for (rc);
       sprintf (code,
@@ -834,16 +717,141 @@
       kind = MHD_FOOTER_KIND;
       off = 0;
     }
-  end = MHD_get_response_header (connection->response,
-                                 MHD_HTTP_HEADER_CONNECTION);
-  must_add_close = ( (MHD_CONNECTION_FOOTERS_RECEIVED == connection->state) &&
-                    (MHD_YES == connection->read_closed) &&
-                     (MHD_YES == keepalive_possible (connection)) &&
-                    (NULL == end) );
+
+  /* calculate extra headers we need to add, such as 'Connection: close',
+     first see what was explicitly requested by the application */
+  must_add_close = MHD_NO;
+  must_add_chunked_encoding = MHD_NO;
+  must_add_keep_alive = MHD_NO;
+  must_add_content_length = MHD_NO;
+  switch (connection->state)
+    {
+    case MHD_CONNECTION_FOOTERS_RECEIVED:
+      response_has_close = MHD_get_response_header (connection->response,
+                                                    
MHD_HTTP_HEADER_CONNECTION);
+      response_has_keepalive = response_has_close;
+      if ( (NULL != response_has_close) &&
+           (0 != strcasecmp (response_has_close, "close")) )
+        response_has_close = NULL;
+      if ( (NULL != response_has_keepalive) &&
+           (0 != strcasecmp (response_has_keepalive, "Keep-Alive")) )
+        response_has_keepalive = NULL;
+      client_requested_close = MHD_lookup_connection_value (connection,
+                                                            MHD_HEADER_KIND,
+                                                            
MHD_HTTP_HEADER_CONNECTION);
+      if ( (NULL != client_requested_close) &&
+           (0 != strcasecmp (client_requested_close, "close")) )
+        client_requested_close = NULL;
+
+      /* now analyze chunked encoding situation */
+      connection->have_chunked_upload = MHD_NO;
+
+      if ( (MHD_SIZE_UNKNOWN == connection->response->total_size) &&
+           (NULL == response_has_close) &&
+           (NULL == client_requested_close) )
+        {
+          /* size is unknown, and close was not explicitly requested;
+             need to either to HTTP 1.1 chunked encoding or
+             close the connection */
+          /* 'close' header doesn't exist yet, see if we need to add one;
+             if the client asked for a close, no need to start chunk'ing */
+          if (MHD_YES == keepalive_possible (connection))
+            {
+              have_encoding = MHD_get_response_header (connection->response,
+                                                       
MHD_HTTP_HEADER_TRANSFER_ENCODING);
+              if (NULL == have_encoding)
+                {
+                  must_add_chunked_encoding = MHD_YES;
+                  connection->have_chunked_upload = MHD_YES;
+                }
+              else if (0 == strcasecmp (have_encoding, "identity"))
+                {
+                  /* application forced identity encoding, can't do 'chunked' 
*/
+                  must_add_close = MHD_YES;
+                }
+              else
+                {
+                  connection->have_chunked_upload = MHD_YES;
+                }
+            }
+          else
+            {
+              /* Keep alive not possible => set close header if not present */
+              if (NULL == response_has_close)
+                must_add_close = MHD_YES;
+            }
+        }
+
+      /* check for other reasons to add 'close' header */
+      if ( ( (NULL != client_requested_close) ||
+             (MHD_YES == connection->read_closed) ) &&
+           (NULL == response_has_close) &&
+           (0 != (connection->response->flags & MHD_RF_HTTP_VERSION_1_0_ONLY) 
) )
+        must_add_close = MHD_YES;
+
+      /* check if we should add a 'content length' header */
+      have_content_length = MHD_get_response_header (connection->response,
+                                                     
MHD_HTTP_HEADER_CONTENT_LENGTH);
+
+      if ( (MHD_SIZE_UNKNOWN != connection->response->total_size) &&
+           (NULL == have_content_length) &&
+           ( (NULL == connection->method) ||
+             (0 != strcasecmp (connection->method,
+                               MHD_HTTP_METHOD_CONNECT)) ) )
+        {
+          /*
+            Here we add a content-length if one is missing; however,
+            for 'connect' methods, the responses MUST NOT include a
+            content-length header *if* the response code is 2xx (in
+            which case we expect there to be no body).  Still,
+            as we don't know the response code here in some cases, we
+            simply only force adding a content-length header if this
+            is not a 'connect' or if the response is not empty
+            (which is kind of more sane, because if some crazy
+            application did return content with a 2xx status code,
+            then having a content-length might again be a good idea).
+
+            Note that the change from 'SHOULD NOT' to 'MUST NOT' is
+            a recent development of the HTTP 1.1 specification.
+          */
+          content_length_len
+            = sprintf (content_length_buf,
+                       MHD_HTTP_HEADER_CONTENT_LENGTH ": " 
MHD_UNSIGNED_LONG_LONG_PRINTF "\r\n",
+                       (MHD_UNSIGNED_LONG_LONG) 
connection->response->total_size);
+          must_add_content_length = MHD_YES;
+        }
+
+      /* check for adding keep alive */
+      if ( (NULL == response_has_keepalive) &&
+           (NULL == response_has_close) &&
+           (MHD_NO == must_add_close) &&
+           (0 == (connection->response->flags & MHD_RF_HTTP_VERSION_1_0_ONLY) 
) &&
+           (MHD_YES == keepalive_possible (connection)) )
+        must_add_keep_alive = MHD_YES;
+      break;
+    case MHD_CONNECTION_BODY_SENT:
+      break;
+    default:
+      EXTRA_CHECK (0);
+    }
+
   if (must_add_close)
     size += strlen ("Connection: close\r\n");
+  if (must_add_keep_alive)
+    size += strlen ("Connection: Keep-Alive\r\n");
+  if (must_add_chunked_encoding)
+    size += strlen ("Transfer-Encoding: chunked\r\n");
+  if (must_add_content_length)
+    size += content_length_len;
+  EXTRA_CHECK (! (must_add_close && must_add_keep_alive) );
+  EXTRA_CHECK (! (must_add_chunked_encoding && must_add_content_length) );
+
   for (pos = connection->response->first_header; NULL != pos; pos = pos->next)
-    if (pos->kind == kind)
+    if ( (pos->kind == kind) &&
+         (! ( (pos->value == response_has_keepalive) &&
+              (MHD_YES == must_add_close) &&
+              (0 == strcasecmp (pos->header,
+                                MHD_HTTP_HEADER_CONNECTION) ) ) ) )
       size += strlen (pos->header) + strlen (pos->value) + 4; /* colon, space, 
linefeeds */
   /* produce data */
   data = MHD_pool_allocate (connection->pool, size + 1, MHD_NO);
@@ -861,21 +869,47 @@
     }
   if (must_add_close)
     {
-      /* we must add the 'close' header because circumstances forced us to
-        stop reading from the socket; however, we are not adding the header
-        to the response as the response may be used in a different context
-        as well */
-      memcpy (&data[off], "Connection: close\r\n",
+      /* we must add the 'Connection: close' header */
+      memcpy (&data[off],
+              "Connection: close\r\n",
              strlen ("Connection: close\r\n"));
       off += strlen ("Connection: close\r\n");
     }
+  if (must_add_keep_alive)
+    {
+      /* we must add the 'Connection: Keep-Alive' header */
+      memcpy (&data[off],
+              "Connection: Keep-Alive\r\n",
+             strlen ("Connection: Keep-Alive\r\n"));
+      off += strlen ("Connection: Keep-Alive\r\n");
+    }
+  if (must_add_chunked_encoding)
+    {
+      /* we must add the 'Transfer-Encoding: chunked' header */
+      memcpy (&data[off],
+              "Transfer-Encoding: chunked\r\n",
+             strlen ("Transfer-Encoding: chunked\r\n"));
+      off += strlen ("Transfer-Encoding: chunked\r\n");
+    }
+  if (must_add_content_length)
+    {
+      /* we must add the 'Content-Length' header */
+      memcpy (&data[off],
+              content_length_buf,
+             content_length_len);
+      off += content_length_len;
+    }
   for (pos = connection->response->first_header; NULL != pos; pos = pos->next)
-    if (pos->kind == kind)
+    if ( (pos->kind == kind) &&
+         (! ( (pos->value == response_has_keepalive) &&
+              (MHD_YES == must_add_close) &&
+              (0 == strcasecmp (pos->header,
+                                MHD_HTTP_HEADER_CONNECTION) ) ) ) )
       off += sprintf (&data[off],
                      "%s: %s\r\n",
                      pos->header,
                      pos->value);
-  if (connection->state == MHD_CONNECTION_FOOTERS_RECEIVED)
+  if (MHD_CONNECTION_FOOTERS_RECEIVED == connection->state)
     {
       strcpy (&data[off], date);
       off += strlen (date);
@@ -2466,16 +2500,13 @@
             continue;
           if (NULL == connection->response)
             break;              /* try again next time */
-          (void) MHD_mutex_lock_ (&connection->response->mutex);
           if (MHD_NO == build_header_response (connection))
             {
               /* oops - close! */
-              (void) MHD_mutex_unlock_ (&connection->response->mutex);
              CONNECTION_CLOSE_ERROR (connection,
                                      "Closing connection (failed to create 
response header)\n");
               continue;
             }
-          (void) MHD_mutex_unlock_ (&connection->response->mutex);
           connection->state = MHD_CONNECTION_HEADERS_SENDING;
 
 #if HAVE_DECL_TCP_CORK
@@ -2542,16 +2573,13 @@
             (void) MHD_mutex_unlock_ (&connection->response->mutex);
           break;
         case MHD_CONNECTION_BODY_SENT:
-          (void) MHD_mutex_lock_ (&connection->response->mutex);
           if (MHD_NO == build_header_response (connection))
             {
               /* oops - close! */
-              (void) MHD_mutex_unlock_ (&connection->response->mutex);
              CONNECTION_CLOSE_ERROR (connection,
                                      "Closing connection (failed to create 
response header)\n");
               continue;
             }
-          (void) MHD_mutex_unlock_ (&connection->response->mutex);
           if ( (MHD_NO == connection->have_chunked_upload) ||
                (connection->write_buffer_send_offset ==
                 connection->write_buffer_append_offset) )
@@ -2883,7 +2911,8 @@
  */
 int
 MHD_queue_response (struct MHD_Connection *connection,
-                    unsigned int status_code, struct MHD_Response *response)
+                    unsigned int status_code,
+                    struct MHD_Response *response)
 {
   if ( (NULL == connection) ||
        (NULL == response) ||




reply via email to

[Prev in Thread] Current Thread [Next in Thread]