[PATCH] Proxy: make timeout directives accept variables

Thibault Charbonnier thibaultcha at fastmail.com
Tue Jan 31 01:39:13 UTC 2017


Hello!

This is a proposal to allow for better granularity (eventually, on a 
per-request basis?) regarding the proxy_*_timeout directives.

Please let me know how you feel about such a feature, and if you think 
this patch should be implemented in a different way.

Thanks!

# HG changeset patch
# User Thibault Charbonnier <thibaultcha at fastmail.com>
# Date 1485823618 28800
#      Mon Jan 30 16:46:58 2017 -0800
# Branch proxy/variables-in-timeouts
# Node ID 9f27c1d40590566ae5ac55e04af473d5f8e97d87
# Parent  640f035293959b2d4b0ba5939d954bc517f57f77
Proxy: make timeout directives accept variables

We try to stay as close as possible from the original behavior regarding the
parsing of the ngx_msec_t fields, to preserve the proper parsing error logs.
As long as the timeout directives do not contain a variable, the behavior is
identical as prior to this patch.

If a timeout directive contains a variable, we consider such variable to
contain the related timeout in ms precision.

diff -r 640f03529395 -r 9f27c1d40590 
src/http/modules/ngx_http_proxy_module.c
--- a/src/http/modules/ngx_http_proxy_module.c    Fri Jan 27 19:06:35 
2017 +0300
+++ b/src/http/modules/ngx_http_proxy_module.c    Mon Jan 30 16:46:58 
2017 -0800
@@ -45,6 +45,16 @@


  typedef struct {
+    ngx_uint_t                     complex;
+
+    union {
+        ngx_msec_t                 simple;
+        ngx_http_complex_value_t  *complex;
+    } value;
+} ngx_http_proxy_timeout_t;
+
+
+typedef struct {
      ngx_array_t                   *flushes;
      ngx_array_t                   *lengths;
      ngx_array_t                   *values;
@@ -77,6 +87,10 @@
      ngx_str_t                      location;
      ngx_str_t                      url;

+    ngx_http_proxy_timeout_t      *connect_timeout;
+    ngx_http_proxy_timeout_t      *send_timeout;
+    ngx_http_proxy_timeout_t      *read_timeout;
+
  #if (NGX_HTTP_CACHE)
      ngx_http_complex_value_t       cache_key;
  #endif
@@ -171,6 +185,8 @@
  static ngx_int_t ngx_http_proxy_init_headers(ngx_conf_t *cf,
      ngx_http_proxy_loc_conf_t *conf, ngx_http_proxy_headers_t *headers,
      ngx_keyval_t *default_headers);
+static ngx_int_t ngx_http_proxy_compile_timeout(ngx_http_request_t *r,
+    ngx_http_complex_value_t *c_timeout, ngx_msec_t *u_timeout);

  static char *ngx_http_proxy_pass(ngx_conf_t *cf, ngx_command_t *cmd,
      void *conf);
@@ -182,6 +198,8 @@
      void *conf);
  static char *ngx_http_proxy_store(ngx_conf_t *cf, ngx_command_t *cmd,
      void *conf);
+static char * ngx_http_proxy_timeout(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
  #if (NGX_HTTP_CACHE)
  static char *ngx_http_proxy_cache(ngx_conf_t *cf, ngx_command_t *cmd,
      void *conf);
@@ -324,16 +342,16 @@

      { ngx_string("proxy_connect_timeout"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
-      ngx_conf_set_msec_slot,
+      ngx_http_proxy_timeout,
        NGX_HTTP_LOC_CONF_OFFSET,
-      offsetof(ngx_http_proxy_loc_conf_t, upstream.connect_timeout),
+      offsetof(ngx_http_proxy_loc_conf_t, connect_timeout),
        NULL },

      { ngx_string("proxy_send_timeout"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
-      ngx_conf_set_msec_slot,
+      ngx_http_proxy_timeout,
        NGX_HTTP_LOC_CONF_OFFSET,
-      offsetof(ngx_http_proxy_loc_conf_t, upstream.send_timeout),
+      offsetof(ngx_http_proxy_loc_conf_t, send_timeout),
        NULL },

      { ngx_string("proxy_send_lowat"),
@@ -408,9 +426,9 @@

      { ngx_string("proxy_read_timeout"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
-      ngx_conf_set_msec_slot,
+      ngx_http_proxy_timeout,
        NGX_HTTP_LOC_CONF_OFFSET,
-      offsetof(ngx_http_proxy_loc_conf_t, upstream.read_timeout),
+      offsetof(ngx_http_proxy_loc_conf_t, read_timeout),
        NULL },

      { ngx_string("proxy_buffers"),
@@ -829,6 +847,11 @@
  };


+static ngx_http_proxy_timeout_t  ngx_http_proxy_default_timeout = {
+    0, { 60000 }
+};
+
+
  static ngx_int_t
  ngx_http_proxy_handler(ngx_http_request_t *r)
  {
@@ -1137,6 +1160,7 @@
      size_t                        len, uri_len, loc_len, body_len;
      uintptr_t                     escape;
      ngx_buf_t                    *b;
+    ngx_int_t                     rc;
      ngx_str_t                     method;
      ngx_uint_t                    i, unparsed_uri;
      ngx_chain_t                  *cl, *body;
@@ -1160,6 +1184,33 @@
      headers = &plcf->headers;
  #endif

+    if (plcf->connect_timeout->complex) {
+        rc = ngx_http_proxy_compile_timeout(r,
+ plcf->connect_timeout->value.complex,
+ &u->conf->connect_timeout);
+        if (rc != NGX_OK) {
+            return rc;
+        }
+    }
+
+    if (plcf->send_timeout->complex) {
+        rc = ngx_http_proxy_compile_timeout(r,
+ plcf->send_timeout->value.complex,
+ &u->conf->send_timeout);
+        if (rc != NGX_OK) {
+            return rc;
+        }
+    }
+
+    if (plcf->read_timeout->complex) {
+        rc = ngx_http_proxy_compile_timeout(r,
+ plcf->read_timeout->value.complex,
+ &u->conf->read_timeout);
+        if (rc != NGX_OK) {
+            return rc;
+        }
+    }
+
      if (u->method.len) {
          /* HEAD was changed to GET to cache response */
          method = u->method;
@@ -2805,6 +2856,9 @@
       *     conf->upstream.ssl_name = NULL;
       *
       *     conf->method = NULL;
+     *     conf->connect_timeout = NULL;
+     *     conf->send_timeout = NULL;
+     *     conf->read_timeout = NULL;
       *     conf->headers_source = NULL;
       *     conf->headers.lengths = NULL;
       *     conf->headers.values = NULL;
@@ -2953,14 +3007,35 @@
      ngx_conf_merge_ptr_value(conf->upstream.local,
                                prev->upstream.local, NULL);

-    ngx_conf_merge_msec_value(conf->upstream.connect_timeout,
-                              prev->upstream.connect_timeout, 60000);
-
-    ngx_conf_merge_msec_value(conf->upstream.send_timeout,
-                              prev->upstream.send_timeout, 60000);
-
-    ngx_conf_merge_msec_value(conf->upstream.read_timeout,
-                              prev->upstream.read_timeout, 60000);
+    if (conf->connect_timeout == NULL) {
+        conf->connect_timeout = (prev->connect_timeout != NULL)
+                                    ? prev->connect_timeout
+                                    : &ngx_http_proxy_default_timeout;
+    }
+
+    if (!conf->connect_timeout->complex) {
+        conf->upstream.connect_timeout = 
conf->connect_timeout->value.simple;
+    }
+
+    if (conf->send_timeout == NULL) {
+        conf->send_timeout = (prev->send_timeout != NULL)
+                                    ? prev->send_timeout
+                                    : &ngx_http_proxy_default_timeout;
+    }
+
+    if (!conf->send_timeout->complex) {
+        conf->upstream.send_timeout = conf->send_timeout->value.simple;
+    }
+
+    if (conf->read_timeout == NULL) {
+        conf->read_timeout = (prev->read_timeout != NULL)
+                                    ? prev->read_timeout
+                                    : &ngx_http_proxy_default_timeout;
+    }
+
+    if (!conf->read_timeout->complex) {
+        conf->upstream.read_timeout = conf->read_timeout->value.simple;
+    }

ngx_conf_merge_msec_value(conf->upstream.next_upstream_timeout,
prev->upstream.next_upstream_timeout, 0);
@@ -3746,6 +3821,61 @@


  static char *
+ngx_http_proxy_timeout(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    char                               *p = conf;
+
+    ngx_msec_t                          msp;
+    ngx_str_t                          *value;
+    ngx_http_proxy_timeout_t          **t;
+    ngx_http_complex_value_t           *cv;
+    ngx_http_compile_complex_value_t    ccv;
+
+    t = (ngx_http_proxy_timeout_t **) (p + cmd->offset);
+    if (*t != NULL) {
+        return "is duplicate";
+    }
+
+    *t = ngx_palloc(cf->pool, sizeof(ngx_http_proxy_timeout_t));
+    if (*t == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    value = cf->args->elts;
+
+    if (ngx_http_script_variables_count(&value[1]) > 0) {
+        cv = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));
+        if (cv == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+        ccv.cf = cf;
+        ccv.value = &value[1];
+        ccv.complex_value = cv;
+
+        if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+            return NGX_CONF_ERROR;
+        }
+
+        (*t)->complex = 1;
+        (*t)->value.complex = cv;
+
+    } else {
+        msp = ngx_parse_time(&value[1], 0);
+        if (msp == (ngx_msec_t) NGX_ERROR) {
+            return "invalid value";
+        }
+
+        (*t)->value.simple = msp;
+    }
+
+    return NGX_CONF_OK;
+}
+
+
+static char *
  ngx_http_proxy_redirect(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  {
      ngx_http_proxy_loc_conf_t *plcf = conf;
@@ -4410,3 +4540,29 @@

      v->uri = u->uri;
  }
+
+
+static ngx_int_t
+ngx_http_proxy_compile_timeout(ngx_http_request_t *r,
+    ngx_http_complex_value_t *c_timeout, ngx_msec_t *u_timeout)
+{
+    ngx_str_t            timeout;
+    ngx_msec_t           msp;
+
+    if (ngx_http_complex_value(r, c_timeout, &timeout) != NGX_OK) {
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                      "could not compile timeout value");
+        return NGX_ERROR;
+    }
+
+    msp = ngx_atoi(timeout.data, timeout.len);
+    if (msp == (ngx_msec_t) NGX_ERROR) {
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                      "invalid timeout value: \"%V\"", &timeout);
+        return NGX_ERROR;
+    }
+
+    *u_timeout = msp;
+
+    return NGX_OK;
+}


More information about the nginx-devel mailing list