Forráskód Böngészése

systemd and sendfile support

Christian Grothoff 15 éve
szülő
commit
8157744d45
8 módosított fájl, 265 hozzáadás és 111 törlés
  1. 5 0
      ChangeLog
  2. 1 0
      README
  3. 35 0
      doc/microhttpd.texi
  4. 12 23
      src/daemon/connection.c
  5. 113 78
      src/daemon/daemon.c
  6. 13 8
      src/daemon/internal.h
  7. 64 0
      src/daemon/response.c
  8. 22 2
      src/include/microhttpd.h

+ 5 - 0
ChangeLog

@@ -1,3 +1,8 @@
+Sun Jul 25 14:57:47 CEST 2010
+	Adding support for sendfile on Linux.  Adding support
+	for systemd-style passing of an existing listen socket
+	as an option. -CG
+
 Sun Jul 25 11:10:45 CEST 2010
 	Changed code to use external libgnutls code instead of
 	the "fork".  Minor API changes for setting TLS options. -CG

+ 1 - 0
README

@@ -95,6 +95,7 @@ Functions not covered by "make check":
 - MHD_del_response_header 
 - MHD_get_response_headers
 - MHD_tls_connection_close
+- MHD_create_response_from_fd & sendfile support (!)
 
 
 Missing documentation:

+ 35 - 0
doc/microhttpd.texi

@@ -342,23 +342,32 @@ MHD_OPTION_URI_LOG_CALLBACK.  Finally, @code{uri} will
 be the 0-terminated URI of the request.
 
 @item MHD_OPTION_HTTPS_MEM_KEY
+@cindex SSL
+@cindex TLS
 Memory pointer to the private key to be used by the
 HTTPS daemon.  This option should be followed by an
 "const char*" argument.
 This should be used in conjunction with 'MHD_OPTION_HTTPS_MEM_CERT'.
    
 @item MHD_OPTION_HTTPS_MEM_CERT
+@cindex SSL
+@cindex TLS
 Memory pointer to the certificate to be used by the
 HTTPS daemon.  This option should be followed by an
 "const char*" argument.
 This should be used in conjunction with 'MHD_OPTION_HTTPS_MEM_KEY'.
    
 @item MHD_OPTION_CRED_TYPE
+@cindex SSL
+@cindex TLS
 Daemon credentials type.  Either certificate or anonymous,
 this option should be followed by one of the values listed in
 "enum MHD_GNUTLS_CredentialsType".
    
 @item MHD_OPTION_HTTPS_PRIORITIES
+@cindex SSL
+@cindex TLS
+@cindex cipher
 SSL/TLS protocol version and ciphers.
 This option must be followwed by an "const char *" argument
 specifying the SSL/TLS protocol versions and ciphers that
@@ -366,6 +375,13 @@ are acceptable for the application.  The string is passed
 unchanged to gnutls_priority_init.  If this option is not
 specified, ``NORMAL'' is used.
   
+@item MHD_OPTION_LISTEN_SOCKET
+@cindex systemd
+Listen socket to use.  Pass a listen socket for MHD to use
+(systemd-style).  If this option is used, MHD will not open its own
+listen socket(s). The argument passed must be of type "int" and refer
+to an existing socket that has been bound to a port and is listening.
+  
 @item MHD_OPTION_EXTERNAL_LOGGER
 @cindex logging
 Use the given function for logging error messages.
@@ -381,6 +397,7 @@ flag being set and the MHD_USE_DEBUG flag being set,
 even if this argument is used.
 
 @item MHD_OPTION_THREAD_POOL_SIZE
+@cindex performance
 Number (unsigned int) of threads in thread pool. Enable
 thread pooling by setting this value to to something
 greater than 1. Currently, thread model must be
@@ -1134,6 +1151,24 @@ Return @mynull{} on error (i.e. invalid arguments, out of memory).
 @end deftypefun
 
 
+
+@deftypefun {struct MHD_Response *} MHD_create_response_from_fd (uint64_t size, int fd)
+Create a response object.  The response object can be extended with
+header information and then it can be used any number of times.
+
+@table @var
+@item size
+size of the data portion of the response, @code{-1} for unknown;
+
+@item fd
+file descriptor referring to a file on disk with the data; will be
+closed when response is destroyed
+@end table
+
+Return @mynull{} on error (i.e. invalid arguments, out of memory).
+@end deftypefun
+
+
 @deftypefun {struct MHD_Response *} MHD_create_response_from_data (size_t size, void *data, int must_free, int must_copy)
 Create a response object.  The response object can be extended with
 header information and then it can be used any number of times.

+ 12 - 23
src/daemon/connection.c

@@ -333,6 +333,11 @@ try_ready_normal_body (struct MHD_Connection *connection)
        (response->data_size + response->data_start >
 	connection->response_write_position) )
     return MHD_YES; /* response already ready */
+#if LINUX
+  if ( (response->fd != -1) &&
+       (0 == (connection->daemon->options & MHD_USE_SSL)) )
+    return MHD_YES; /* will use sendfile */
+#endif
   ret = response->crc (response->crc_cls,
                        connection->response_write_position,
                        response->data,
@@ -1795,29 +1800,13 @@ MHD_connection_handle_write (struct MHD_Connection *connection)
               connection->state = MHD_CONNECTION_NORMAL_BODY_UNREADY;
               break;
             }
-#if HTTPS_SUPPORT
-          if (connection->daemon->options & MHD_USE_SSL)
-            {
-              ret = gnutls_record_send (connection->tls_session,
-                                             &connection->response->data
-                                             [connection->
-                                              response_write_position -
-                                              response->data_start],
-                                             response->data_size -
-                                             (connection->response_write_position
-                                              - response->data_start));
-            }
-          else
-#endif
-            {
-              ret = connection->send_cls (connection,
-                                          &response->data
-                                          [connection->response_write_position
-                                           - response->data_start],
-                                          response->data_size -
-                                          (connection->response_write_position
-                                           - response->data_start));
-            }
+	  ret = connection->send_cls (connection,
+				      &response->data
+				      [connection->response_write_position
+				       - response->data_start],
+				      response->data_size -
+				      (connection->response_write_position
+				       - response->data_start));
 #if DEBUG_SEND_DATA
           if (ret > 0)
             FPRINTF (stderr,

+ 113 - 78
src/daemon/daemon.c

@@ -40,6 +40,10 @@
 #include <poll.h>
 #endif
 
+#ifdef LINUX
+#include <sys/sendfile.h>
+#endif
+
 /**
  * Default connection limit.
  */
@@ -603,8 +607,7 @@ recv_param_adapter (struct MHD_Connection *connection, void *other, size_t i)
     return -1;
   if (0 != (connection->daemon->options & MHD_USE_SSL))
     return RECV (connection->socket_fd, other, i, MSG_NOSIGNAL);
-  else
-    return RECV (connection->socket_fd, other, i, MSG_NOSIGNAL | MSG_DONTWAIT);
+  return RECV (connection->socket_fd, other, i, MSG_NOSIGNAL | MSG_DONTWAIT);
 }
 
 /**
@@ -619,12 +622,27 @@ static ssize_t
 send_param_adapter (struct MHD_Connection *connection,
                     const void *other, size_t i)
 {
+#if LINUX
+  int fd;
+  off_t offset;
+#endif
   if (connection->socket_fd == -1)
     return -1;
   if (0 != (connection->daemon->options & MHD_USE_SSL))
     return SEND (connection->socket_fd, other, i, MSG_NOSIGNAL);
-  else
-    return SEND (connection->socket_fd, other, i, MSG_NOSIGNAL | MSG_DONTWAIT);
+#if LINUX
+  if ( (NULL != connection->response) &&
+       (-1 != (fd = connection->response->fd)) )
+    {
+      /* can use sendfile */
+      offset = (off_t) connection->response_write_position;
+      return sendfile (connection->socket_fd, 
+		       fd,
+		       &offset,
+		       i);
+    }
+#endif
+  return SEND (connection->socket_fd, other, i, MSG_NOSIGNAL | MSG_DONTWAIT);
 }
 
 
@@ -1310,7 +1328,7 @@ parse_options_va (struct MHD_Daemon *daemon,
 		     opt);	  
 #endif
           break;
-	case MHD_OPTION_CRED_TYPE:
+	case MHD_OPTION_HTTPS_CRED_TYPE:
 	  daemon->cred_type = va_arg (ap, gnutls_credentials_type_t);
 	  break;
         case MHD_OPTION_HTTPS_PRIORITIES:
@@ -1328,6 +1346,9 @@ parse_options_va (struct MHD_Daemon *daemon,
 	    return MHD_NO;
           break;
 #endif
+	case MHD_OPTION_LISTEN_SOCKET:
+	  daemon->socket_fd = va_arg (ap, int);	  
+	  break;
         case MHD_OPTION_EXTERNAL_LOGGER:
 #if HAVE_MESSAGES
           daemon->custom_error_log =
@@ -1367,7 +1388,8 @@ parse_options_va (struct MHD_Daemon *daemon,
 		    return MHD_NO;
 		  break;
 		  /* all options taking 'int' or 'enum' */
-		case MHD_OPTION_CRED_TYPE:
+		case MHD_OPTION_HTTPS_CRED_TYPE:
+		case MHD_OPTION_LISTEN_SOCKET:
 		  if (MHD_YES != parse_options (daemon,
 						servaddr,
 						opt,
@@ -1471,6 +1493,7 @@ MHD_start_daemon_va (unsigned int options,
 			"NORMAL",
 			NULL);
 #endif
+  retVal->socket_fd = -1;
   retVal->options = (enum MHD_OPTION)options;
   retVal->port = port;
   retVal->apc = apc;
@@ -1547,105 +1570,117 @@ MHD_start_daemon_va (unsigned int options,
       return NULL;
     }
 #endif
-  if ((options & MHD_USE_IPv6) != 0)
+  if (retVal->socket_fd == -1)
+    {
+      if ((options & MHD_USE_IPv6) != 0)
 #if HAVE_INET6
-    socket_fd = SOCKET (PF_INET6, SOCK_STREAM, 0);
+	socket_fd = SOCKET (PF_INET6, SOCK_STREAM, 0);
 #else
-    {
+      {
 #if HAVE_MESSAGES
-      fprintf (stderr, "AF_INET6 not supported\n");
+	fprintf (stderr, "AF_INET6 not supported\n");
 #endif
-      free (retVal);
-      return NULL;
-    }
-#endif
-  else
-    socket_fd = SOCKET (PF_INET, SOCK_STREAM, 0);
-  if (socket_fd == -1)
-    {
-#if HAVE_MESSAGES
-      if ((options & MHD_USE_DEBUG) != 0)
-        FPRINTF (stderr, "Call to socket failed: %s\n", STRERROR (errno));
+	free (retVal);
+	return NULL;
+      }
 #endif
-      free (retVal);
-      return NULL;
-    }
-#ifndef WINDOWS
-  if ( (socket_fd >= FD_SETSIZE) &&
-       (0 == (options & MHD_USE_POLL)) )
-    {
+      else
+	socket_fd = SOCKET (PF_INET, SOCK_STREAM, 0);
+      if (socket_fd == -1)
+	{
 #if HAVE_MESSAGES
-      if ((options & MHD_USE_DEBUG) != 0)
-        FPRINTF (stderr,
-                 "Socket descriptor larger than FD_SETSIZE: %d > %d\n",
-                 socket_fd,
-                 FD_SETSIZE);
+	  if ((options & MHD_USE_DEBUG) != 0)
+	    FPRINTF (stderr, "Call to socket failed: %s\n", STRERROR (errno));
 #endif
-      CLOSE (socket_fd);
-      free (retVal);
-      return NULL;
-    }
-#endif
-  if ((SETSOCKOPT (socket_fd,
-                   SOL_SOCKET,
-                   SO_REUSEADDR,
-                   &on, sizeof (on)) < 0) && ((options & MHD_USE_DEBUG) != 0))
-    {
+	  free (retVal);
+	  return NULL;
+	}
+      if ((SETSOCKOPT (socket_fd,
+		       SOL_SOCKET,
+		       SO_REUSEADDR,
+		       &on, sizeof (on)) < 0) && ((options & MHD_USE_DEBUG) != 0))
+	{
 #if HAVE_MESSAGES
-      FPRINTF (stderr, "setsockopt failed: %s\n", STRERROR (errno));
+	  FPRINTF (stderr, "setsockopt failed: %s\n", STRERROR (errno));
 #endif
-    }
-
-  /* check for user supplied sockaddr */
+	}
+      
+      /* check for user supplied sockaddr */
 #if HAVE_INET6
-  if ((options & MHD_USE_IPv6) != 0)
-    addrlen = sizeof (struct sockaddr_in6);
-  else
+      if ((options & MHD_USE_IPv6) != 0)
+	addrlen = sizeof (struct sockaddr_in6);
+      else
 #endif
-    addrlen = sizeof (struct sockaddr_in);
-  if (NULL == servaddr)
-    {
+	addrlen = sizeof (struct sockaddr_in);
+      if (NULL == servaddr)
+	{
 #if HAVE_INET6
+	  if ((options & MHD_USE_IPv6) != 0)
+	    {
+	      memset (&servaddr6, 0, sizeof (struct sockaddr_in6));
+	      servaddr6.sin6_family = AF_INET6;
+	      servaddr6.sin6_port = htons (port);
+	      servaddr = (struct sockaddr *) &servaddr6;
+	    }
+	  else
+#endif
+	    {
+	      memset (&servaddr4, 0, sizeof (struct sockaddr_in));
+	      servaddr4.sin_family = AF_INET;
+	      servaddr4.sin_port = htons (port);
+	      servaddr = (struct sockaddr *) &servaddr4;
+	    }
+	}
+      retVal->socket_fd = socket_fd;
+
       if ((options & MHD_USE_IPv6) != 0)
-        {
-          memset (&servaddr6, 0, sizeof (struct sockaddr_in6));
-          servaddr6.sin6_family = AF_INET6;
-          servaddr6.sin6_port = htons (port);
-          servaddr = (struct sockaddr *) &servaddr6;
-        }
-      else
+	{
+	  const int on = 1;
+	  setsockopt (socket_fd, 
+		      IPPROTO_IPV6, IPV6_V6ONLY, 
+		      &on, sizeof (on));      
+	}
+      if (BIND (socket_fd, servaddr, addrlen) == -1)
+	{
+#if HAVE_MESSAGES
+	  if ((options & MHD_USE_DEBUG) != 0)
+	    FPRINTF (stderr,
+		     "Failed to bind to port %u: %s\n", port, STRERROR (errno));
 #endif
-        {
-          memset (&servaddr4, 0, sizeof (struct sockaddr_in));
-          servaddr4.sin_family = AF_INET;
-          servaddr4.sin_port = htons (port);
-          servaddr = (struct sockaddr *) &servaddr4;
-        }
-    }
-  retVal->socket_fd = socket_fd;
-  if (BIND (socket_fd, servaddr, addrlen) == -1)
-    {
+	  CLOSE (socket_fd);
+	  free (retVal);
+	  return NULL;
+	}
+      
+      if (LISTEN (socket_fd, 20) < 0)
+	{
 #if HAVE_MESSAGES
-      if ((options & MHD_USE_DEBUG) != 0)
-        FPRINTF (stderr,
-                 "Failed to bind to port %u: %s\n", port, STRERROR (errno));
+	  if ((options & MHD_USE_DEBUG) != 0)
+	    FPRINTF (stderr,
+		     "Failed to listen for connections: %s\n", STRERROR (errno));
 #endif
-      CLOSE (socket_fd);
-      free (retVal);
-      return NULL;
+	  CLOSE (socket_fd);
+	  free (retVal);
+	  return NULL;
+	}      
     }
 
-  if (LISTEN (socket_fd, 20) < 0)
+#ifndef WINDOWS
+  if ( (socket_fd >= FD_SETSIZE) &&
+       (0 == (options & MHD_USE_POLL)) )
     {
 #if HAVE_MESSAGES
       if ((options & MHD_USE_DEBUG) != 0)
         FPRINTF (stderr,
-                 "Failed to listen for connections: %s\n", STRERROR (errno));
+                 "Socket descriptor larger than FD_SETSIZE: %d > %d\n",
+                 socket_fd,
+                 FD_SETSIZE);
 #endif
       CLOSE (socket_fd);
       free (retVal);
       return NULL;
     }
+#endif
 
   if (0 != pthread_mutex_init (&retVal->per_ip_connection_mutex, NULL))
     {

+ 13 - 8
src/daemon/internal.h

@@ -186,15 +186,15 @@ struct MHD_Response
   pthread_mutex_t mutex;
 
   /**
-   * Reference count for this response.  Free
-   * once the counter hits zero.
+   * Set to MHD_SIZE_UNKNOWN if size is not known.
    */
-  unsigned int reference_count;
+  uint64_t total_size;
 
   /**
-   * Set to MHD_SIZE_UNKNOWN if size is not known.
+   * At what offset in the stream is the
+   * beginning of data located?
    */
-  uint64_t total_size;
+  uint64_t data_start;
 
   /**
    * Size of data.
@@ -207,10 +207,15 @@ struct MHD_Response
   size_t data_buffer_size;
 
   /**
-   * At what offset in the stream is the
-   * beginning of data located?
+   * Reference count for this response.  Free
+   * once the counter hits zero.
    */
-  uint64_t data_start;
+  unsigned int reference_count;
+
+  /**
+   * File-descriptor if this response is FD-backed.
+   */
+  int fd;
 
 };
 

+ 64 - 0
src/daemon/response.c

@@ -189,6 +189,7 @@ MHD_create_response_from_callback (uint64_t size,
   if (retVal == NULL)
     return NULL;
   memset (retVal, 0, sizeof (struct MHD_Response));
+  retVal->fd = -1;
   retVal->data = (void *) &retVal[1];
   retVal->data_buffer_size = block_size;
   if (pthread_mutex_init (&retVal->mutex, NULL) != 0)
@@ -204,6 +205,68 @@ MHD_create_response_from_callback (uint64_t size,
   return retVal;
 }
 
+
+/**
+ * Given a file descriptor, read data from the file
+ * to generate the response.
+ * 
+ * @param cls pointer to the file descriptor
+ * @param pos offset in the file to access
+ * @param buf where to write the data
+ * @param max number of bytes to write at most
+ * @return number of bytes written
+ */
+static int
+file_reader (void *cls, uint64_t pos, char *buf, int max)
+{
+  int *fd = cls;
+
+  (void) lseek (*fd, pos, SEEK_SET);
+  return read (*fd, buf, max);
+}
+
+
+/**
+ * Destroy file reader context.  Closes the file
+ * descriptor.
+ *
+ * @param cls pointer to file descriptor
+ */
+static void
+free_callback (void *cls)
+{
+  int *fd = cls;
+  close (*fd);
+  *fd = -1;
+}
+
+
+/**
+ * Create a response object.  The response object can be extended with
+ * header information and then be used any number of times.
+ *
+ * @param size size of the data portion of the response
+ * @param fd file descriptor referring to a file on disk with the data
+ * @return NULL on error (i.e. invalid arguments, out of memory)
+ */
+struct MHD_Response *MHD_create_response_from_fd (size_t size,
+						  int fd)
+{
+  struct MHD_Response *ret;
+
+  ret = MHD_create_response_from_callback (size,
+					   4 * 1024,
+					   &file_reader,
+					   NULL,
+					   &free_callback);
+  if (ret == NULL)
+    return NULL;
+  ret->fd = fd;
+  ret->crc_cls = &ret->fd;
+  return ret;
+}
+
+
 /**
  * Create a response object.  The response object can be extended with
  * header information and then be used any number of times.
@@ -229,6 +292,7 @@ MHD_create_response_from_data (size_t size,
   if (retVal == NULL)
     return NULL;
   memset (retVal, 0, sizeof (struct MHD_Response));
+  retVal->fd = -1;
   if (pthread_mutex_init (&retVal->mutex, NULL) != 0)
     {
       free (retVal);

+ 22 - 2
src/include/microhttpd.h

@@ -415,13 +415,21 @@ enum MHD_OPTION
    * Followed by an argument of type
    * "gnutls_credentials_type_t".
    */
-  MHD_OPTION_CRED_TYPE = 10,
+  MHD_OPTION_HTTPS_CRED_TYPE = 10,
 
   /**
    * Memory pointer to a "const char*" specifying the
    * cipher algorithm (default: "NORMAL").
    */
-  MHD_OPTION_HTTPS_PRIORITIES = 12,
+  MHD_OPTION_HTTPS_PRIORITIES = 11,
+
+  /**
+   * Pass a listen socket for MHD to use (systemd-style).  If this
+   * option is used, MHD will not open its own listen socket(s). The
+   * argument passed must be of type "int" and refer to an
+   * existing socket that has been bound to a port and is listening.
+   */
+  MHD_OPTION_LISTEN_SOCKET = 12,
 
   /**
    * Use the given function for logging error messages.
@@ -1074,6 +1082,18 @@ struct MHD_Response *MHD_create_response_from_data (size_t size,
                                                     int must_free,
                                                     int must_copy);
 
+
+/**
+ * Create a response object.  The response object can be extended with
+ * header information and then be used any number of times.
+ *
+ * @param size size of the data portion of the response
+ * @param fd file descriptor referring to a file on disk with the data; will be closed when response is destroyed
+ * @return NULL on error (i.e. invalid arguments, out of memory)
+ */
+struct MHD_Response *MHD_create_response_from_fd (size_t size,
+						  int fd);
+
 /**
  * Destroy a response object and associated resources.  Note that
  * libmicrohttpd may keep some of the resources around if the response