changeset 5923:2c10db908b8c

SPDY: push pending data while closing a stream as with keepalive. This helps to avoid delays in sending the last chunk of data because of bad interaction between Nagle's algorithm on nginx side and delayed ACK on the client side. Delays could also be caused by TCP_CORK/TCP_NOPUSH if SPDY was working without SSL and sendfile() was used.
author Valentin Bartenev <vbart@nginx.com>
date Fri, 21 Nov 2014 22:51:49 +0300
parents 68f64bc17fa4
children 58956c644ad0
files src/http/ngx_http_spdy.c
diffstat 1 files changed, 51 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/src/http/ngx_http_spdy.c
+++ b/src/http/ngx_http_spdy.c
@@ -3317,8 +3317,10 @@ ngx_http_spdy_close_stream_handler(ngx_e
 void
 ngx_http_spdy_close_stream(ngx_http_spdy_stream_t *stream, ngx_int_t rc)
 {
+    int                           tcp_nodelay;
     ngx_event_t                  *ev;
-    ngx_connection_t             *fc;
+    ngx_connection_t             *c, *fc;
+    ngx_http_core_loc_conf_t     *clcf;
     ngx_http_spdy_stream_t      **index, *s;
     ngx_http_spdy_srv_conf_t     *sscf;
     ngx_http_spdy_connection_t   *sc;
@@ -3344,6 +3346,54 @@ ngx_http_spdy_close_stream(ngx_http_spdy
         {
             sc->connection->error = 1;
         }
+
+    } else {
+        c = sc->connection;
+
+        if (c->tcp_nopush == NGX_TCP_NOPUSH_SET) {
+            if (ngx_tcp_push(c->fd) == -1) {
+                ngx_connection_error(c, ngx_socket_errno,
+                                     ngx_tcp_push_n " failed");
+                c->error = 1;
+                tcp_nodelay = 0;
+
+            } else {
+                c->tcp_nopush = NGX_TCP_NOPUSH_UNSET;
+                tcp_nodelay = ngx_tcp_nodelay_and_tcp_nopush ? 1 : 0;
+            }
+
+        } else {
+            tcp_nodelay = 1;
+        }
+
+        clcf = ngx_http_get_module_loc_conf(stream->request,
+                                            ngx_http_core_module);
+
+        if (tcp_nodelay
+            && clcf->tcp_nodelay
+            && c->tcp_nodelay == NGX_TCP_NODELAY_UNSET)
+        {
+            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "tcp_nodelay");
+
+            if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY,
+                           (const void *) &tcp_nodelay, sizeof(int))
+                == -1)
+            {
+#if (NGX_SOLARIS)
+                /* Solaris returns EINVAL if a socket has been shut down */
+                c->log_error = NGX_ERROR_IGNORE_EINVAL;
+#endif
+
+                ngx_connection_error(c, ngx_socket_errno,
+                                     "setsockopt(TCP_NODELAY) failed");
+
+                c->log_error = NGX_ERROR_INFO;
+                c->error = 1;
+
+            } else {
+                c->tcp_nodelay = NGX_TCP_NODELAY_SET;
+            }
+        }
     }
 
     if (sc->stream == stream) {