Merge branch 'nginx' (nginx-1.17.5).

Change-Id: I275ecd6c5824a8397b6132da04c4154978552565
Signed-off-by: Piotr Sikora <piotrsikora@google.com>
diff --git a/.hgtags b/.hgtags
index 19ec901..42f1877 100644
--- a/.hgtags
+++ b/.hgtags
@@ -443,3 +443,4 @@
 2fc9f853a6b7cd29dc84e0af2ed3cf78e0da6ca8 release-1.17.2
 ed4303aa1b31a9aad5440640c0840d9d0af45fed release-1.17.3
 ce2ced3856909f36f8130c99eaa4dbdbae636ddc release-1.17.4
+9af0dddbddb2c368bfedd2801bc100ffad01e19b release-1.17.5
diff --git a/BUILD b/BUILD
index bfe49ee..c2e5c91 100644
--- a/BUILD
+++ b/BUILD
@@ -1520,5 +1520,5 @@
     preinst = "@nginx_pkgoss//:debian_preinst",
     prerm = "@nginx_pkgoss//:debian_prerm",
     section = "httpd",
-    version = "1.17.4",
+    version = "1.17.5",
 )
diff --git a/auto/unix b/auto/unix
index cdc5868..c3276f2 100644
--- a/auto/unix
+++ b/auto/unix
@@ -943,6 +943,18 @@
 . $NGX_AUTO/feature
 
 
+ngx_feature="ioctl(FIONREAD)"
+ngx_feature_name="NGX_HAVE_FIONREAD"
+ngx_feature_run=no
+ngx_feature_incs="#include <sys/ioctl.h>
+                  #include <stdio.h>
+                  $NGX_INCLUDE_SYS_FILIO_H"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="int i = FIONREAD; printf(\"%d\", i)"
+. $NGX_AUTO/feature
+
+
 ngx_feature="struct tm.tm_gmtoff"
 ngx_feature_name="NGX_HAVE_GMTOFF"
 ngx_feature_run=no
diff --git a/build.bzl b/build.bzl
index 662cfa4..49060ce 100644
--- a/build.bzl
+++ b/build.bzl
@@ -673,9 +673,9 @@
         name = "nginx_pkgoss",
         build_file_content = _PKGOSS_BUILD_FILE.format(nginx = nginx) +
                              _PKGOSS_BUILD_FILE_TAIL,
-        commit = "b111e7858767a7f112093e752eba1a27a69bf778",  # nginx-1.17.4
+        commit = "8358ebc96da490c997960f08b03ffff6d33ad2a5",  # nginx-1.17.5
         remote = "https://nginx.googlesource.com/nginx-pkgoss",
-        shallow_since = "1569336583 +0300",
+        shallow_since = "1571744169 +0300",
     )
 
 def nginx_repositories_zlib(bind):
diff --git a/docs/xml/nginx/changes.xml b/docs/xml/nginx/changes.xml
index fd7e308..29738da 100644
--- a/docs/xml/nginx/changes.xml
+++ b/docs/xml/nginx/changes.xml
@@ -5,6 +5,69 @@
 <change_log title="nginx">
 
 
+<changes ver="1.17.5" date="2019-10-22">
+
+<change type="feature">
+<para lang="ru">
+теперь nginx использует вызов ioctl(FIONREAD), если он доступен,
+чтобы избежать чтения из быстрого соединения в течение долгого времени.
+</para>
+<para lang="en">
+now nginx uses ioctl(FIONREAD), if available,
+to avoid reading from a fast connection for a long time.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+неполные закодированные символы в конце URI запроса игнорировались.
+</para>
+<para lang="en">
+incomplete escaped characters at the end of the request URI were ignored.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+"/." и "/.." в конце URI запроса не нормализовывались.
+</para>
+<para lang="en">
+"/." and "/.." at the end of the request URI were not normalized.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+в директиве merge_slashes.
+</para>
+<para lang="en">
+in the "merge_slashes" directive.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+в директиве ignore_invalid_headers.<br/>
+Спасибо Alan Kemp.
+</para>
+<para lang="en">
+in the "ignore_invalid_headers" directive.<br/>
+Thanks to Alan Kemp.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+nginx не собирался с MinGW-w64 gcc 8.1 и новее.
+</para>
+<para lang="en">
+nginx could not be built with MinGW-w64 gcc 8.1 or newer.
+</para>
+</change>
+
+</changes>
+
+
 <changes ver="1.17.4" date="2019-09-24">
 
 <change type="change">
diff --git a/src/core/nginx.h b/src/core/nginx.h
index 053201d..6910a87 100644
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -13,8 +13,8 @@
 #define NGINX_NAME         "nginx"
 #endif
 
-#define nginx_version      1017004
-#define NGINX_VERSION      "1.17.4"
+#define nginx_version      1017005
+#define NGINX_VERSION      "1.17.5"
 #define NGINX_VER          NGINX_NAME "/" NGINX_VERSION
 
 #ifdef NGX_BUILD
diff --git a/src/core/ngx_rbtree.c b/src/core/ngx_rbtree.c
index 969d549..377b82f 100644
--- a/src/core/ngx_rbtree.c
+++ b/src/core/ngx_rbtree.c
@@ -174,12 +174,7 @@
 
     } else {
         subst = ngx_rbtree_min(node->right, sentinel);
-
-        if (subst->left != sentinel) {
-            temp = subst->left;
-        } else {
-            temp = subst->right;
-        }
+        temp = subst->right;
     }
 
     if (subst == *root) {
diff --git a/src/event/modules/ngx_devpoll_module.c b/src/event/modules/ngx_devpoll_module.c
index b7bf98d..d955754 100644
--- a/src/event/modules/ngx_devpoll_module.c
+++ b/src/event/modules/ngx_devpoll_module.c
@@ -497,6 +497,7 @@
 
         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 09e5e48..fa79d07 100644
--- a/src/event/modules/ngx_epoll_module.c
+++ b/src/event/modules/ngx_epoll_module.c
@@ -894,11 +894,10 @@
             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 061c996..ffc98e1 100644
--- a/src/event/modules/ngx_eventport_module.c
+++ b/src/event/modules/ngx_eventport_module.c
@@ -571,6 +571,7 @@
 
             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 a12c112..9259030 100644
--- a/src/event/modules/ngx_poll_module.c
+++ b/src/event/modules/ngx_poll_module.c
@@ -372,6 +372,7 @@
 
             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 59810f6..9d84d79 100644
--- a/src/event/modules/ngx_select_module.c
+++ b/src/event/modules/ngx_select_module.c
@@ -332,6 +332,7 @@
 
         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 @@
 
             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 c464805..c6e1bcf 100644
--- a/src/event/modules/ngx_win32_select_module.c
+++ b/src/event/modules/ngx_win32_select_module.c
@@ -332,6 +332,7 @@
 
         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 32c637d..f173945 100644
--- a/src/event/ngx_event.c
+++ b/src/event/ngx_event.c
@@ -237,6 +237,12 @@
         }
     }
 
+    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 @@
 #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 f6eaf8c..3b8e50c 100644
--- a/src/event/ngx_event.h
+++ b/src/event/ngx_event.h
@@ -91,21 +91,14 @@
      *   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 b8d7b08..7d16b1f 100644
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -43,6 +43,7 @@
 #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);
@@ -1945,6 +1946,10 @@
             last += n;
             bytes += n;
 
+            if (!c->read->ready) {
+                return bytes;
+            }
+
             if (last == b->end) {
                 cl = cl->next;
 
@@ -2022,6 +2027,48 @@
 
             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;
             }
 
@@ -2304,6 +2351,31 @@
 }
 
 
+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 f34ccf9..7aa471b 100644
--- a/src/event/ngx_event_openssl.h
+++ b/src/event/ngx_event_openssl.h
@@ -86,6 +86,7 @@
 
     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 @@
              */
 
             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 @@
 
 
 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/http/ngx_http_parse.c b/src/http/ngx_http_parse.c
index 8e1b118..cfc42f9 100644
--- a/src/http/ngx_http_parse.c
+++ b/src/http/ngx_http_parse.c
@@ -912,6 +912,8 @@
                         i = 1;
 
                     } else {
+                        hash = 0;
+                        i = 0;
                         r->invalid_header = 1;
                     }
 
@@ -922,6 +924,8 @@
                     return NGX_HTTP_PARSE_INVALID_HEADER;
                 }
 
+                hash = 0;
+                i = 0;
                 r->invalid_header = 1;
 
                 break;
@@ -1437,9 +1441,11 @@
                 state = sw_quoted;
                 break;
             case '?':
+                u--;
                 r->args_start = p;
                 goto args;
             case '#':
+                u--;
                 goto done;
             case '+':
                 r->plus_in_uri = 1;
@@ -1467,8 +1473,9 @@
             case '\\':
 #endif
             case '/':
-                state = sw_slash;
-                u -= 5;
+            case '?':
+            case '#':
+                u -= 4;
                 for ( ;; ) {
                     if (u < r->uri.data) {
                         return NGX_HTTP_PARSE_INVALID_REQUEST;
@@ -1479,16 +1486,19 @@
                     }
                     u--;
                 }
+                if (ch == '?') {
+                    r->args_start = p;
+                    goto args;
+                }
+                if (ch == '#') {
+                    goto done;
+                }
+                state = sw_slash;
                 break;
             case '%':
                 quoted_state = state;
                 state = sw_quoted;
                 break;
-            case '?':
-                r->args_start = p;
-                goto args;
-            case '#':
-                goto done;
             case '+':
                 r->plus_in_uri = 1;
                 /* fall through */
@@ -1561,6 +1571,30 @@
         }
     }
 
+    if (state == sw_quoted || state == sw_quoted_second) {
+        return NGX_HTTP_PARSE_INVALID_REQUEST;
+    }
+
+    if (state == sw_dot) {
+        u--;
+
+    } else if (state == sw_dot_dot) {
+        u -= 4;
+
+        for ( ;; ) {
+            if (u < r->uri.data) {
+                return NGX_HTTP_PARSE_INVALID_REQUEST;
+            }
+
+            if (*u == '/') {
+                u++;
+                break;
+            }
+
+            u--;
+        }
+    }
+
 done:
 
     r->uri.len = u - r->uri.data;
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 @@
                        "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 @@
 
 #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 @@
                        "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 @@
 
 #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 @@
 
 #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_errno.c b/src/os/win32/ngx_errno.c
index b732bf4..966d3de 100644
--- a/src/os/win32/ngx_errno.c
+++ b/src/os/win32/ngx_errno.c
@@ -22,7 +22,7 @@
     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
                         NULL, err, lang, (char *) errstr, size, NULL);
 
-    if (len == 0 && lang && GetLastError() == ERROR_RESOURCE_LANG_NOT_FOUND) {
+    if (len == 0 && lang) {
 
         /*
          * Try to use English messages first and fallback to a language,
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
@@ -28,6 +28,21 @@
 
 
 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)
 {
     return 0;
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 @@
 #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 @@
 #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_win32_init.c b/src/os/win32/ngx_win32_init.c
index 70bee8e..3249fb2 100644
--- a/src/os/win32/ngx_win32_init.c
+++ b/src/os/win32/ngx_win32_init.c
@@ -240,7 +240,7 @@
         goto nopoll;
     }
 
-    WSAPoll = (ngx_wsapoll_pt) GetProcAddress(hmod, "WSAPoll");
+    WSAPoll = (ngx_wsapoll_pt) (void *) GetProcAddress(hmod, "WSAPoll");
     if (WSAPoll == NULL) {
         ngx_log_error(NGX_LOG_NOTICE, log, ngx_errno,
                       "GetProcAddress(\"WSAPoll\") failed");
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 @@
         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 @@
         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;
     }