libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

commit e3ce8fdf5739928c5250863bb2132f041d4cab64
parent d93a774bd642a7d83f775099959f10d246a3f1ca
Author: Evgeny Grin (Karlson2k) <k2k@narod.ru>
Date:   Sun, 12 Mar 2017 23:24:37 +0300

Rewritten logic of handling "upgraded" TLS connections in epoll mode.

Diffstat:
MChangeLog | 9++++++++-
Msrc/microhttpd/connection.c | 7+++++++
Msrc/microhttpd/daemon.c | 120++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
Msrc/microhttpd/internal.h | 31++++++++++++++++++++++++++++++-
Msrc/microhttpd/response.c | 8++++++--
5 files changed, 149 insertions(+), 26 deletions(-)

diff --git a/ChangeLog b/ChangeLog @@ -1,4 +1,11 @@ -Thu Mar 17 10:45:31 MSK 2017 +Sun Mar 19 13:57:30 MSK 2017 + Rewritten logic of handling "upgraded" TLS connections in epoll mode: + used edge trigger instead of level trigger, + upgraded "ready" connection are stored in DL-list, + fixed handling of more than 128 ready connections, + fixed busy-waiting for idle "upgraded" TLS connections. -EG + +Fri Mar 17 10:45:31 MSK 2017 If read buffer is full, MHD need to receive remote data and application suspended connection, do not fail while connection is suspended and give application one more chance to read data from buffer once connection is diff --git a/src/microhttpd/connection.c b/src/microhttpd/connection.c @@ -593,6 +593,13 @@ MHD_connection_finish_forward_ (struct MHD_Connection *connection) { MHD_PANIC (_("Failed to remove FD from epoll set\n")); } + if (urh->in_eready_list) + { + EDLL_remove (daemon->eready_urh_head, + daemon->eready_urh_tail, + urh); + urh->in_eready_list = false; + } #endif /* EPOLL_SUPPORT */ if (MHD_INVALID_SOCKET != urh->mhd.socket) { diff --git a/src/microhttpd/daemon.c b/src/microhttpd/daemon.c @@ -3252,7 +3252,11 @@ MHD_get_timeout (struct MHD_Daemon *daemon, #ifdef EPOLL_SUPPORT if ( (0 != (daemon->options & MHD_USE_EPOLL)) && - (NULL != daemon->eready_head) ) + ((NULL != daemon->eready_head) +#if defined(UPGRADE_SUPPORT) && defined(HTTPS_SUPPORT) + || (NULL != daemon->eready_urh_head) +#endif /* UPGRADE_SUPPORT && HTTPS_SUPPORT */ + ) ) { /* Some connection(s) already have some data pending. */ *timeout = 0; @@ -3972,6 +3976,47 @@ MHD_poll (struct MHD_Daemon *daemon, #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) /** + * Checks whether @a urh has some data to process. + * + * @param urh upgrade handler to analyse + * @return 'true' if @a urh has some data to process, + * 'false' otherwise + */ +static bool +is_urh_ready(struct MHD_UpgradeResponseHandle * const urh) +{ + const struct MHD_Connection * const connection = urh->connection; + + if ( (0 == urh->in_buffer_size) && + (0 == urh->out_buffer_size) && + (0 == urh->in_buffer_used) && + (0 == urh->out_buffer_used) ) + return false; + + if (connection->daemon->shutdown) + return true; + + if ( ( (0 != (MHD_EPOLL_STATE_READ_READY & urh->app.celi)) || + (connection->tls_read_ready) ) && + (urh->in_buffer_used < urh->in_buffer_size) ) + return true; + + if ( (0 != (MHD_EPOLL_STATE_READ_READY & urh->mhd.celi)) && + (urh->out_buffer_used < urh->out_buffer_size) ) + return true; + + if ( (0 != (MHD_EPOLL_STATE_WRITE_READY & urh->app.celi)) && + (urh->out_buffer_used > 0) ) + return true; + + if ( (0 != (MHD_EPOLL_STATE_WRITE_READY & urh->mhd.celi)) && + (urh->in_buffer_used > 0) ) + return true; + + return false; +} + +/** * 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 @@ -3984,6 +4029,8 @@ run_epoll_for_upgrade (struct MHD_Daemon *daemon) { struct epoll_event events[MAX_EVENTS]; int num_events; + struct MHD_UpgradeResponseHandle * pos; + struct MHD_UpgradeResponseHandle * prev; num_events = MAX_EVENTS; while (MAX_EVENTS == num_events) @@ -4006,45 +4053,74 @@ run_epoll_for_upgrade (struct MHD_Daemon *daemon) #endif return MHD_NO; } - for (i=0;i<(unsigned int) num_events;i++) + for (i = 0; i < (unsigned int) num_events; i++) { struct UpgradeEpollHandle * const ueh = events[i].data.ptr; struct MHD_UpgradeResponseHandle * const urh = ueh->urh; + bool new_err_state = false; - /* Each MHD_UpgradeResponseHandle can be processed two times: - * one time for TLS data and one time for socketpair data. - * If forwarding was finished on first time, second time must - * be skipped as urh must not be used anymore. */ if (urh->clean_ready) continue; - /* Update our state based on what is ready according to epoll() */ + /* Update ueh 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; if (0 != (events[i].events & EPOLLHUP)) ueh->celi |= MHD_EPOLL_STATE_READ_READY | MHD_EPOLL_STATE_WRITE_READY; - if (0 != (events[i].events & (EPOLLERR | EPOLLPRI))) - ueh->celi |= MHD_EPOLL_STATE_ERROR; - process_urh (urh); - /* Finished forwarding? */ - if ( (0 == urh->in_buffer_size) && - (0 == urh->out_buffer_size) && - (0 == urh->in_buffer_used) && - (0 == urh->out_buffer_used) ) + if ( (0 == (ueh->celi & MHD_EPOLL_STATE_ERROR)) && + (0 != (events[i].events & (EPOLLERR | EPOLLPRI))) ) + { + /* Process new error state only one time + * and avoid continuously marking this connection + * as 'ready'. */ + ueh->celi |= MHD_EPOLL_STATE_ERROR; + new_err_state = true; + } + + if (! urh->in_eready_list) { - MHD_connection_finish_forward_ (urh->connection); - urh->clean_ready = true; - /* If 'urh->was_closed' set to true, connection will be - * moved immediately to cleanup list. Otherwise connection - * will stay in suspended list until 'urh' will be marked - * with 'was_closed' by application. */ - MHD_resume_connection(urh->connection); + if (new_err_state || + is_urh_ready(urh)) + { + EDLL_insert (daemon->eready_urh_head, + daemon->eready_urh_tail, + urh); + urh->in_eready_list = true; + } } } } + prev = daemon->eready_urh_tail; + while (NULL != (pos = prev)) + { + prev = pos->prevE; + process_urh (pos); + if (! is_urh_ready(pos)) + { + EDLL_remove (daemon->eready_urh_head, + daemon->eready_urh_tail, + pos); + pos->in_eready_list = false; + } + /* Finished forwarding? */ + if ( (0 == pos->in_buffer_size) && + (0 == pos->out_buffer_size) && + (0 == pos->in_buffer_used) && + (0 == pos->out_buffer_used) ) + { + MHD_connection_finish_forward_ (pos->connection); + pos->clean_ready = true; + /* If 'pos->was_closed' already was set to true, connection + * will be moved immediately to cleanup list. Otherwise + * connection will stay in suspended list until 'pos' will + * be marked with 'was_closed' by application. */ + MHD_resume_connection(pos->connection); + } + } + return MHD_YES; } #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ diff --git a/src/microhttpd/internal.h b/src/microhttpd/internal.h @@ -1026,6 +1026,23 @@ struct MHD_UpgradeResponseHandle */ struct MHD_UpgradeResponseHandle *prev; +#ifdef EPOLL_SUPPORT + /** + * Next pointer for the EDLL listing urhs that are epoll-ready. + */ + struct MHD_UpgradeResponseHandle *nextE; + + /** + * Previous pointer for the EDLL listing urhs that are epoll-ready. + */ + struct MHD_UpgradeResponseHandle *prevE; + + /** + * Specifies whether urh already in EDLL list of ready connections. + */ + bool in_eready_list; +#endif + /** * The buffer for receiving data from TLS to * be passed to the application. Contains @e in_buffer_size @@ -1217,7 +1234,19 @@ struct MHD_Daemon * Tail of EDLL of connections ready for processing (in epoll mode) */ struct MHD_Connection *eready_tail; -#endif + +#ifdef UPGRADE_SUPPORT + /** + * Head of EDLL of upgraded connections ready for processing (in epoll mode). + */ + struct MHD_UpgradeResponseHandle *eready_urh_head; + + /** + * Tail of EDLL of upgraded connections ready for processing (in epoll mode) + */ + struct MHD_UpgradeResponseHandle *eready_urh_tail; +#endif /* UPGRADE_SUPPORT */ +#endif /* EPOLL_SUPPORT */ /** * Head of the XDLL of ALL connections with a default ('normal') diff --git a/src/microhttpd/response.c b/src/microhttpd/response.c @@ -851,7 +851,7 @@ MHD_response_execute_upgrade_ (struct MHD_Response *response, EXTRA_CHECK (-1 != daemon->epoll_upgrade_fd); /* First, add network socket */ - event.events = EPOLLIN | EPOLLOUT | EPOLLPRI; + event.events = EPOLLIN | EPOLLOUT | EPOLLPRI | EPOLLET; event.data.ptr = &urh->app; if (0 != epoll_ctl (daemon->epoll_upgrade_fd, EPOLL_CTL_ADD, @@ -870,7 +870,7 @@ MHD_response_execute_upgrade_ (struct MHD_Response *response, } /* Second, add our end of the UNIX socketpair() */ - event.events = EPOLLIN | EPOLLOUT | EPOLLPRI; + event.events = EPOLLIN | EPOLLOUT | EPOLLPRI | EPOLLET; event.data.ptr = &urh->mhd; if (0 != epoll_ctl (daemon->epoll_upgrade_fd, EPOLL_CTL_ADD, @@ -894,6 +894,10 @@ MHD_response_execute_upgrade_ (struct MHD_Response *response, free (urh); return MHD_NO; } + EDLL_insert (daemon->eready_urh_head, + daemon->eready_urh_tail, + urh); + urh->in_eready_list = true; } #endif /* EPOLL_SUPPORT */ if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION) )