[PATCH] Add support for tcp_user_timeout in http listen directive

Maxim Dounin mdounin at mdounin.ru
Wed Jul 8 16:33:32 UTC 2015


Hello!

On Mon, Jun 29, 2015 at 03:55:25PM -0700, Andy Isaacson wrote:

> # HG changeset patch
> # User Andy Isaacson <adi at orionlabs.co>
> # Date 1435618451 25200
> #      Mon Jun 29 15:54:11 2015 -0700
> # Node ID c11304760218324ea55de7250a613af8f13e431b
> # Parent  b95e70ae6bcdbae99a967df01e1011839f19ee0e
> Add support for tcp_user_timeout in http listen directive
> 
> This commit adds support for a new tcp_user_timeout=<timeout_sec>
> parameter to the listen directive.  When enabled and set to a value
> greater than zero, the TCP_USER_TIMEOUT sockopt is set.  From tcp(7):
> 
>     This specifies the maximum amount of time in milliseconds
>     that transmitted data may remain unacknowledged before TCP
>     will forcibly close the corresponding connection and return
>     ETIMEDOUT to the application.
> 
> Without this capability, a HTTP longpoll connection can remain active
> for up to 950 seconds after the last ACK from the client.
> 
> Note that the tcp_user_timeout value is specified in (integer) seconds,
> but the setsockopt API is specified in milliseconds.
> 
> This capability is similar to the systemwide configuration
> net.ipv4.tcp_retries2 on Linux, but more flexible and per-socket.

It would be a good idea to clarify expected use cases and how it's 
different from SO_KEEPALIVE / TCP_KEEPCNT / TCP_KEEPIDLE / 
TCP_KEEPINTVL we already have.

> 
> diff -r b95e70ae6bcd -r c11304760218 auto/unix
> --- a/auto/unix	Thu Sep 05 16:53:02 2013 +0400
> +++ b/auto/unix	Mon Jun 29 15:54:11 2015 -0700
> @@ -330,6 +330,18 @@
>  . auto/feature
>  
>  
> +ngx_feature="TCP_USER_TIMEOUT"
> +ngx_feature_name="NGX_HAVE_TCP_USER_TIMEOUT"
> +ngx_feature_run=no
> +ngx_feature_incs="#include <sys/socket.h>
> +                  #include <netinet/in.h>
> +                  #include <netinet/tcp.h>"
> +ngx_feature_path=
> +ngx_feature_libs=
> +ngx_feature_test="setsockopt(0, IPPROTO_TCP, TCP_USER_TIMEOUT, NULL, 0)"
> +. auto/feature
> +
> +

Probably putting the test after the TCP_KEEPIDLE test would be 
more logical.

>  ngx_feature="TCP_KEEPIDLE"
>  ngx_feature_name="NGX_HAVE_KEEPALIVE_TUNABLE"
>  ngx_feature_run=no
> diff -r b95e70ae6bcd -r c11304760218 src/core/ngx_connection.h
> --- a/src/core/ngx_connection.h	Thu Sep 05 16:53:02 2013 +0400
> +++ b/src/core/ngx_connection.h	Mon Jun 29 15:54:11 2015 -0700
> @@ -80,6 +80,10 @@
>      int                 setfib;
>  #endif
>  
> +#if (NGX_HAVE_TCP_USER_TIMEOUT)
> +    int                 tcp_user_timeout;
> +#endif
> +
>  };
>  
>  
> diff -r b95e70ae6bcd -r c11304760218 src/event/ngx_event_accept.c
> --- a/src/event/ngx_event_accept.c	Thu Sep 05 16:53:02 2013 +0400
> +++ b/src/event/ngx_event_accept.c	Mon Jun 29 15:54:11 2015 -0700
> @@ -284,6 +284,23 @@
>              }
>          }
>  
> +#if (NGX_HAVE_TCP_USER_TIMEOUT)
> +#ifdef TCP_USER_TIMEOUT
> +        if (ls->tcp_user_timeout) {
> +            int value = ls->tcp_user_timeout;
> +
> +            if (setsockopt(s, IPPROTO_TCP, TCP_USER_TIMEOUT, &value,
> +                            sizeof(int))
> +                == -1)
> +            {
> +                ngx_log_error(NGX_LOG_ALERT, log, ngx_socket_errno,
> +                              "setsockopt(TCP_USER_TIMEOUT, %d) for %V failed",
> +                              value, c->addr_text);
> +            }
> +        }
> +#endif
> +#endif
> +
>  #if (NGX_DEBUG)
>          {
>  

This looks like a very wrong file to put the code.  The option 
should be set on a listening socket instead.  Doing an extra 
syscall on each accept() is clearly bad idea.

There is no need to check if the TCP_USER_TIMEOUT macro is defined 
twice.  It's already tested by the configure test, and there is no 
need to test it again.

Please add

[diff]
showfunc = true

to your ~/.hgrc to simplify review.

> diff -r b95e70ae6bcd -r c11304760218 src/http/ngx_http.c
> --- a/src/http/ngx_http.c	Thu Sep 05 16:53:02 2013 +0400
> +++ b/src/http/ngx_http.c	Mon Jun 29 15:54:11 2015 -0700
> @@ -1800,6 +1800,10 @@
>      ls->deferred_accept = addr->opt.deferred_accept;
>  #endif
>  
> +#if (NGX_HAVE_TCP_USER_TIMEOUT && defined TCP_USER_TIMEOUT)
> +    ls->tcp_user_timeout = addr->opt.tcp_user_timeout;
> +#endif
> +
>  #if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
>      ls->ipv6only = addr->opt.ipv6only;
>  #endif
> diff -r b95e70ae6bcd -r c11304760218 src/http/ngx_http_core_module.c
> --- a/src/http/ngx_http_core_module.c	Thu Sep 05 16:53:02 2013 +0400
> +++ b/src/http/ngx_http_core_module.c	Mon Jun 29 15:54:11 2015 -0700
> @@ -4085,6 +4085,31 @@
>              continue;
>          }
>  
> +        if (ngx_strncmp(value[n].data, "tcp_user_timeout=", 17) == 0) {
> +#if (NGX_HAVE_TCP_USER_TIMEOUT && defined TCP_USER_TIMEOUT)
> +            int timeout_sec = ngx_atoi(value[n].data + 17, value[n].len - 17);
> +
> +            /*
> +             * convert from seconds (in config file) to milliseconds (for
> +             * setsockopt)
> +             */
> +            lsopt.tcp_user_timeout = timeout_sec * 1000;

Consider using ngx_parse_time() instead.

> +
> +            if (lsopt.tcp_user_timeout < 0) {
> +                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
> +                                   "Invalid tcp_user_timeout \"%V\"",

The message doesn't match style of other messages.

> +                                   &value[n]);
> +                return NGX_CONF_ERROR;
> +            }
> +#else
> +            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
> +                               "tcp_user_timeout \"%V\" is not supported on "

The "%V" is useless here.

> +                               "this platform, ignored",
> +                               &value[n]);
> +#endif
> +            continue;
> +        }
> +
>          if (ngx_strcmp(value[n].data, "deferred") == 0) {
>  #if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)
>              lsopt.deferred_accept = 1;
> diff -r b95e70ae6bcd -r c11304760218 src/http/ngx_http_core_module.h
> --- a/src/http/ngx_http_core_module.h	Thu Sep 05 16:53:02 2013 +0400
> +++ b/src/http/ngx_http_core_module.h	Mon Jun 29 15:54:11 2015 -0700
> @@ -102,6 +102,10 @@
>      ngx_uint_t                 deferred_accept;
>  #endif
>  
> +#if (NGX_HAVE_TCP_USER_TIMEOUT && defined TCP_USER_TIMEOUT)
> +    int                        tcp_user_timeout;
> +#endif
> +

The field ordering is type-based here.
Consider putting this somewhere after tcp_keepidle instead.

>      u_char                     addr[NGX_SOCKADDR_STRLEN + 1];
>  } ngx_http_listen_opt_t;
>  
> 

-- 
Maxim Dounin
http://nginx.org/



More information about the nginx-devel mailing list