diff --git a/auto/unix b/auto/unix index 43d3b25..ff9697a 100644 --- a/auto/unix +++ b/auto/unix @@ -943,6 +943,18 @@ ngx_feature_test="int i = FIONBIO; printf(\"%d\", i)" . auto/feature +ngx_feature="ioctl(FIONREAD)" +ngx_feature_name="NGX_HAVE_FIONREAD" +ngx_feature_run=no +ngx_feature_incs="#include + #include + $NGX_INCLUDE_SYS_FILIO_H" +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="int i = FIONREAD; printf(\"%d\", i)" +. auto/feature + + ngx_feature="struct tm.tm_gmtoff" ngx_feature_name="NGX_HAVE_GMTOFF" ngx_feature_run=no diff --git a/src/event/modules/ngx_devpoll_module.c b/src/event/modules/ngx_devpoll_module.c index ee9f854..590eb28 100644 --- a/src/event/modules/ngx_devpoll_module.c +++ b/src/event/modules/ngx_devpoll_module.c @@ -495,6 +495,7 @@ ngx_devpoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, if ((revents & POLLIN) && rev->active) { rev->ready = 1; + rev->available = -1; if (flags & NGX_POST_EVENTS) { queue = rev->accept ? &ngx_posted_accept_events diff --git a/src/event/modules/ngx_epoll_module.c b/src/event/modules/ngx_epoll_module.c index 76aee08..98e3ce7 100644 --- a/src/event/modules/ngx_epoll_module.c +++ b/src/event/modules/ngx_epoll_module.c @@ -886,11 +886,10 @@ ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags) if (revents & EPOLLRDHUP) { rev->pending_eof = 1; } - - rev->available = 1; #endif rev->ready = 1; + rev->available = -1; if (flags & NGX_POST_EVENTS) { queue = rev->accept ? &ngx_posted_accept_events diff --git a/src/event/modules/ngx_eventport_module.c b/src/event/modules/ngx_eventport_module.c index 11ad093..f67c704 100644 --- a/src/event/modules/ngx_eventport_module.c +++ b/src/event/modules/ngx_eventport_module.c @@ -559,6 +559,7 @@ ngx_eventport_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, if (revents & POLLIN) { rev->ready = 1; + rev->available = -1; if (flags & NGX_POST_EVENTS) { queue = rev->accept ? &ngx_posted_accept_events diff --git a/src/event/modules/ngx_poll_module.c b/src/event/modules/ngx_poll_module.c index b46ab53..c16f024 100644 --- a/src/event/modules/ngx_poll_module.c +++ b/src/event/modules/ngx_poll_module.c @@ -370,6 +370,7 @@ ngx_poll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags) ev = c->read; ev->ready = 1; + ev->available = -1; queue = ev->accept ? &ngx_posted_accept_events : &ngx_posted_events; diff --git a/src/event/modules/ngx_select_module.c b/src/event/modules/ngx_select_module.c index 0644621..b9fceb3 100644 --- a/src/event/modules/ngx_select_module.c +++ b/src/event/modules/ngx_select_module.c @@ -330,6 +330,7 @@ ngx_select_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, if (found) { ev->ready = 1; + ev->available = -1; queue = ev->accept ? &ngx_posted_accept_events : &ngx_posted_events; diff --git a/src/event/modules/ngx_win32_poll_module.c b/src/event/modules/ngx_win32_poll_module.c index 9fe867f..2fbc1b3 100644 --- a/src/event/modules/ngx_win32_poll_module.c +++ b/src/event/modules/ngx_win32_poll_module.c @@ -380,6 +380,7 @@ ngx_poll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags) ev = c->read; ev->ready = 1; + ev->available = -1; queue = ev->accept ? &ngx_posted_accept_events : &ngx_posted_events; diff --git a/src/event/modules/ngx_win32_select_module.c b/src/event/modules/ngx_win32_select_module.c index 8093a60..962514a 100644 --- a/src/event/modules/ngx_win32_select_module.c +++ b/src/event/modules/ngx_win32_select_module.c @@ -330,6 +330,7 @@ ngx_select_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, if (found) { ev->ready = 1; + ev->available = -1; queue = ev->accept ? &ngx_posted_accept_events : &ngx_posted_events; diff --git a/src/event/ngx_event.c b/src/event/ngx_event.c index ffb4e2f..8c49c06 100644 --- a/src/event/ngx_event.c +++ b/src/event/ngx_event.c @@ -237,6 +237,12 @@ ngx_process_events_and_timers(ngx_cycle_t *cycle) } } + if (!ngx_queue_empty(&ngx_posted_next_events)) { + ngx_queue_add(&ngx_posted_events, &ngx_posted_next_events); + ngx_queue_init(&ngx_posted_next_events); + timer = 0; + } + delta = ngx_current_msec; (void) ngx_process_events(cycle, timer, flags); @@ -639,6 +645,7 @@ ngx_event_process_init(ngx_cycle_t *cycle) #endif ngx_queue_init(&ngx_posted_accept_events); + ngx_queue_init(&ngx_posted_next_events); ngx_queue_init(&ngx_posted_events); if (ngx_event_timer_init(cycle->log) == NGX_ERROR) { diff --git a/src/event/ngx_event.h b/src/event/ngx_event.h index 5c1fd0d..115878a 100644 --- a/src/event/ngx_event.h +++ b/src/event/ngx_event.h @@ -91,21 +91,14 @@ struct ngx_event_s { * write: available space in buffer when event is ready * or lowat when event is set with NGX_LOWAT_EVENT flag * - * epoll with EPOLLRDHUP: - * accept: 1 if accept many, 0 otherwise - * read: 1 if there can be data to read, 0 otherwise - * * iocp: TODO * * otherwise: * accept: 1 if accept many, 0 otherwise + * read: bytes to read when event is ready, -1 if not known */ -#if (NGX_HAVE_KQUEUE) || (NGX_HAVE_IOCP) int available; -#else - unsigned available:1; -#endif ngx_event_handler_pt handler; diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c index ad3ba70..3bf5bf1 100644 --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -43,6 +43,7 @@ static ssize_t ngx_ssl_recv_early(ngx_connection_t *c, u_char *buf, #endif static ngx_int_t ngx_ssl_handle_recv(ngx_connection_t *c, int n); static void ngx_ssl_write_handler(ngx_event_t *wev); +static void ngx_ssl_next_read_handler(ngx_event_t *rev); #ifdef SSL_READ_EARLY_DATA_SUCCESS static ssize_t ngx_ssl_write_early(ngx_connection_t *c, u_char *data, size_t size); @@ -1931,6 +1932,10 @@ ngx_ssl_recv_chain(ngx_connection_t *c, ngx_chain_t *cl, off_t limit) last += n; bytes += n; + if (!c->read->ready) { + return bytes; + } + if (last == b->end) { cl = cl->next; @@ -2008,6 +2013,48 @@ ngx_ssl_recv(ngx_connection_t *c, u_char *buf, size_t size) if (size == 0) { c->read->ready = 1; + + if (c->read->available >= 0) { + c->read->available -= bytes; + + /* + * there can be data buffered at SSL layer, + * so we post an event to continue reading on the next + * iteration of the event loop + */ + + if (c->read->available < 0) { + c->read->available = 0; + c->read->ready = 0; + + if (c->ssl->next_read_handler == NULL) { + c->ssl->next_read_handler = c->read->handler; + c->read->handler = ngx_ssl_next_read_handler; + } + + ngx_post_event(c->read, &ngx_posted_next_events); + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "SSL_read: avail:%d", c->read->available); + + } else { + +#if (NGX_HAVE_FIONREAD) + + if (ngx_socket_nread(c->fd, &c->read->available) == -1) { + c->read->error = 1; + ngx_connection_error(c, ngx_socket_errno, + ngx_socket_nread_n " failed"); + return NGX_ERROR; + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "SSL_read: avail:%d", c->read->available); + +#endif + } + return bytes; } @@ -2290,6 +2337,31 @@ ngx_ssl_write_handler(ngx_event_t *wev) } +static void +ngx_ssl_next_read_handler(ngx_event_t *rev) +{ + ngx_connection_t *c; + + c = rev->data; + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL next read handler"); + + rev->handler = c->ssl->next_read_handler; + c->ssl->next_read_handler = NULL; + + if (!rev->ready) { + rev->ready = 1; + rev->available = -1; + } + + if (rev->posted) { + ngx_delete_posted_event(rev); + } + + rev->handler(rev); +} + + /* * OpenSSL has no SSL_writev() so we copy several bufs into our 16K buffer * before the SSL_write() call to decrease a SSL overhead. diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h index 0ef823b..1b438a3 100644 --- a/src/event/ngx_event_openssl.h +++ b/src/event/ngx_event_openssl.h @@ -95,6 +95,7 @@ struct ngx_ssl_connection_s { ngx_event_handler_pt saved_read_handler; ngx_event_handler_pt saved_write_handler; + ngx_event_handler_pt next_read_handler; u_char early_buf; diff --git a/src/event/ngx_event_pipe.c b/src/event/ngx_event_pipe.c index da7c4ee..531b13a 100644 --- a/src/event/ngx_event_pipe.c +++ b/src/event/ngx_event_pipe.c @@ -172,7 +172,11 @@ ngx_event_pipe_read_upstream(ngx_event_pipe_t *p) */ if (p->upstream->read->available == 0 - && p->upstream->read->pending_eof) + && p->upstream->read->pending_eof +#if (NGX_SSL) + && !p->upstream->ssl +#endif + ) { p->upstream->read->ready = 0; p->upstream->read->eof = 1; diff --git a/src/event/ngx_event_posted.c b/src/event/ngx_event_posted.c index d851f3d..fd0b411 100644 --- a/src/event/ngx_event_posted.c +++ b/src/event/ngx_event_posted.c @@ -11,6 +11,7 @@ ngx_queue_t ngx_posted_accept_events; +ngx_queue_t ngx_posted_next_events; ngx_queue_t ngx_posted_events; diff --git a/src/event/ngx_event_posted.h b/src/event/ngx_event_posted.h index 145d30f..bac5b35 100644 --- a/src/event/ngx_event_posted.h +++ b/src/event/ngx_event_posted.h @@ -42,6 +42,7 @@ void ngx_event_process_posted(ngx_cycle_t *cycle, ngx_queue_t *posted); extern ngx_queue_t ngx_posted_accept_events; +extern ngx_queue_t ngx_posted_next_events; extern ngx_queue_t ngx_posted_events; diff --git a/src/os/unix/ngx_readv_chain.c b/src/os/unix/ngx_readv_chain.c index 454cfdc..a3577ce 100644 --- a/src/os/unix/ngx_readv_chain.c +++ b/src/os/unix/ngx_readv_chain.c @@ -60,7 +60,7 @@ ngx_readv_chain(ngx_connection_t *c, ngx_chain_t *chain, off_t limit) "readv: eof:%d, avail:%d", rev->pending_eof, rev->available); - if (!rev->available && !rev->pending_eof) { + if (rev->available == 0 && !rev->pending_eof) { return NGX_AGAIN; } } @@ -165,6 +165,40 @@ ngx_readv_chain(ngx_connection_t *c, ngx_chain_t *chain, off_t limit) #endif +#if (NGX_HAVE_FIONREAD) + + if (rev->available >= 0) { + rev->available -= n; + + /* + * negative rev->available means some additional bytes + * were received between kernel notification and readv(), + * and therefore ev->ready can be safely reset even for + * edge-triggered event methods + */ + + if (rev->available < 0) { + rev->available = 0; + rev->ready = 0; + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "readv: avail:%d", rev->available); + + } else if (n == size) { + + if (ngx_socket_nread(c->fd, &rev->available) == -1) { + n = ngx_connection_error(c, ngx_socket_errno, + ngx_socket_nread_n " failed"); + break; + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "readv: avail:%d", rev->available); + } + +#endif + #if (NGX_HAVE_EPOLLRDHUP) if ((ngx_event_flags & NGX_USE_EPOLL_EVENT) diff --git a/src/os/unix/ngx_recv.c b/src/os/unix/ngx_recv.c index c85fd45..ddfae4d 100644 --- a/src/os/unix/ngx_recv.c +++ b/src/os/unix/ngx_recv.c @@ -57,7 +57,7 @@ ngx_unix_recv(ngx_connection_t *c, u_char *buf, size_t size) "recv: eof:%d, avail:%d", rev->pending_eof, rev->available); - if (!rev->available && !rev->pending_eof) { + if (rev->available == 0 && !rev->pending_eof) { rev->ready = 0; return NGX_AGAIN; } @@ -116,6 +116,40 @@ ngx_unix_recv(ngx_connection_t *c, u_char *buf, size_t size) #endif +#if (NGX_HAVE_FIONREAD) + + if (rev->available >= 0) { + rev->available -= n; + + /* + * negative rev->available means some additional bytes + * were received between kernel notification and recv(), + * and therefore ev->ready can be safely reset even for + * edge-triggered event methods + */ + + if (rev->available < 0) { + rev->available = 0; + rev->ready = 0; + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "recv: avail:%d", rev->available); + + } else if ((size_t) n == size) { + + if (ngx_socket_nread(c->fd, &rev->available) == -1) { + n = ngx_connection_error(c, ngx_socket_errno, + ngx_socket_nread_n " failed"); + break; + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "recv: avail:%d", rev->available); + } + +#endif + #if (NGX_HAVE_EPOLLRDHUP) if ((ngx_event_flags & NGX_USE_EPOLL_EVENT) diff --git a/src/os/unix/ngx_socket.h b/src/os/unix/ngx_socket.h index fcc5153..ec66a6f 100644 --- a/src/os/unix/ngx_socket.h +++ b/src/os/unix/ngx_socket.h @@ -38,6 +38,13 @@ int ngx_blocking(ngx_socket_t s); #endif +#if (NGX_HAVE_FIONREAD) + +#define ngx_socket_nread(s, n) ioctl(s, FIONREAD, n) +#define ngx_socket_nread_n "ioctl(FIONREAD)" + +#endif + int ngx_tcp_nopush(ngx_socket_t s); int ngx_tcp_push(ngx_socket_t s); diff --git a/src/os/win32/ngx_socket.c b/src/os/win32/ngx_socket.c index 05a39f4..b1b4afb 100644 --- a/src/os/win32/ngx_socket.c +++ b/src/os/win32/ngx_socket.c @@ -27,6 +27,21 @@ ngx_blocking(ngx_socket_t s) } +int +ngx_socket_nread(ngx_socket_t s, int *n) +{ + unsigned long nread; + + if (ioctlsocket(s, FIONREAD, &nread) == -1) { + return -1; + } + + *n = nread; + + return 0; +} + + int ngx_tcp_push(ngx_socket_t s) { diff --git a/src/os/win32/ngx_socket.h b/src/os/win32/ngx_socket.h index f8a453d..ab56bc8 100644 --- a/src/os/win32/ngx_socket.h +++ b/src/os/win32/ngx_socket.h @@ -31,6 +31,9 @@ int ngx_blocking(ngx_socket_t s); #define ngx_nonblocking_n "ioctlsocket(FIONBIO)" #define ngx_blocking_n "ioctlsocket(!FIONBIO)" +int ngx_socket_nread(ngx_socket_t s, int *n); +#define ngx_socket_nread_n "ioctlsocket(FIONREAD)" + #define ngx_shutdown_socket shutdown #define ngx_shutdown_socket_n "shutdown()" diff --git a/src/os/win32/ngx_win32_config.h b/src/os/win32/ngx_win32_config.h index 4824d05..9615687 100644 --- a/src/os/win32/ngx_win32_config.h +++ b/src/os/win32/ngx_win32_config.h @@ -273,6 +273,10 @@ typedef int sig_atomic_t; #define NGX_HAVE_SO_SNDLOWAT 0 #endif +#ifndef NGX_HAVE_FIONREAD +#define NGX_HAVE_FIONREAD 1 +#endif + #define NGX_HAVE_GETADDRINFO 1 #define ngx_random rand diff --git a/src/os/win32/ngx_wsarecv.c b/src/os/win32/ngx_wsarecv.c index 1925f0b..ac88310 100644 --- a/src/os/win32/ngx_wsarecv.c +++ b/src/os/win32/ngx_wsarecv.c @@ -51,6 +51,45 @@ ngx_wsarecv(ngx_connection_t *c, u_char *buf, size_t size) return n; } +#if (NGX_HAVE_FIONREAD) + + if (rev->available >= 0 && bytes > 0) { + rev->available -= bytes; + + /* + * negative rev->available means some additional bytes + * were received between kernel notification and WSARecv(), + * and therefore ev->ready can be safely reset even for + * edge-triggered event methods + */ + + if (rev->available < 0) { + rev->available = 0; + rev->ready = 0; + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "WSARecv: avail:%d", rev->available); + + } else if (bytes == size) { + + if (ngx_socket_nread(c->fd, &rev->available) == -1) { + n = ngx_connection_error(c, ngx_socket_errno, + ngx_socket_nread_n " failed"); + + if (n == NGX_ERROR) { + rev->error = 1; + } + + return n; + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "WSARecv: avail:%d", rev->available); + } + +#endif + if (bytes < size) { rev->ready = 0; } diff --git a/src/os/win32/ngx_wsarecv_chain.c b/src/os/win32/ngx_wsarecv_chain.c index 2598e09..87f0239 100644 --- a/src/os/win32/ngx_wsarecv_chain.c +++ b/src/os/win32/ngx_wsarecv_chain.c @@ -94,6 +94,41 @@ ngx_wsarecv_chain(ngx_connection_t *c, ngx_chain_t *chain, off_t limit) return NGX_ERROR; } +#if (NGX_HAVE_FIONREAD) + + if (rev->available >= 0 && bytes > 0) { + rev->available -= bytes; + + /* + * negative rev->available means some additional bytes + * were received between kernel notification and WSARecv(), + * and therefore ev->ready can be safely reset even for + * edge-triggered event methods + */ + + if (rev->available < 0) { + rev->available = 0; + rev->ready = 0; + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "WSARecv: avail:%d", rev->available); + + } else if (bytes == size) { + + if (ngx_socket_nread(c->fd, &rev->available) == -1) { + rev->error = 1; + ngx_connection_error(c, ngx_socket_errno, + ngx_socket_nread_n " failed"); + return NGX_ERROR; + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "WSARecv: avail:%d", rev->available); + } + +#endif + if (bytes < size) { rev->ready = 0; }