gnunet-svn
[Top][All Lists]
Advanced

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

[GNUnet-SVN] r37821 - in libmicrohttpd: doc src/include src/microhttpd


From: gnunet
Subject: [GNUnet-SVN] r37821 - in libmicrohttpd: doc src/include src/microhttpd
Date: Mon, 29 Aug 2016 01:09:45 +0200

Author: grothoff
Date: 2016-08-29 01:09:44 +0200 (Mon, 29 Aug 2016)
New Revision: 37821

Modified:
   libmicrohttpd/doc/libmicrohttpd.texi
   libmicrohttpd/src/include/microhttpd.h
   libmicrohttpd/src/microhttpd/connection.c
   libmicrohttpd/src/microhttpd/daemon.c
   libmicrohttpd/src/microhttpd/internal.h
   libmicrohttpd/src/microhttpd/response.c
Log:
-theoretically finishing epoll() + HTTPS-based connection upgrade logic

Modified: libmicrohttpd/doc/libmicrohttpd.texi
===================================================================
--- libmicrohttpd/doc/libmicrohttpd.texi        2016-08-28 20:51:54 UTC (rev 
37820)
+++ libmicrohttpd/doc/libmicrohttpd.texi        2016-08-28 23:09:44 UTC (rev 
37821)
@@ -587,6 +587,19 @@
 supported on Linux >= 3.6.  On other systems using this option with
 cause @code{MHD_start_daemon} to fail.
 
address@hidden MHD_USE_TLS_EPOLL_UPGRADE
address@hidden epoll
address@hidden upgrade
address@hidden https
address@hidden tls
+This option must be set if you want to use @code{epoll()} in
+combination with a server offering TLS and then upgrade connections
+(via ``101 Switching Protocols'' responses).  This requires MHD to
+open up and process an extra socket, and hence we require this
+special flag in case this is really needed.   Note that using
+this option automatically implies @code{MHD_USE_EPOLL},
address@hidden and @code{MHD_USE_SUSPEND_RESUME}.
+
 @end table
 @end deftp
 

Modified: libmicrohttpd/src/include/microhttpd.h
===================================================================
--- libmicrohttpd/src/include/microhttpd.h      2016-08-28 20:51:54 UTC (rev 
37820)
+++ libmicrohttpd/src/include/microhttpd.h      2016-08-28 23:09:44 UTC (rev 
37821)
@@ -538,11 +538,16 @@
   MHD_USE_DEBUG = 1,
 
   /**
-   * Run in HTTPS mode.
+   * Run in HTTPS mode.  Same as #MHD_USE_TLS, just using the historic name.
    */
   MHD_USE_SSL = 2,
 
   /**
+   * Run in HTTPS mode.  The modern protocol is called TLS.
+   */
+  MHD_USE_TLS = 2,
+
+  /**
    * Run using one thread per connection.
    */
   MHD_USE_THREAD_PER_CONNECTION = 4,
@@ -672,8 +677,18 @@
    * kernel >= 3.6.  On other systems, using this option cases 
#MHD_start_daemon
    * to fail.
    */
-  MHD_USE_TCP_FASTOPEN = 16384
+  MHD_USE_TCP_FASTOPEN = 16384,
 
+  /**
+   * You need to set this option if you want to use epoll() in
+   * combination with HTTPS connections and switching protocols via
+   * connection upgrades (via #MHD_create_response_for_upgrade()).
+   * This flag is required as under these circumstances we need to
+   * open up an extra file descriptor, which we do not want to do
+   * unless necessary.
+   */
+  MHD_USE_TLS_EPOLL_UPGRADE = 32768 | MHD_USE_SUSPEND_RESUME | MHD_USE_EPOLL | 
MHD_USE_TLS
+
 };
 
 

Modified: libmicrohttpd/src/microhttpd/connection.c
===================================================================
--- libmicrohttpd/src/microhttpd/connection.c   2016-08-28 20:51:54 UTC (rev 
37820)
+++ libmicrohttpd/src/microhttpd/connection.c   2016-08-28 23:09:44 UTC (rev 
37821)
@@ -3158,6 +3158,8 @@
                     unsigned int status_code,
                     struct MHD_Response *response)
 {
+  struct MHD_Daemon *daemon = connection->daemon;
+
   if ( (NULL == connection) ||
        (NULL == response) ||
        (NULL != connection->response) ||
@@ -3168,20 +3170,31 @@
        (NULL != response->upgrade_handler) )
     {
 #ifdef HAVE_MESSAGES
-      MHD_DLOG (connection->daemon,
+      MHD_DLOG (daemon,
                 "Application used invalid status code for 'upgrade' 
response!\n");
 #endif
       return MHD_NO;
     }
   if ( (NULL != response->upgrade_handler) &&
-       (0 == (connection->daemon->options & MHD_USE_SUSPEND_RESUME)) )
+       (0 == (daemon->options & MHD_USE_SUSPEND_RESUME)) )
     {
 #ifdef HAVE_MESSAGES
-      MHD_DLOG (connection->daemon,
+      MHD_DLOG (daemon,
                 "Application attempted 'upgrade' without setting 
MHD_USE_SUSPEND_RESUME!\n");
 #endif
       return MHD_NO;
     }
+  if ( (NULL != response->upgrade_handler) &&
+       (0 != (MHD_USE_EPOLL & daemon->options)) &&
+       (0 != (MHD_USE_TLS & daemon->options)) &&
+       (MHD_USE_TLS_EPOLL_UPGRADE != (MHD_USE_TLS_EPOLL_UPGRADE & 
daemon->options)) )
+    {
+#ifdef HAVE_MESSAGES
+      MHD_DLOG (daemon,
+                "Application attempted 'upgrade' HTTPS connection in epoll 
mode without setting MHD_USE_HTTPS_EPOLL_UPGRADE!\n");
+#endif
+      return MHD_NO;
+    }
   MHD_increment_response_rc (response);
   connection->response = response;
   connection->responseCode = status_code;

Modified: libmicrohttpd/src/microhttpd/daemon.c
===================================================================
--- libmicrohttpd/src/microhttpd/daemon.c       2016-08-28 20:51:54 UTC (rev 
37820)
+++ libmicrohttpd/src/microhttpd/daemon.c       2016-08-28 23:09:44 UTC (rev 
37821)
@@ -626,11 +626,15 @@
               fd_set *except_fd_set,
               MHD_socket *max_fd)
 {
-  return MHD_get_fdset2(daemon, read_fd_set,
-      write_fd_set, except_fd_set,
-      max_fd, _MHD_SYS_DEFAULT_FD_SETSIZE);
+  return MHD_get_fdset2 (daemon,
+                         read_fd_set,
+                         write_fd_set,
+                         except_fd_set,
+                         max_fd,
+                         _MHD_SYS_DEFAULT_FD_SETSIZE);
 }
 
+
 /**
  * Obtain the `select()` sets for this daemon.
  * Daemon's FDs will be added to fd_sets. To get only
@@ -729,25 +733,25 @@
     }
   for (urh = daemon->urh_head; NULL != urh; urh = urh->next)
     {
-      if ( (0 == (MHD_EPOLL_STATE_READ_READY & urh->celi_mhd)) &&
-           (! MHD_add_to_fd_set_ (urh->mhd_socket,
+      if ( (0 == (MHD_EPOLL_STATE_READ_READY & urh->mhd.celi)) &&
+           (! MHD_add_to_fd_set_ (urh->mhd.socket,
                                   read_fd_set,
                                   max_fd,
                                   fd_setsize)) )
         result = MHD_NO;
-      if ( (0 != (MHD_EPOLL_STATE_WRITE_READY & urh->celi_mhd)) &&
-           (! MHD_add_to_fd_set_ (urh->mhd_socket,
+      if ( (0 != (MHD_EPOLL_STATE_WRITE_READY & urh->mhd.celi)) &&
+           (! MHD_add_to_fd_set_ (urh->mhd.socket,
                                   write_fd_set,
                                   max_fd,
                                   fd_setsize)) )
         result = MHD_NO;
-      if ( (0 != (MHD_EPOLL_STATE_READ_READY & urh->celi_client)) &&
+      if ( (0 != (MHD_EPOLL_STATE_READ_READY & urh->app.celi)) &&
            (! MHD_add_to_fd_set_ (urh->connection->socket_fd,
                                   read_fd_set,
                                   max_fd,
                                   fd_setsize)) )
         result = MHD_NO;
-      if ( (0 != (MHD_EPOLL_STATE_WRITE_READY & urh->celi_client)) &&
+      if ( (0 != (MHD_EPOLL_STATE_WRITE_READY & urh->app.celi)) &&
            (! MHD_add_to_fd_set_ (urh->connection->socket_fd,
                                   write_fd_set,
                                   max_fd,
@@ -2175,7 +2179,7 @@
 process_urh (struct MHD_UpgradeResponseHandle *urh)
 {
   /* handle reading from TLS client and writing to application */
-  if ( (0 != (MHD_EPOLL_STATE_READ_READY & urh->celi_client)) &&
+  if ( (0 != (MHD_EPOLL_STATE_READ_READY & urh->app.celi)) &&
        (urh->in_buffer_off < urh->in_buffer_size) )
     {
       ssize_t res;
@@ -2186,7 +2190,7 @@
       if ( (GNUTLS_E_AGAIN == res) ||
            (GNUTLS_E_INTERRUPTED == res) )
         {
-          urh->celi_client &= ~MHD_EPOLL_STATE_READ_READY;
+          urh->app.celi &= ~MHD_EPOLL_STATE_READ_READY;
         }
       else if (res > 0)
         {
@@ -2193,18 +2197,18 @@
           urh->in_buffer_off += res;
         }
     }
-  if ( (0 != (MHD_EPOLL_STATE_WRITE_READY & urh->celi_mhd)) &&
+  if ( (0 != (MHD_EPOLL_STATE_WRITE_READY & urh->mhd.celi)) &&
        (urh->in_buffer_off > 0) )
     {
       size_t res;
 
-      res = write (urh->mhd_socket,
+      res = write (urh->mhd.socket,
                    urh->in_buffer,
                    urh->in_buffer_off);
       if (-1 == res)
         {
           /* FIXME: differenciate by errno? */
-          urh->celi_mhd &= ~MHD_EPOLL_STATE_WRITE_READY;
+          urh->mhd.celi &= ~MHD_EPOLL_STATE_WRITE_READY;
         }
       else
         {
@@ -2223,18 +2227,18 @@
     }
 
   /* handle reading from application and writing to HTTPS client */
-  if ( (0 != (MHD_EPOLL_STATE_READ_READY & urh->celi_mhd)) &&
+  if ( (0 != (MHD_EPOLL_STATE_READ_READY & urh->mhd.celi)) &&
        (urh->out_buffer_off < urh->out_buffer_size) )
     {
       size_t res;
 
-      res = read (urh->mhd_socket,
+      res = read (urh->mhd.socket,
                   &urh->out_buffer[urh->out_buffer_off],
                   urh->out_buffer_size - urh->out_buffer_off);
       if (-1 == res)
         {
           /* FIXME: differenciate by errno? */
-          urh->celi_mhd &= ~MHD_EPOLL_STATE_READ_READY;
+          urh->mhd.celi &= ~MHD_EPOLL_STATE_READ_READY;
         }
       else
         {
@@ -2241,7 +2245,7 @@
           urh->out_buffer_off += res;
         }
     }
-  if ( (0 != (MHD_EPOLL_STATE_WRITE_READY & urh->celi_client)) &&
+  if ( (0 != (MHD_EPOLL_STATE_WRITE_READY & urh->app.celi)) &&
        (urh->out_buffer_off > 0) )
     {
       ssize_t res;
@@ -2252,7 +2256,7 @@
       if ( (GNUTLS_E_AGAIN == res) ||
            (GNUTLS_E_INTERRUPTED == res) )
         {
-          urh->celi_client &= ~MHD_EPOLL_STATE_WRITE_READY;
+          urh->app.celi &= ~MHD_EPOLL_STATE_WRITE_READY;
         }
       else if (res > 0)
         {
@@ -2359,13 +2363,13 @@
     {
       /* update urh state based on select() output */
       if (FD_ISSET (urh->connection->socket_fd, read_fd_set))
-        urh->celi_client |= MHD_EPOLL_STATE_READ_READY;
+        urh->app.celi |= MHD_EPOLL_STATE_READ_READY;
       if (FD_ISSET (urh->connection->socket_fd, write_fd_set))
-        urh->celi_client |= MHD_EPOLL_STATE_WRITE_READY;
-      if (FD_ISSET (urh->mhd_socket, read_fd_set))
-        urh->celi_mhd |= MHD_EPOLL_STATE_READ_READY;
-      if (FD_ISSET (urh->mhd_socket, write_fd_set))
-        urh->celi_mhd |= MHD_EPOLL_STATE_WRITE_READY;
+        urh->app.celi |= MHD_EPOLL_STATE_WRITE_READY;
+      if (FD_ISSET (urh->mhd.socket, read_fd_set))
+        urh->mhd.celi |= MHD_EPOLL_STATE_READ_READY;
+      if (FD_ISSET (urh->mhd.socket, write_fd_set))
+        urh->mhd.celi |= MHD_EPOLL_STATE_WRITE_READY;
       /* call generic forwarding function for passing data */
       process_urh (urh);
     }
@@ -2632,15 +2636,15 @@
     for (urh = daemon->urh_head; NULL != urh; urh = urh->next)
       {
         p[poll_server+i].fd = urh->connection->socket_fd;
-        if (0 == (MHD_EPOLL_STATE_READ_READY & urh->celi_client))
+        if (0 == (MHD_EPOLL_STATE_READ_READY & urh->app.celi))
           p[poll_server+i].events |= POLLIN;
-        if (0 == (MHD_EPOLL_STATE_WRITE_READY & urh->celi_client))
+        if (0 == (MHD_EPOLL_STATE_WRITE_READY & urh->app.celi))
           p[poll_server+i].events |= POLLOUT;
         i++;
-        p[poll_server+i].fd = urh->mhd_socket;
-        if (0 == (MHD_EPOLL_STATE_READ_READY & urh->celi_mhd))
+        p[poll_server+i].fd = urh->mhd.socket;
+        if (0 == (MHD_EPOLL_STATE_READ_READY & urh->mhd.celi))
           p[poll_server+i].events |= POLLIN;
-        if (0 == (MHD_EPOLL_STATE_WRITE_READY & urh->celi_mhd))
+        if (0 == (MHD_EPOLL_STATE_WRITE_READY & urh->mhd.celi))
           p[poll_server+i].events |= POLLOUT;
         i++;
       }
@@ -2701,11 +2705,11 @@
         if (p[poll_server+i].fd != urh->connection->socket_fd)
           continue; /* fd mismatch, something else happened, retry later ... */
         if (0 != (p[poll_server+i].revents & POLLIN))
-          urh->celi_client |= MHD_EPOLL_STATE_READ_READY;
+          urh->app.celi |= MHD_EPOLL_STATE_READ_READY;
         if (0 != (p[poll_server+i].revents & POLLOUT))
-          urh->celi_client |= MHD_EPOLL_STATE_WRITE_READY;
+          urh->app.celi |= MHD_EPOLL_STATE_WRITE_READY;
         i++;
-        if (p[poll_server+i].fd != urh->mhd_socket)
+        if (p[poll_server+i].fd != urh->mhd.socket)
           {
             /* fd mismatch, something else happened, retry later ... */
             /* may still be able to do something based on updates
@@ -2714,9 +2718,9 @@
             continue;
           }
         if (0 != (p[poll_server+i].revents & POLLIN))
-          urh->celi_mhd |= MHD_EPOLL_STATE_READ_READY;
+          urh->mhd.celi |= MHD_EPOLL_STATE_READ_READY;
         if (0 != (p[poll_server+i].revents & POLLOUT))
-          urh->celi_mhd |= MHD_EPOLL_STATE_WRITE_READY;
+          urh->mhd.celi |= MHD_EPOLL_STATE_WRITE_READY;
         i++;
         process_urh (urh);
       }
@@ -2832,7 +2836,137 @@
 #define MAX_EVENTS 128
 
 
+#if HTTPS_SUPPORT
+
 /**
+ * Do epoll()-based processing for TLS connections that have been
+ * upgraded.  This requires a separate epoll() invocation as we
+ * cannot use the `struct MHD_Connection` data structures for
+ * the `union epoll_data` in this case.
+ */
+static int
+run_epoll_for_upgrade (struct MHD_Daemon *daemon)
+{
+  struct epoll_event events[MAX_EVENTS];
+  int num_events;
+  unsigned int i;
+
+  num_events = MAX_EVENTS;
+  while (MAX_EVENTS == num_events)
+    {
+      /* update event masks */
+      num_events = epoll_wait (daemon->epoll_upgrade_fd,
+                              events,
+                               MAX_EVENTS,
+                               0);
+      if (-1 == num_events)
+       {
+          const int err = MHD_socket_get_error_ ();
+          if (MHD_SCKT_ERR_IS_EINTR_ (err))
+           return MHD_YES;
+#ifdef HAVE_MESSAGES
+          MHD_DLOG (daemon,
+                    "Call to epoll_wait failed: %s\n",
+                    MHD_socket_strerr_ (err));
+#endif
+         return MHD_NO;
+       }
+      for (i=0;i<(unsigned int) num_events;i++)
+       {
+          struct UpgradeEpollHandle *ueh = events[i].data.ptr;
+          struct MHD_UpgradeResponseHandle *urh = ueh->urh;
+          struct epoll_event event;
+          int fd;
+
+          /* Update our state based on what is ready according to epoll() */
+          if (0 != (events[i].events & EPOLLIN))
+            ueh->celi |= MHD_EPOLL_STATE_READ_READY;
+          if (0 != (events[i].events & EPOLLOUT))
+            ueh->celi |= MHD_EPOLL_STATE_WRITE_READY;
+
+          /* shuffle data based on buffers and FD readyness */
+          process_urh (urh);
+
+          /* if we drained the IO buffer, re-add to epoll() to wait for more! 
*/
+          if (0 == (ueh->celi & MHD_EPOLL_STATE_READ_READY))
+            {
+              event.events = EPOLLIN;
+              event.data.ptr = ueh;
+              fd = (ueh == &urh->mhd) ? ueh->socket : 
urh->connection->socket_fd;
+              if (0 != epoll_ctl (daemon->epoll_upgrade_fd,
+                                  EPOLL_CTL_ADD,
+                                  fd,
+                                  &event))
+                {
+                  MHD_socket myfd;
+
+                  /* Handle error by closing OUR socket; with some
+                     luck, this should tricker the application to fail
+                     to read, and then the application should close
+                     the connection completely. */
+
+                  /* epoll documentation suggests that closing a FD
+                     automatically removes it from the epoll set;
+                     however, this is not true as if we fail to do
+                     manually remove it, we are still seeing an event
+                     for this fd in epoll, causing grief
+                     (use-after-free...) --- at least on my system. */
+                  myfd = urh->mhd.socket;
+                  if ( (fd != myfd) &&
+                       (0 != epoll_ctl (daemon->epoll_upgrade_fd,
+                                        EPOLL_CTL_DEL,
+                                        urh->mhd.socket,
+                                        NULL)) )
+                       MHD_PANIC ("Failed to remove FD from epoll set\n");
+                  urh->mhd.socket = MHD_INVALID_SOCKET;
+                  MHD_socket_close_ (myfd);
+                  continue;
+                }
+            }
+          if (0 == (ueh->celi & MHD_EPOLL_STATE_WRITE_READY))
+            {
+              event.events = EPOLLOUT;
+              event.data.ptr = ueh;
+              fd = (ueh == &ueh->urh->mhd) ? ueh->socket : 
ueh->urh->connection->socket_fd;
+              if (0 != epoll_ctl (daemon->epoll_upgrade_fd,
+                                  EPOLL_CTL_ADD,
+                                  fd,
+                                  &event))
+                {
+                  MHD_socket myfd;
+
+                  /* Handle error by closing OUR socket; with some
+                     luck, this should tricker the application to fail
+                     to read, and then the application should close
+                     the connection completely. */
+
+                  /* epoll documentation suggests that closing a FD
+                     automatically removes it from the epoll set;
+                     however, this is not true as if we fail to do
+                     manually remove it, we are still seeing an event
+                     for this fd in epoll, causing grief
+                     (use-after-free...) --- at least on my system. */
+                  myfd = urh->mhd.socket;
+                  if ( (fd != myfd) &&
+                       (0 != epoll_ctl (daemon->epoll_upgrade_fd,
+                                        EPOLL_CTL_DEL,
+                                        urh->mhd.socket,
+                                        NULL)) )
+                       MHD_PANIC ("Failed to remove FD from epoll set\n");
+
+                  urh->mhd.socket = MHD_INVALID_SOCKET;
+                  MHD_socket_close_ (myfd);
+                  continue;
+                }
+            }
+        }
+    }
+  return MHD_YES;
+}
+#endif
+
+
+/**
  * Do epoll()-based processing (this function is allowed to
  * block if @a may_block is set to #MHD_YES).
  *
@@ -2844,6 +2978,9 @@
 MHD_epoll (struct MHD_Daemon *daemon,
           int may_block)
 {
+#if HTTPS_SUPPORT
+  static const char *upgrade_marker = "upgrade_ptr";
+#endif
   struct MHD_Connection *pos;
   struct MHD_Connection *next;
   struct epoll_event events[MAX_EVENTS];
@@ -2879,6 +3016,27 @@
        }
       daemon->listen_socket_in_epoll = MHD_YES;
     }
+#if HTTPS_SUPPORT
+  if ( (MHD_NO == daemon->upgrade_fd_in_epoll) &&
+       (MHD_INVALID_SOCKET != daemon->epoll_upgrade_fd) )
+    {
+      event.events = EPOLLIN | EPOLLOUT;
+      event.data.ptr = (void *) upgrade_marker;
+      if (0 != epoll_ctl (daemon->epoll_fd,
+                         EPOLL_CTL_ADD,
+                         daemon->epoll_upgrade_fd,
+                         &event))
+       {
+#ifdef HAVE_MESSAGES
+          MHD_DLOG (daemon,
+                    "Call to epoll_ctl failed: %s\n",
+                    MHD_socket_last_strerr_ ());
+#endif
+         return MHD_NO;
+       }
+      daemon->upgrade_fd_in_epoll = MHD_YES;
+    }
+#endif
   if ( ( (MHD_YES == daemon->listen_socket_in_epoll) &&
          (daemon->connections == daemon->connection_limit) ) ||
        (MHD_YES == daemon->at_limit) )
@@ -2917,7 +3075,9 @@
     {
       /* update event masks */
       num_events = epoll_wait (daemon->epoll_fd,
-                              events, MAX_EVENTS, timeout_ms);
+                              events,
+                               MAX_EVENTS,
+                               timeout_ms);
       if (-1 == num_events)
        {
           const int err = MHD_socket_get_error_ ();
@@ -2932,8 +3092,25 @@
        }
       for (i=0;i<(unsigned int) num_events;i++)
        {
+          /* First, check for the values of `ptr` that would indicate
+             that this event is not about a normal connection. */
          if (NULL == events[i].data.ptr)
            continue; /* shutdown signal! */
+#if HTTPS_SUPPORT
+          if (upgrade_marker == events[i].data.ptr)
+            {
+              /* activity on an upgraded connection, we process
+                 those in a separate epoll() */
+              daemon->upgrade_fd_in_epoll = MHD_NO;
+              run_epoll_for_upgrade (daemon);
+              continue;
+            }
+#endif
+          /* UGH: we're storing pointers and fds in the same union
+             here; incredibly ugly and somewhat risky, even though a
+             pointer with the same numeric value as the wpipe[0] can
+             be expected to be rare... FIXME (a construction similar
+             to what we did with the `upgrade_marker` should do) */
           if ( (MHD_INVALID_PIPE_ != daemon->wpipe[0]) &&
                (daemon->wpipe[0] == events[i].data.fd) )
             {
@@ -2942,40 +3119,8 @@
               MHD_pipe_drain_ (daemon->wpipe[0]);
               continue;
             }
-         if (daemon != events[i].data.ptr)
+         if (daemon == events[i].data.ptr)
            {
-             /* this is an event relating to a 'normal' connection,
-                remember the event and if appropriate mark the
-                connection as 'eready'. */
-             pos = events[i].data.ptr;
-             if (0 != (events[i].events & EPOLLIN))
-               {
-                 pos->epoll_state |= MHD_EPOLL_STATE_READ_READY;
-                 if ( ( (MHD_EVENT_LOOP_INFO_READ == pos->event_loop_info) ||
-                        (pos->read_buffer_size > pos->read_buffer_offset) ) &&
-                      (0 == (pos->epoll_state & 
MHD_EPOLL_STATE_IN_EREADY_EDLL) ) )
-                   {
-                     EDLL_insert (daemon->eready_head,
-                                  daemon->eready_tail,
-                                  pos);
-                     pos->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL;
-                   }
-               }
-             if (0 != (events[i].events & EPOLLOUT))
-               {
-                 pos->epoll_state |= MHD_EPOLL_STATE_WRITE_READY;
-                 if ( (MHD_EVENT_LOOP_INFO_WRITE == pos->event_loop_info) &&
-                      (0 == (pos->epoll_state & 
MHD_EPOLL_STATE_IN_EREADY_EDLL) ) )
-                   {
-                     EDLL_insert (daemon->eready_head,
-                                  daemon->eready_tail,
-                                  pos);
-                     pos->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL;
-                   }
-               }
-           }
-         else /* must be listen socket */
-           {
              /* run 'accept' until it fails or we are not allowed to take
                 on more connections */
              series_length = 0;
@@ -2984,8 +3129,39 @@
                      (series_length < 128) &&
                       (MHD_NO == daemon->at_limit) )
                 series_length++;
+              continue;
            }
-       }
+          /* this is an event relating to a 'normal' connection,
+             remember the event and if appropriate mark the
+             connection as 'eready'. */
+          pos = events[i].data.ptr;
+          /* normal processing: update read/write data */
+          if (0 != (events[i].events & EPOLLIN))
+            {
+              pos->epoll_state |= MHD_EPOLL_STATE_READ_READY;
+              if ( ( (MHD_EVENT_LOOP_INFO_READ == pos->event_loop_info) ||
+                     (pos->read_buffer_size > pos->read_buffer_offset) ) &&
+                   (0 == (pos->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL) ) 
)
+                {
+                  EDLL_insert (daemon->eready_head,
+                               daemon->eready_tail,
+                               pos);
+                  pos->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL;
+                }
+            }
+          if (0 != (events[i].events & EPOLLOUT))
+            {
+              pos->epoll_state |= MHD_EPOLL_STATE_WRITE_READY;
+              if ( (MHD_EVENT_LOOP_INFO_WRITE == pos->event_loop_info) &&
+                   (0 == (pos->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL) ) 
)
+                {
+                  EDLL_insert (daemon->eready_head,
+                               daemon->eready_tail,
+                               pos);
+                  pos->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL;
+                }
+            }
+        }
     }
 
   /* we handle resumes here because we may have ready connections
@@ -3426,8 +3602,8 @@
               if (gnutls_dh_params_init (&daemon->https_mem_dhparams) < 0)
                 {
 #ifdef HAVE_MESSAGES
-                  MHD_DLOG(daemon,
-                           "Error initializing DH parameters\n");
+                  MHD_DLOG (daemon,
+                            "Error initializing DH parameters\n");
 #endif
                   return MHD_NO;
                 }
@@ -3437,8 +3613,8 @@
                                                  GNUTLS_X509_FMT_PEM) < 0)
                 {
 #ifdef HAVE_MESSAGES
-                  MHD_DLOG(daemon,
-                           "Bad Diffie-Hellman parameters format\n");
+                  MHD_DLOG (daemon,
+                            "Bad Diffie-Hellman parameters format\n");
 #endif
                   gnutls_dh_params_deinit (daemon->https_mem_dhparams);
                   return MHD_NO;
@@ -3652,24 +3828,17 @@
 
 
 #ifdef EPOLL_SUPPORT
-/**
- * Setup epoll() FD for the daemon and initialize it to listen
- * on the listen FD.
- *
- * @param daemon daemon to initialize for epoll()
- * @return #MHD_YES on success, #MHD_NO on failure
- */
 static int
-setup_epoll_to_listen (struct MHD_Daemon *daemon)
+setup_epoll_fd (struct MHD_Daemon *daemon)
 {
-  struct epoll_event event;
+  int fd;
 
 #ifdef USE_EPOLL_CREATE1
-  daemon->epoll_fd = epoll_create1 (EPOLL_CLOEXEC);
+  fd = epoll_create1 (EPOLL_CLOEXEC);
 #else  /* ! USE_EPOLL_CREATE1 */
-  daemon->epoll_fd = epoll_create (MAX_EVENTS);
+  fd = epoll_create (MAX_EVENTS);
 #endif /* ! USE_EPOLL_CREATE1 */
-  if (-1 == daemon->epoll_fd)
+  if (MHD_INVALID_SOCKET == fd)
     {
 #ifdef HAVE_MESSAGES
       MHD_DLOG (daemon,
@@ -3676,10 +3845,10 @@
                 "Call to epoll_create1 failed: %s\n",
                 MHD_socket_last_strerr_ ());
 #endif
-      return MHD_NO;
+      return MHD_INVALID_SOCKET;
     }
 #if !defined(USE_EPOLL_CREATE1)
-  if (!MHD_socket_noninheritable_ (daemon->epoll_fd))
+  if (! MHD_socket_noninheritable_ (fd))
     {
 #ifdef HAVE_MESSAGES
       MHD_DLOG (daemon,
@@ -3687,6 +3856,33 @@
 #endif
     }
 #endif /* ! USE_EPOLL_CREATE1 */
+  return fd;
+}
+
+
+/**
+ * Setup epoll() FD for the daemon and initialize it to listen
+ * on the listen FD.
+ *
+ * @param daemon daemon to initialize for epoll()
+ * @return #MHD_YES on success, #MHD_NO on failure
+ */
+static int
+setup_epoll_to_listen (struct MHD_Daemon *daemon)
+{
+  struct epoll_event event;
+
+  daemon->epoll_fd = setup_epoll_fd (daemon);
+  if (MHD_INVALID_SOCKET == daemon->epoll_fd)
+    return MHD_NO;
+#if HTTPS_SUPPORT
+  if (MHD_USE_TLS_EPOLL_UPGRADE == (MHD_USE_TLS_EPOLL_UPGRADE & 
daemon->options))
+    {
+       daemon->epoll_upgrade_fd = setup_epoll_fd (daemon);
+       if (MHD_INVALID_SOCKET == daemon->epoll_upgrade_fd)
+         return MHD_NO;
+    }
+#endif
   if (MHD_INVALID_SOCKET == daemon->socket_fd)
     return MHD_YES; /* non-listening daemon */
   event.events = EPOLLIN;
@@ -3788,7 +3984,10 @@
   memset (daemon, 0, sizeof (struct MHD_Daemon));
 #ifdef EPOLL_SUPPORT
   daemon->epoll_fd = -1;
+#if HTTPS_SUPPORT
+  daemon->epoll_upgrade_fd = -1;
 #endif
+#endif
   /* try to open listen socket */
 #if HTTPS_SUPPORT
   if (0 != (flags & MHD_USE_SSL))
@@ -3842,7 +4041,7 @@
       free (daemon);
       return NULL;
     }
-    if (!MHD_itc_nonblocking_(daemon->wpipe[0]))
+    if (! MHD_itc_nonblocking_(daemon->wpipe[0]))
       {
 #ifdef HAVE_MESSAGES
         MHD_DLOG (daemon,
@@ -4482,7 +4681,11 @@
 #ifdef EPOLL_SUPPORT
   if (-1 != daemon->epoll_fd)
     close (daemon->epoll_fd);
+#if HTTPS_SUPPORT
+  if (-1 != daemon->epoll_upgrade_fd)
+    close (daemon->epoll_upgrade_fd);
 #endif
+#endif
 #ifdef DAUTH_SUPPORT
   free (daemon->nnc);
   (void) MHD_mutex_destroy_ (&daemon->nnc_lock);
@@ -4710,7 +4913,12 @@
          if ( (-1 != daemon->worker_pool[i].epoll_fd) &&
               (0 != MHD_socket_close_ (daemon->worker_pool[i].epoll_fd)) )
            MHD_PANIC ("close failed\n");
+#if HTTPS_SUPPORT
+         if ( (-1 != daemon->worker_pool[i].epoll_upgrade_fd) &&
+              (0 != MHD_socket_close_ 
(daemon->worker_pool[i].epoll_upgrade_fd)) )
+           MHD_PANIC ("close failed\n");
 #endif
+#endif
           /* Individual pipes are always used */
           if (1)
             {
@@ -4762,7 +4970,13 @@
        (-1 != daemon->epoll_fd) &&
        (0 != MHD_socket_close_ (daemon->epoll_fd)) )
     MHD_PANIC ("close failed\n");
+#if HTTPS_SUPPORT
+  if ( (0 != (daemon->options & MHD_USE_EPOLL)) &&
+       (-1 != daemon->epoll_upgrade_fd) &&
+       (0 != MHD_socket_close_ (daemon->epoll_upgrade_fd)) )
+    MHD_PANIC ("close failed\n");
 #endif
+#endif
 
 #ifdef DAUTH_SUPPORT
   free (daemon->nnc);

Modified: libmicrohttpd/src/microhttpd/internal.h
===================================================================
--- libmicrohttpd/src/microhttpd/internal.h     2016-08-28 20:51:54 UTC (rev 
37820)
+++ libmicrohttpd/src/microhttpd/internal.h     2016-08-28 23:09:44 UTC (rev 
37821)
@@ -899,6 +899,45 @@
 #define RESERVE_EBUF_SIZE 8
 
 /**
+ * Context we pass to epoll() for each of the two sockets
+ * of a `struct MHD_UpgradeResponseHandle`.  We need to do
+ * this so we can distinguish the two sockets when epoll()
+ * gives us event notifications.
+ */
+struct UpgradeEpollHandle
+{
+  /**
+   * Reference to the overall response handle this struct is
+   * included within.
+   */
+  struct MHD_UpgradeResponseHandle *urh;
+
+  /**
+   * The socket this event is kind-of about.  Note that this is NOT
+   * necessarily the socket we are polling on, as for when we read
+   * from TLS, we epoll() on the connection's socket
+   * (`urh->connection->socket_fd`), while this then the application's
+   * socket (where the application will read from).  Nevertheless, for
+   * the application to read, we need to first read from TLS, hence
+   * the two are related.
+   *
+   * Similarly, for writing to TLS, this epoll() will be on the
+   * connection's `socket_fd`, and this will merely be the FD which
+   * the applicatio would write to.  Hence this struct must always be
+   * interpreted based on which field in `struct
+   * MHD_UpgradeResponseHandle` it is (`app` or `mhd`).
+   */
+  MHD_socket socket;
+
+  /**
+   * IO-state of the @e socket (or the connection's `socket_fd`).
+   */
+  enum MHD_EpollState celi;
+
+};
+
+
+/**
  * Handle given to the application to manage special
  * actions relating to MHD responses that "upgrade"
  * the HTTP protocol (i.e. to WebSockets).
@@ -960,25 +999,15 @@
   /**
    * The socket we gave to the application (r/w).
    */
-  MHD_socket app_socket;
+  struct UpgradeEpollHandle app;
 
   /**
    * If @a app_sock was a socketpair, our end of it, otherwise
    * #MHD_INVALID_SOCKET; (r/w).
    */
-  MHD_socket mhd_socket;
+  struct UpgradeEpollHandle mhd;
 
   /**
-   * IO-state of the @e mhd_socket.
-   */
-  enum MHD_EpollState celi_mhd;
-
-  /**
-   * IO-state of the @e connection's socket.
-   */
-  enum MHD_EpollState celi_client;
-
-  /**
    * Emergency IO buffer we use in case the memory pool has literally
    * nothing left.
    */
@@ -1257,12 +1286,27 @@
   int epoll_fd;
 
   /**
-   * MHD_YES if the listen socket is in the 'epoll' set,
-   * MHD_NO if not.
+   * #MHD_YES if the listen socket is in the 'epoll' set,
+   * #MHD_NO if not.
    */
   int listen_socket_in_epoll;
+
+#if HTTPS_SUPPORT
+  /**
+   * File descriptor associated with the #run_epoll_for_upgrade() loop.
+   * Only available if #MHD_USE_HTTPS_EPOLL_UPGRADE is set.
+   */
+  int epoll_upgrade_fd;
+
+  /**
+   * #MHD_YES if @e epoll_upgrade_fd is in the 'epoll' set,
+   * #MHD_NO if not.
+   */
+  int upgrade_fd_in_epoll;
 #endif
 
+#endif
+
   /**
    * Pipe we use to signal shutdown, unless
    * 'HAVE_LISTEN_SHUTDOWN' is defined AND we have a listen

Modified: libmicrohttpd/src/microhttpd/response.c
===================================================================
--- libmicrohttpd/src/microhttpd/response.c     2016-08-28 20:51:54 UTC (rev 
37820)
+++ libmicrohttpd/src/microhttpd/response.c     2016-08-28 23:09:44 UTC (rev 
37821)
@@ -599,7 +599,8 @@
                     enum MHD_UpgradeAction action,
                     ...)
 {
-  struct MHD_Daemon *daemon = urh->connection->daemon;
+  struct MHD_Connection *connection = urh->connection;
+  struct MHD_Daemon *daemon = connection->daemon;
 
   switch (action)
   {
@@ -611,18 +612,46 @@
         DLL_remove (daemon->urh_head,
                     daemon->urh_tail,
                     urh);
-        /* FIXME: if running in epoll()-mode, do we have
-           to remove any of the FDs from any epoll-sets here? */
-        if ( (MHD_INVALID_SOCKET != urh->app_socket) &&
-             (0 != MHD_socket_close_ (urh->app_socket)) )
-          MHD_PANIC ("close failed\n");
-        if ( (MHD_INVALID_SOCKET != urh->mhd_socket) &&
-             (0 != MHD_socket_close_ (urh->mhd_socket)) )
-          MHD_PANIC ("close failed\n");
+        if (0 != (daemon->options & MHD_USE_EPOLL))
+          {
+            /* epoll documentation suggests that closing a FD
+               automatically removes it from the epoll set; however,
+               this is not true as if we fail to do manually remove it,
+               we are still seeing an event for this fd in epoll,
+               causing grief (use-after-free...) --- at least on my
+               system. */
+            if (0 != epoll_ctl (daemon->epoll_upgrade_fd,
+                                EPOLL_CTL_DEL,
+                                connection->socket_fd,
+                                NULL))
+              MHD_PANIC ("Failed to remove FD from epoll set\n");
+          }
+        if (MHD_INVALID_SOCKET != urh->app.socket)
+          {
+            if (0 != MHD_socket_close_ (urh->app.socket))
+              MHD_PANIC ("close failed\n");
+          }
+        if (MHD_INVALID_SOCKET != urh->mhd.socket)
+          {
+            /* epoll documentation suggests that closing a FD
+               automatically removes it from the epoll set; however,
+               this is not true as if we fail to do manually remove it,
+               we are still seeing an event for this fd in epoll,
+               causing grief (use-after-free...) --- at least on my
+               system. */
+            if ( (0 != (daemon->options & MHD_USE_EPOLL)) &&
+                 (0 != epoll_ctl (daemon->epoll_upgrade_fd,
+                                  EPOLL_CTL_DEL,
+                                  urh->mhd.socket,
+                                  NULL)) )
+              MHD_PANIC ("Failed to remove FD from epoll set\n");
+            if (0 != MHD_socket_close_ (urh->mhd.socket))
+              MHD_PANIC ("close failed\n");
+          }
       }
 #endif
-    MHD_resume_connection (urh->connection);
-    MHD_connection_close_ (urh->connection,
+    MHD_resume_connection (connection);
+    MHD_connection_close_ (connection,
                            MHD_REQUEST_TERMINATED_COMPLETED_OK);
     free (urh);
     return MHD_YES;
@@ -691,6 +720,15 @@
       free (urh);
       return MHD_NO;
     }
+    if ( (! MHD_itc_nonblocking_(sv[0])) ||
+         (! MHD_itc_nonblocking_(sv[1])) )
+      {
+#ifdef HAVE_MESSAGES
+        MHD_DLOG (daemon,
+                 "Failed to make read side of inter-thread control channel 
non-blocking: %s\n",
+                 MHD_pipe_last_strerror_ ());
+#endif
+      }
     if ( (! MHD_SCKT_FD_FITS_FDSET_(sv[1], NULL)) &&
          (0 == (daemon->options & (MHD_USE_POLL | MHD_USE_EPOLL))) )
       {
@@ -707,8 +745,12 @@
         free (urh);
         return MHD_NO;
       }
-    urh->app_socket = sv[0];
-    urh->mhd_socket = sv[1];
+    urh->app.socket = sv[0];
+    urh->app.urh = urh;
+    urh->app.celi = MHD_EPOLL_STATE_UNREADY;
+    urh->mhd.socket = sv[1];
+    urh->mhd.urh = urh;
+    urh->mhd.celi = MHD_EPOLL_STATE_UNREADY;
     pool = connection->pool;
     avail = MHD_pool_get_free (pool);
     if (avail < 8)
@@ -740,26 +782,80 @@
                                connection->client_context,
                                connection->read_buffer,
                                rbo,
-                               urh->app_socket,
+                               urh->app.socket,
                                urh);
     /* As far as MHD is concerned, this connection is
        suspended; it will be resumed once we are done
        in the #MHD_upgrade_action() function */
     MHD_suspend_connection (connection);
-    urh->celi_mhd = MHD_EPOLL_STATE_UNREADY;
-    urh->celi_client = MHD_EPOLL_STATE_UNREADY;
-    /* FIXME: is it possible we did not fully drain the client
-       socket yet and are thus read-ready already? This may
-       matter if we are in epoll() edge triggered mode... */
+
     /* Launch IO processing by the event loop */
-    /* FIXME: this will not work (yet) for thread-per-connection processing */
-    DLL_insert (connection->daemon->urh_head,
-                connection->daemon->urh_tail,
+    if (0 != (daemon->options & MHD_USE_EPOLL))
+      {
+        /* We're running with epoll(), need to add the sockets
+           to the event set of the daemon's `epoll_upgrade_fd` */
+        struct epoll_event event;
+
+        EXTRA_CHECK (MHD_SOCKET_INVALID != daemon->epoll_upgrade_fd);
+        /* First, add network socket */
+        event.events = EPOLLIN | EPOLLOUT;
+        event.data.ptr = &urh->app;
+        if (0 != epoll_ctl (daemon->epoll_upgrade_fd,
+                            EPOLL_CTL_ADD,
+                            connection->socket_fd,
+                            &event))
+       {
+#ifdef HAVE_MESSAGES
+          MHD_DLOG (daemon,
+                    "Call to epoll_ctl failed: %s\n",
+                    MHD_socket_last_strerr_ ());
+#endif
+          if (0 != MHD_socket_close_ (sv[0]))
+            MHD_PANIC ("close failed\n");
+          if (0 != MHD_socket_close_ (sv[1]))
+            MHD_PANIC ("close failed\n");
+          free (urh);
+          return MHD_NO;
+       }
+
+        /* Second, add our end of the UNIX socketpair() */
+        event.events = EPOLLIN | EPOLLOUT;
+        event.data.ptr = &urh->mhd;
+        if (0 != epoll_ctl (daemon->epoll_upgrade_fd,
+                            EPOLL_CTL_ADD,
+                            urh->mhd.socket,
+                            &event))
+       {
+          event.events = EPOLLIN | EPOLLOUT;
+          event.data.ptr = &urh->app;
+          if (0 != epoll_ctl (daemon->epoll_upgrade_fd,
+                              EPOLL_CTL_DEL,
+                              connection->socket_fd,
+                              &event))
+            MHD_PANIC ("Error cleaning up while handling epoll error");
+#ifdef HAVE_MESSAGES
+          MHD_DLOG (daemon,
+                    "Call to epoll_ctl failed: %s\n",
+                    MHD_socket_last_strerr_ ());
+#endif
+          if (0 != MHD_socket_close_ (sv[0]))
+            MHD_PANIC ("close failed\n");
+          if (0 != MHD_socket_close_ (sv[1]))
+            MHD_PANIC ("close failed\n");
+          free (urh);
+          return MHD_NO;
+       }
+      }
+
+    /* This takes care of most event loops: simply add to DLL */
+    DLL_insert (daemon->urh_head,
+                daemon->urh_tail,
                 urh);
+    /* FIXME: None of the above will not work (yet) for thread-per-connection 
processing */
     return MHD_YES;
   }
-  urh->app_socket = MHD_INVALID_SOCKET;
-  urh->mhd_socket = MHD_INVALID_SOCKET;
+  urh->app.socket = MHD_INVALID_SOCKET;
+  urh->mhd.socket = MHD_INVALID_SOCKET;
 #endif
   response->upgrade_handler (response->upgrade_handler_cls,
                              connection,




reply via email to

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