libmicrohttpd

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

commit 6578d480c57c6fef92b5a980d41eabb22192f2cf
parent 02171fdaeb0c1e02f778cd1e595319fd63941b8e
Author: Christian Grothoff <christian@grothoff.org>
Date:   Fri, 30 Aug 2013 11:54:56 +0000

towards fixing #3008

Diffstat:
MChangeLog | 7+++++++
Msrc/include/microhttpd.h | 12++++++++++--
Msrc/microhttpd/connection.c | 32+++++++++++++++++++-------------
Msrc/microhttpd/daemon.c | 36+++++++++++++++++++++++++++++-------
Msrc/testcurl/test_post.c | 4++++
5 files changed, 69 insertions(+), 22 deletions(-)

diff --git a/ChangeLog b/ChangeLog @@ -1,3 +1,10 @@ +Fri Aug 30 13:53:04 CEST 2013 + Started to implement #3008 (RFC 2616, section 8.1.4 + says HTTP server SHOULD terminate connection if the + client closes it for writing via TCP FIN, so we should + to continue to try to read and react differently + if recv() returns zero). -CG + Wed Aug 28 18:40:47 CEST 2013 Fix #3007 (build issue if messages are disabled). -CG diff --git a/src/include/microhttpd.h b/src/include/microhttpd.h @@ -120,7 +120,7 @@ extern "C" /** * Current version of the library. */ -#define MHD_VERSION 0x00092201 +#define MHD_VERSION 0x00092202 /** * MHD-internal return code for "YES". @@ -882,7 +882,15 @@ enum MHD_RequestTerminationCode * data. * @ingroup request */ - MHD_REQUEST_TERMINATED_READ_ERROR = 4 + MHD_REQUEST_TERMINATED_READ_ERROR = 4, + + /** + * The client terminated the connection by closing the socket + * for writing (TCP half-closed); MHD aborted sending the + * response according to RFC 2616, section 8.1.4. + * @ingroup request + */ + MHD_REQUEST_TERMINATED_CLIENT_ABORT = 5 }; diff --git a/src/microhttpd/connection.c b/src/microhttpd/connection.c @@ -1494,7 +1494,6 @@ do_read (struct MHD_Connection *connection) if (connection->read_buffer_size == connection->read_buffer_offset) return MHD_NO; - bytes_read = connection->recv_cls (connection, &connection->read_buffer [connection->read_buffer_offset], @@ -1520,11 +1519,11 @@ do_read (struct MHD_Connection *connection) } if (0 == bytes_read) { - /* other side closed connection */ + /* other side closed connection; RFC 2616, section 8.1.4 suggests + we should then shutdown ourselves as well. */ connection->read_closed = MHD_YES; - /* shutdown is not required here, as the other side already - knows; so flagging this internally should suffice */ - /* SHUTDOWN (connection->socket_fd, SHUT_RD); */ + MHD_connection_close (connection, + MHD_REQUEST_TERMINATED_CLIENT_ABORT); return MHD_YES; } connection->read_buffer_offset += bytes_read; @@ -2465,6 +2464,15 @@ MHD_connection_handle_idle (struct MHD_Connection *connection) } break; case MHD_EVENT_LOOP_INFO_WRITE: + if ( (connection->read_buffer_size > connection->read_buffer_offset) && + (0 != (connection->epoll_state & MHD_EPOLL_STATE_READ_READY)) && + (0 == (connection->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL)) ) + { + EDLL_insert (daemon->eready_head, + daemon->eready_tail, + connection); + connection->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL; + } if ( (0 != (connection->epoll_state & MHD_EPOLL_STATE_WRITE_READY)) && (0 == (connection->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL)) ) { @@ -2498,8 +2506,8 @@ MHD_connection_handle_idle (struct MHD_Connection *connection) #if EPOLL_SUPPORT /** - * Perform epoll processing, possibly moving the connection back into - * the epoll set if needed. + * Perform epoll() processing, possibly moving the connection back into + * the epoll() set if needed. * * @param connection connection to process * @return MHD_YES if we should continue to process the @@ -2514,7 +2522,8 @@ MHD_connection_epoll_update_ (struct MHD_Connection *connection) (0 == (connection->epoll_state & MHD_EPOLL_STATE_IN_EPOLL_SET)) && ( (0 == (connection->epoll_state & MHD_EPOLL_STATE_WRITE_READY)) || ( (0 == (connection->epoll_state & MHD_EPOLL_STATE_READ_READY)) && - (MHD_EVENT_LOOP_INFO_READ == connection->event_loop_info) && + ( (MHD_EVENT_LOOP_INFO_READ == connection->event_loop_info) || + (connection->read_buffer_size > connection->read_buffer_offset) ) && (MHD_NO == connection->read_closed) ) ) ) { /* add to epoll set */ @@ -2694,11 +2703,8 @@ MHD_queue_response (struct MHD_Connection *connection, (0 == strcasecmp (connection->method, MHD_HTTP_METHOD_PUT))) ) { - /* response was queued "early", - refuse to read body / footers or further - requests! */ - if (0 == (connection->daemon->options & MHD_USE_EPOLL_TURBO)) - (void) SHUTDOWN (connection->socket_fd, SHUT_RD); + /* response was queued "early", refuse to read body / footers or + further requests! */ connection->read_closed = MHD_YES; connection->state = MHD_CONNECTION_FOOTERS_RECEIVED; } diff --git a/src/microhttpd/daemon.c b/src/microhttpd/daemon.c @@ -615,9 +615,12 @@ MHD_get_fdset (struct MHD_Daemon *daemon, break; case MHD_EVENT_LOOP_INFO_WRITE: add_to_fd_set (pos->socket_fd, write_fd_set, max_fd); + if (pos->read_buffer_size > pos->read_buffer_offset) + add_to_fd_set (pos->socket_fd, read_fd_set, max_fd); break; case MHD_EVENT_LOOP_INFO_BLOCK: - /* not in any FD set */ + if (pos->read_buffer_size > pos->read_buffer_offset) + add_to_fd_set (pos->socket_fd, read_fd_set, max_fd); break; case MHD_EVENT_LOOP_INFO_CLEANUP: /* this should never happen */ @@ -693,8 +696,12 @@ MHD_handle_connection (void *data) break; case MHD_EVENT_LOOP_INFO_WRITE: add_to_fd_set (con->socket_fd, &ws, &max); + if (con->read_buffer_size > con->read_buffer_offset) + add_to_fd_set (con->socket_fd, &rs, &max); break; case MHD_EVENT_LOOP_INFO_BLOCK: + if (con->read_buffer_size > con->read_buffer_offset) + add_to_fd_set (con->socket_fd, &rs, &max); tv.tv_sec = 0; tv.tv_usec = 0; tvp = &tv; @@ -741,8 +748,12 @@ MHD_handle_connection (void *data) break; case MHD_EVENT_LOOP_INFO_WRITE: p[0].events |= POLLOUT; + if (con->read_buffer_size > con->read_buffer_offset) + p[0].events |= POLLIN; break; case MHD_EVENT_LOOP_INFO_BLOCK: + if (con->read_buffer_size > con->read_buffer_offset) + p[0].events |= POLLIN; tv.tv_sec = 0; tv.tv_usec = 0; tvp = &tv; @@ -1774,11 +1785,16 @@ MHD_run_from_select (struct MHD_Daemon *daemon, pos->read_handler (pos); break; case MHD_EVENT_LOOP_INFO_WRITE: + if ( (FD_ISSET (ds, read_fd_set)) && + (pos->read_buffer_size > pos->read_buffer_offset) ) + pos->read_handler (pos); if (FD_ISSET (ds, write_fd_set)) pos->write_handler (pos); break; case MHD_EVENT_LOOP_INFO_BLOCK: - /* only idle handler */ + if ( (FD_ISSET (ds, read_fd_set)) && + (pos->read_buffer_size > pos->read_buffer_offset) ) + pos->read_handler (pos); break; case MHD_EVENT_LOOP_INFO_CLEANUP: /* should never happen */ @@ -1903,9 +1919,7 @@ MHD_poll_all (struct MHD_Daemon *daemon, /* count number of connections and thus determine poll set size */ num_connections = 0; for (pos = daemon->connections_head; NULL != pos; pos = pos->next) - if ( (MHD_EVENT_LOOP_INFO_READ == pos->event_loop_info) || - (MHD_EVENT_LOOP_INFO_WRITE == pos->event_loop_info) ) - num_connections++; + num_connections++; { struct pollfd p[2 + num_connections]; MHD_UNSIGNED_LONG_LONG ltimeout; @@ -1953,9 +1967,12 @@ MHD_poll_all (struct MHD_Daemon *daemon, break; case MHD_EVENT_LOOP_INFO_WRITE: p[poll_server+i].events |= POLLOUT; + if (pos->read_buffer_size > pos->read_buffer_offset) + p[poll_server+i].events |= POLLIN; break; case MHD_EVENT_LOOP_INFO_BLOCK: - /* not in poll */ + if (pos->read_buffer_size > pos->read_buffer_offset) + p[poll_server+i].events |= POLLIN; break; case MHD_EVENT_LOOP_INFO_CLEANUP: /* should never happen */ @@ -2005,12 +2022,16 @@ MHD_poll_all (struct MHD_Daemon *daemon, if (p[poll_server+i].fd != pos->socket_fd) break; /* fd mismatch, something else happened, retry later ... */ /* normal handling */ + if (0 != (p[poll_server+i].revents & POLLIN)) + pos->read_handler (pos); if (0 != (p[poll_server+i].revents & POLLOUT)) pos->write_handler (pos); pos->idle_handler (pos); i++; break; case MHD_EVENT_LOOP_INFO_BLOCK: + if (0 != (p[poll_server+i].revents & POLLIN)) + pos->read_handler (pos); pos->idle_handler (pos); break; case MHD_EVENT_LOOP_INFO_CLEANUP: @@ -2234,7 +2255,8 @@ MHD_epoll (struct MHD_Daemon *daemon, if (0 != (events[i].events & EPOLLIN)) { pos->epoll_state |= MHD_EPOLL_STATE_READ_READY; - if ( (MHD_EVENT_LOOP_INFO_READ == pos->event_loop_info) && + 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, diff --git a/src/testcurl/test_post.c b/src/testcurl/test_post.c @@ -481,6 +481,7 @@ struct CRBC size_t pos; }; + static size_t readBuffer(void *p, size_t size, size_t nmemb, void *opaque) { @@ -497,6 +498,7 @@ readBuffer(void *p, size_t size, size_t nmemb, void *opaque) return required/size; } + static size_t slowReadBuffer(void *p, size_t size, size_t nmemb, void *opaque) { @@ -504,6 +506,7 @@ slowReadBuffer(void *p, size_t size, size_t nmemb, void *opaque) return readBuffer(p, size, nmemb, opaque); } + #define FLAG_EXPECT_CONTINUE 1 #define FLAG_CHUNKED 2 #define FLAG_FORM_DATA 4 @@ -604,6 +607,7 @@ testMultithreadedPostCancelPart(int flags) return result; } + static int testMultithreadedPostCancel() {