From borodin at octonica.com Thu Jun 1 11:54:50 2017 From: borodin at octonica.com (Andrew Borodin) Date: Thu, 1 Jun 2017 16:54:50 +0500 Subject: Use primes for hashtable size In-Reply-To: <20170530130137.GT55433@mdounin.ru> References: <20170530130137.GT55433@mdounin.ru> Message-ID: Hi, Maxim! 2017-05-30 18:01 GMT+05:00 Maxim Dounin : > > The maximum size of hash table as specified by the hinit->max_size > field is indeed maximum size, and not the size of the hash table. > Following code in the ngx_hash_init() will try hard to find to > find out an optimal hash size for a given set of values within the > maximum size specified, and will test all the prime numbers as > well. > > I see no reasons to additionally limit the maximum size to a prime > number. If you think there are some, please be more specific. > > You are right. I've modified patch to checkout primes first, then proceed to "hard work" . Also I've kolhozed some perf prove of improvement. This test creates a hash table of 5000 semirandom strings (not very random, just bytes permutated). On my Ubuntu VM without patch hash creation is 92-96ms, with patch it's strictly 0. "hard work" search tries about 2k sizes before success, primes search hits at second. Docs say some words about startup speed and I wanted to apply primes somewhere, so here we go. Best regards, Andrey Borodin. -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- diff --git a/src/core/ngx_hash.c b/src/core/ngx_hash.c index 1944c7a21d..4e733c4625 100644 --- a/src/core/ngx_hash.c +++ b/src/core/ngx_hash.c @@ -244,6 +244,152 @@ ngx_hash_find_combined(ngx_hash_combined_t *hash, ngx_uint_t key, u_char *name, return NULL; } +static ngx_uint_t ngx_hash_min_prime(ngx_uint_t value) { + static ngx_uint_t primes[] = + { + 3, + 7, + 11, + 17, + 23, + 29, + 37, + 47, + 59, + 71, + 89, + 107, + 131, + 163, + 197, + 239, + 293, + 353, + 431, + 521, + 631, + 761, + 919, + 1103, + 1327, + 1597, + 1931, + 2333, + 2801, + 3371, + 4049, + 4861, + 5839, + 7013, + 8419, + 10103, + 12143, + 14591, + 17519, + 21023, + 25229, + 30293, + 36353, + 43627, + 52361, + 62851, + 75431, + 90523, + 108631, + 130363, + 156437, + 187751, + 225307, + 270371, + 324449, + 389357, + 467237, + 560689, + 672827, + 807403, + 968897, + 1162687, + 1395263, + 1674319, + 2009191, + 2411033, + 2893249, + 3471899, + 4166287, + 4999559, + 5999471, + 7199369, + 7919311, + 8711267, + 9582409, + 10540661, + 11594729, + 12754219, + 14029643, + 15432619, + 16975891, + 18673483, + 20540831, + 22594919, + 24854419, + 27339863, + 30073853, + 33081239, + 36389369, + 40028333, + 44031179, + 48434303, + 53277769, + 58605563, + 64466147, + 70912783, + 78004061, + 85804471, + 94384919, + 103823417, + 114205771, + 125626351, + 138189017, + 152007971, + 167208817, + 183929719, + 202322693, + 222554977, + 244810487, + 269291537, + 296220709, + 325842779, + 358427071, + 394269781, + 433696759, + 477066449, + 524773133, + 577250461, + 634975519, + 698473099, + 768320467, + 845152513, + 929667799, + 1022634581, + 1124898043, + 1237387859, + 1361126671, + 1497239377, + 1646963321, + 1811659669, + 1992825643, + }; + + for (size_t i = 0; i < sizeof(primes)/sizeof(*primes); ++i) + { + ngx_uint_t prime = primes[i]; + if (prime > value) + { + return prime; + } + } + return value; +} #define NGX_HASH_ELT_SIZE(name) \ (sizeof(void *) + ngx_align((name)->key.len + 2, sizeof(void *))) @@ -283,6 +429,36 @@ ngx_hash_init(ngx_hash_init_t *hinit, ngx_hash_key_t *names, ngx_uint_t nelts) bucket_size = hinit->bucket_size - sizeof(void *); + /* + * For hash size check primes first, then try others. + * There cannot be more than log(max_size) primes to test + */ + + for (size = ngx_hash_min_prime(nelts); size <= hinit->max_size; size = ngx_hash_min_prime(size)) { + + ngx_memzero(test, size * sizeof(u_short)); + + for (n = 0; n < nelts; n++) { + if (names[n].key.data == NULL) { + continue; + } + + key = names[n].key_hash % size; + test[key] = (u_short) (test[key] + NGX_HASH_ELT_SIZE(&names[n])); + + if (test[key] > (u_short) bucket_size) { + goto next_prime; + } + } + + goto found; + + next_prime: + + continue; + } + + start = nelts / (bucket_size / (2 * sizeof(void *))); start = start ? start : 1; -------------- next part -------------- diff --git a/src/core/nginx.c b/src/core/nginx.c index abaa50d618..904a11fb76 100644 --- a/src/core/nginx.c +++ b/src/core/nginx.c @@ -8,6 +8,8 @@ #include #include #include +#include +#include static void ngx_show_version_info(void); @@ -282,6 +284,49 @@ main(int argc, char *const *argv) } cycle = ngx_init_cycle(&init_cycle); + + + /* Here will be hash tests */ + { + size_t elementscount = 5000; + ngx_hash_t hashx; + ngx_hash_key_t *elements; + ngx_hash_init_t hash; + struct rusage start,end; + ngx_log_stderr(0,"doing tests"); + + + elements = ngx_alloc(sizeof(ngx_hash_key_t)*elementscount,log); + + hash.hash = &hashx; + hash.key = ngx_hash_key; + hash.max_size = 10000; + hash.bucket_size = 64; + hash.name = "test_hash"; + hash.pool = ngx_create_pool(NGX_CYCLE_POOL_SIZE, log); + hash.temp_pool = NULL; + + srand(42); + for(size_t i=0;i>4)%16); + ((u_char*)elements[i].key.data)[2] = 'a' + ((i>>8)%16); + ((u_char*)elements[i].key.data)[0] = 'a' + ((i>>16)%16); + elements[i].key_hash = ngx_hash_key(elements[i].key.data,4); + } + + ngx_log_stderr(0, "before init"); + getrusage(RUSAGE_SELF,&start); + ngx_hash_init(&hash, elements , elementscount); + getrusage(RUSAGE_SELF,&end); + ngx_log_stderr(0, "after init"); + ngx_log_stderr(0, "time %d",end.ru_utime.tv_usec - start.ru_utime.tv_usec); + return 0; + } + if (cycle == NULL) { if (ngx_test_config) { ngx_log_stderr(0, "configuration file %s test failed", @@ -317,6 +362,7 @@ main(int argc, char *const *argv) return 0; } + if (ngx_signal) { return ngx_signal_process(cycle, ngx_signal); } From mdounin at mdounin.ru Thu Jun 1 13:52:24 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Thu, 01 Jun 2017 13:52:24 +0000 Subject: [nginx] Headers filter: style. Message-ID: details: http://hg.nginx.org/nginx/rev/057ec63be834 branches: changeset: 7017:057ec63be834 user: Piotr Sikora date: Wed May 31 13:51:35 2017 -0700 description: Headers filter: style. Signed-off-by: Piotr Sikora diffstat: src/http/modules/ngx_http_headers_filter_module.c | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diffs (21 lines): diff --git a/src/http/modules/ngx_http_headers_filter_module.c b/src/http/modules/ngx_http_headers_filter_module.c --- a/src/http/modules/ngx_http_headers_filter_module.c +++ b/src/http/modules/ngx_http_headers_filter_module.c @@ -98,7 +98,7 @@ static ngx_command_t ngx_http_headers_f ngx_http_headers_expires, NGX_HTTP_LOC_CONF_OFFSET, 0, - NULL}, + NULL }, { ngx_string("add_header"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF @@ -106,7 +106,7 @@ static ngx_command_t ngx_http_headers_f ngx_http_headers_add, NGX_HTTP_LOC_CONF_OFFSET, 0, - NULL}, + NULL }, ngx_null_command }; From mdounin at mdounin.ru Thu Jun 1 13:52:27 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Thu, 01 Jun 2017 13:52:27 +0000 Subject: [nginx] Upstream: style. Message-ID: details: http://hg.nginx.org/nginx/rev/610a219b28f6 branches: changeset: 7018:610a219b28f6 user: Piotr Sikora date: Wed May 31 13:51:36 2017 -0700 description: Upstream: style. Signed-off-by: Piotr Sikora diffstat: src/http/ngx_http_upstream.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diffs (12 lines): diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c --- a/src/http/ngx_http_upstream.c +++ b/src/http/ngx_http_upstream.c @@ -2729,7 +2729,7 @@ ngx_http_upstream_process_body_in_memory rev = c->read; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http upstream process body on memory"); + "http upstream process body in memory"); if (rev->timedout) { ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out"); From mdounin at mdounin.ru Thu Jun 1 13:52:30 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Thu, 01 Jun 2017 13:52:30 +0000 Subject: [nginx] Style. Message-ID: details: http://hg.nginx.org/nginx/rev/8ce1a34f160b branches: changeset: 7019:8ce1a34f160b user: Maxim Dounin date: Thu Jun 01 16:49:14 2017 +0300 description: Style. diffstat: src/os/unix/ngx_udp_sendmsg_chain.c | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-) diffs (20 lines): diff --git a/src/os/unix/ngx_udp_sendmsg_chain.c b/src/os/unix/ngx_udp_sendmsg_chain.c --- a/src/os/unix/ngx_udp_sendmsg_chain.c +++ b/src/os/unix/ngx_udp_sendmsg_chain.c @@ -206,13 +206,13 @@ ngx_sendmsg(ngx_connection_t *c, ngx_iov #if (NGX_HAVE_MSGHDR_MSG_CONTROL) #if (NGX_HAVE_IP_SENDSRCADDR) - u_char msg_control[CMSG_SPACE(sizeof(struct in_addr))]; + u_char msg_control[CMSG_SPACE(sizeof(struct in_addr))]; #elif (NGX_HAVE_IP_PKTINFO) - u_char msg_control[CMSG_SPACE(sizeof(struct in_pktinfo))]; + u_char msg_control[CMSG_SPACE(sizeof(struct in_pktinfo))]; #endif #if (NGX_HAVE_INET6 && NGX_HAVE_IPV6_RECVPKTINFO) - u_char msg_control6[CMSG_SPACE(sizeof(struct in6_pktinfo))]; + u_char msg_control6[CMSG_SPACE(sizeof(struct in6_pktinfo))]; #endif #endif From mdounin at mdounin.ru Thu Jun 1 13:52:42 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Thu, 1 Jun 2017 16:52:42 +0300 Subject: [PATCH] Headers filter: style In-Reply-To: <057ec63be834988b6435.1496264040@piotrsikora.sfo.corp.google.com> References: <057ec63be834988b6435.1496264040@piotrsikora.sfo.corp.google.com> Message-ID: <20170601135242.GK55433@mdounin.ru> Hello! On Wed, May 31, 2017 at 01:54:00PM -0700, Piotr Sikora via nginx-devel wrote: > # HG changeset patch > # User Piotr Sikora > # Date 1496263895 25200 > # Wed May 31 13:51:35 2017 -0700 > # Node ID 057ec63be834988b6435b4ef64a1c3bd0cc23959 > # Parent ab6ef3037840393752d82fac01ea1eb4f972301c > Headers filter: style. > > Signed-off-by: Piotr Sikora > > diff -r ab6ef3037840 -r 057ec63be834 src/http/modules/ngx_http_headers_filter_module.c > --- a/src/http/modules/ngx_http_headers_filter_module.c > +++ b/src/http/modules/ngx_http_headers_filter_module.c > @@ -98,7 +98,7 @@ static ngx_command_t ngx_http_headers_f > ngx_http_headers_expires, > NGX_HTTP_LOC_CONF_OFFSET, > 0, > - NULL}, > + NULL }, > > { ngx_string("add_header"), > NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF > @@ -106,7 +106,7 @@ static ngx_command_t ngx_http_headers_f > ngx_http_headers_add, > NGX_HTTP_LOC_CONF_OFFSET, > 0, > - NULL}, > + NULL }, > > ngx_null_command > }; Committed, thanks. -- Maxim Dounin http://nginx.org/ From mdounin at mdounin.ru Thu Jun 1 13:52:54 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Thu, 1 Jun 2017 16:52:54 +0300 Subject: [PATCH] Upstream: style In-Reply-To: References: Message-ID: <20170601135254.GL55433@mdounin.ru> Hello! On Wed, May 31, 2017 at 01:54:05PM -0700, Piotr Sikora via nginx-devel wrote: > # HG changeset patch > # User Piotr Sikora > # Date 1496263896 25200 > # Wed May 31 13:51:36 2017 -0700 > # Node ID e7219bf8bc3781d3912a951f09553bb2f0a53b70 > # Parent ab6ef3037840393752d82fac01ea1eb4f972301c > Upstream: style. > > Signed-off-by: Piotr Sikora > > diff -r ab6ef3037840 -r e7219bf8bc37 src/http/ngx_http_upstream.c > --- a/src/http/ngx_http_upstream.c > +++ b/src/http/ngx_http_upstream.c > @@ -2729,7 +2729,7 @@ ngx_http_upstream_process_body_in_memory > rev = c->read; > > ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, > - "http upstream process body on memory"); > + "http upstream process body in memory"); > > if (rev->timedout) { > ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out"); Committed, thanks. -- Maxim Dounin http://nginx.org/ From arut at nginx.com Thu Jun 1 14:05:18 2017 From: arut at nginx.com (Roman Arutyunyan) Date: Thu, 01 Jun 2017 14:05:18 +0000 Subject: [nginx] Configure: disabled IP_PKTINFO feature on certain platforms. Message-ID: details: http://hg.nginx.org/nginx/rev/716852cce913 branches: changeset: 7020:716852cce913 user: Roman Arutyunyan date: Thu Jun 01 15:44:23 2017 +0300 description: Configure: disabled IP_PKTINFO feature on certain platforms. On Cygwin and NetBSD 7.0+ struct in_pktinfo has no ipi_spec_dst field, which caused nginx compilation error. Now presence of this field is ensured by the IP_PKTINFO feature test. The problem was introduced by dbb0c854e308 (1.13.0). diffstat: auto/unix | 5 ++++- 1 files changed, 4 insertions(+), 1 deletions(-) diffs (15 lines): diff -r 8ce1a34f160b -r 716852cce913 auto/unix --- a/auto/unix Thu Jun 01 16:49:14 2017 +0300 +++ b/auto/unix Thu Jun 01 15:44:23 2017 +0300 @@ -428,7 +428,10 @@ ngx_feature_incs="#include " ngx_feature_path= ngx_feature_libs= -ngx_feature_test="setsockopt(0, IPPROTO_IP, IP_PKTINFO, NULL, 0)" +ngx_feature_test="struct in_pktinfo pkt; + pkt.ipi_spec_dst.s_addr = INADDR_ANY; + (void) pkt; + setsockopt(0, IPPROTO_IP, IP_PKTINFO, NULL, 0)" . auto/feature From vbart at nginx.com Thu Jun 1 14:48:25 2017 From: vbart at nginx.com (Valentin V. Bartenev) Date: Thu, 01 Jun 2017 17:48:25 +0300 Subject: [PATCH 1 of 4] HTTP/2: emit new frames only after applying all SETTINGS params In-Reply-To: <07adf0a7009c3244de4b.1493074103@piotrsikora.sfo.corp.google.com> References: <07adf0a7009c3244de4b.1493074103@piotrsikora.sfo.corp.google.com> Message-ID: <8753794.qGPzJ25FTB@vbart-workstation> On Monday 24 April 2017 15:48:23 Piotr Sikora via nginx-devel wrote: > # HG changeset patch > # User Piotr Sikora > # Date 1493073310 25200 > # Mon Apr 24 15:35:10 2017 -0700 > # Node ID 07adf0a7009c3244de4b795c0c06927f4316a87f > # Parent 2c4dbcd6f2e4c9c2a1eb8dc1f0d39c99975ae208 > HTTP/2: emit new frames only after applying all SETTINGS params. > > Previously, new frames could be emitted in the middle of applying > new (and already acknowledged) SETTINGS params, which is illegal. > > Signed-off-by: Piotr Sikora > > diff -r 2c4dbcd6f2e4 -r 07adf0a7009c src/http/v2/ngx_http_v2.c > --- a/src/http/v2/ngx_http_v2.c > +++ b/src/http/v2/ngx_http_v2.c > @@ -1982,7 +1982,9 @@ static u_char * > ngx_http_v2_state_settings_params(ngx_http_v2_connection_t *h2c, u_char *pos, > u_char *end) > { > - ngx_uint_t id, value; > + ngx_uint_t id, value, adjustment; The new initial window size can be lower than the previous one, so the difference can be negative (that's why the delta parameter of ngx_http_v2_adjust_windows() is ssize_t). Please consider the patch below: diff -r 00015416ae79 src/http/v2/ngx_http_v2.c --- a/src/http/v2/ngx_http_v2.c Mon Apr 24 15:35:10 2017 -0700 +++ b/src/http/v2/ngx_http_v2.c Thu Jun 01 17:45:37 2017 +0300 @@ -1969,7 +1969,8 @@ static u_char * ngx_http_v2_state_settings_params(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end) { - ngx_uint_t id, value, adjustment; + ssize_t window_delta; + ngx_uint_t id, value; adjustment = 0; @@ -1997,7 +1998,8 @@ ngx_http_v2_state_settings_params(ngx_ht NGX_HTTP_V2_FLOW_CTRL_ERROR); } - adjustment = value - h2c->init_window; + window_delta = value - h2c->init_window; + h2c->init_window = value; break; @@ -2024,8 +2026,8 @@ ngx_http_v2_state_settings_params(ngx_ht pos += NGX_HTTP_V2_SETTINGS_PARAM_SIZE; } - if (adjustment) { - if (ngx_http_v2_adjust_windows(h2c, adjustment) != NGX_OK) { + if (window_delta) { + if (ngx_http_v2_adjust_windows(h2c, window_delta) != NGX_OK) { return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR); } wbr, Valentin V. Bartenev From Nate.Karstens at garmin.com Thu Jun 1 17:20:53 2017 From: Nate.Karstens at garmin.com (Karstens, Nate) Date: Thu, 1 Jun 2017 17:20:53 +0000 Subject: PSK Support Message-ID: <145451D4E6785E4DA4DFA353A58CB7B2015E15B7E3@OLAWPA-EXMB04.ad.garmin.com> Greetings, I'm about push 3 patches that add support for PSK TLS cipher suites to nginx and thought it would be good to discuss the feature itself in a separate thread. First, PSK support is useful in certain environments that are not conducive to a full public key infrastructure. The environment I'm personally working with is the recreational boating market; we are developing a new industry standard that relies on HTTPS, secured by PSK, for much of its underlying security protocol. I think this would also be useful to the IoT market. A quick search shows that some other users have been interested in this feature: https://forum.nginx.org/read.php?2,272443,272443 https://stackoverflow.com/questions/22513641/pre-shared-keys-tls-psk-nginx-configuration After applying the patches, one can enable PSK support by adding a few directives to their nginx.conf: 1) "ssl_nocert" -- This disables checks for a certificate within nginx. By default these checks are enabled because most users will need a certificate. This is analogous to the "-nocert" option in the OpenSSL s_server. 2) "ssl_psk_path" -- This is a local folder that contains all of the valid PSKs. Each file in the folder is loaded into memory as a PSK, and its file name is used as the PSK identity. When the client connects it specifies the identity of the PSK it is using for the connection. The server looks up the key using hash of the loaded PSKs and if the keys match then the TLS handshake is successful. Note that the identity of the PSK is made available in the variable $ssl_psk_identity. 3) Add some PSK ciphers to the "ssl_ciphers" directive. Thanks, Nate Karstens Garmin International, Inc. ________________________________ CONFIDENTIALITY NOTICE: This email and any attachments are for the sole use of the intended recipient(s) and contain information that may be Garmin confidential and/or Garmin legally privileged. If you have received this email in error, please notify the sender by reply email and delete the message. Any disclosure, copying, distribution or use of this communication (including attachments) by someone other than the intended recipient is prohibited. Thank you. From Nate.Karstens at garmin.com Thu Jun 1 17:21:01 2017 From: Nate.Karstens at garmin.com (Karstens, Nate) Date: Thu, 1 Jun 2017 17:21:01 +0000 Subject: [PATCH 1 of 3] PSK: make server certificates optional Message-ID: <145451D4E6785E4DA4DFA353A58CB7B2015E15B7F1@OLAWPA-EXMB04.ad.garmin.com> # HG changeset patch # User Nate Karstens # Date 1496332504 18000 # Thu Jun 01 10:55:04 2017 -0500 # Node ID a38066b79d71b6ecb62a9f7618afe2cf3ed8a4f9 # Parent 716852cce9136d977b81a2d1b8b6f9fbca0dce49 PSK: make server certificates optional Adds the directive "ssl_nocert" to the ngx_http_ssl_module to allow the user to indicate that the absence of a certificate is intentional. Any cipher suites that rely on certificates will not function properly. Servers that only use PSK will error out without this change. Signed-off-by: Nate Karstens diff -r 716852cce913 -r a38066b79d71 contrib/vim/syntax/nginx.vim --- a/contrib/vim/syntax/nginx.vim Thu Jun 01 15:44:23 2017 +0300 +++ b/contrib/vim/syntax/nginx.vim Thu Jun 01 10:55:04 2017 -0500 @@ -546,6 +546,7 @@ syn keyword ngxDirective contained ssl_ecdh_curve syn keyword ngxDirective contained ssl_engine syn keyword ngxDirective contained ssl_handshake_timeout +syn keyword ngxDirective contained ssl_nocert syn keyword ngxDirective contained ssl_password_file syn keyword ngxDirective contained ssl_prefer_server_ciphers syn keyword ngxDirective contained ssl_preread diff -r 716852cce913 -r a38066b79d71 src/http/modules/ngx_http_ssl_module.c --- a/src/http/modules/ngx_http_ssl_module.c Thu Jun 01 15:44:23 2017 +0300 +++ b/src/http/modules/ngx_http_ssl_module.c Thu Jun 01 10:55:04 2017 -0500 @@ -101,6 +101,13 @@ 0, NULL }, + { ngx_string("ssl_nocert"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_ssl_srv_conf_t, nocert), + NULL }, + { ngx_string("ssl_dhparam"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_str_slot, @@ -546,6 +553,7 @@ sscf->buffer_size = NGX_CONF_UNSET_SIZE; sscf->verify = NGX_CONF_UNSET_UINT; sscf->verify_depth = NGX_CONF_UNSET_UINT; + sscf->nocert = NGX_CONF_UNSET; sscf->certificates = NGX_CONF_UNSET_PTR; sscf->certificate_keys = NGX_CONF_UNSET_PTR; sscf->passwords = NGX_CONF_UNSET_PTR; @@ -595,6 +603,7 @@ ngx_conf_merge_uint_value(conf->verify, prev->verify, 0); ngx_conf_merge_uint_value(conf->verify_depth, prev->verify_depth, 1); + ngx_conf_merge_value(conf->nocert, prev->nocert, 0); ngx_conf_merge_ptr_value(conf->certificates, prev->certificates, NULL); ngx_conf_merge_ptr_value(conf->certificate_keys, prev->certificate_keys, NULL); @@ -622,50 +631,52 @@ conf->ssl.log = cf->log; - if (conf->enable) { + if (!conf->nocert) { + if (conf->enable) { - if (conf->certificates == NULL) { - ngx_log_error(NGX_LOG_EMERG, cf->log, 0, - "no \"ssl_certificate\" is defined for " - "the \"ssl\" directive in %s:%ui", - conf->file, conf->line); - return NGX_CONF_ERROR; - } + if (conf->certificates == NULL) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no \"ssl_certificate\" is defined for " + "the \"ssl\" directive in %s:%ui", + conf->file, conf->line); + return NGX_CONF_ERROR; + } - if (conf->certificate_keys == NULL) { - ngx_log_error(NGX_LOG_EMERG, cf->log, 0, - "no \"ssl_certificate_key\" is defined for " - "the \"ssl\" directive in %s:%ui", - conf->file, conf->line); - return NGX_CONF_ERROR; - } + if (conf->certificate_keys == NULL) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no \"ssl_certificate_key\" is defined for " + "the \"ssl\" directive in %s:%ui", + conf->file, conf->line); + return NGX_CONF_ERROR; + } - if (conf->certificate_keys->nelts < conf->certificates->nelts) { - ngx_log_error(NGX_LOG_EMERG, cf->log, 0, - "no \"ssl_certificate_key\" is defined " - "for certificate \"%V\" and " - "the \"ssl\" directive in %s:%ui", - ((ngx_str_t *) conf->certificates->elts) - + conf->certificates->nelts - 1, - conf->file, conf->line); - return NGX_CONF_ERROR; - } + if (conf->certificate_keys->nelts < conf->certificates->nelts) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no \"ssl_certificate_key\" is defined " + "for certificate \"%V\" and " + "the \"ssl\" directive in %s:%ui", + ((ngx_str_t *) conf->certificates->elts) + + conf->certificates->nelts - 1, + conf->file, conf->line); + return NGX_CONF_ERROR; + } - } else { + } else { - if (conf->certificates == NULL) { - return NGX_CONF_OK; - } + if (conf->certificates == NULL) { + return NGX_CONF_OK; + } - if (conf->certificate_keys == NULL - || conf->certificate_keys->nelts < conf->certificates->nelts) - { - ngx_log_error(NGX_LOG_EMERG, cf->log, 0, - "no \"ssl_certificate_key\" is defined " - "for certificate \"%V\"", - ((ngx_str_t *) conf->certificates->elts) - + conf->certificates->nelts - 1); - return NGX_CONF_ERROR; + if (conf->certificate_keys == NULL + || conf->certificate_keys->nelts < conf->certificates->nelts) + { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no \"ssl_certificate_key\" is defined " + "for certificate \"%V\"", + ((ngx_str_t *) conf->certificates->elts) + + conf->certificates->nelts - 1); + return NGX_CONF_ERROR; + } } } @@ -704,11 +715,15 @@ cln->handler = ngx_ssl_cleanup_ctx; cln->data = &conf->ssl; - if (ngx_ssl_certificates(cf, &conf->ssl, conf->certificates, - conf->certificate_keys, conf->passwords) - != NGX_OK) - { - return NGX_CONF_ERROR; + if (conf->certificates && conf->certificate_keys) { + + if (ngx_ssl_certificates(cf, &conf->ssl, conf->certificates, + conf->certificate_keys, conf->passwords) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + } if (ngx_ssl_ciphers(cf, &conf->ssl, &conf->ciphers, diff -r 716852cce913 -r a38066b79d71 src/http/modules/ngx_http_ssl_module.h --- a/src/http/modules/ngx_http_ssl_module.h Thu Jun 01 15:44:23 2017 +0300 +++ b/src/http/modules/ngx_http_ssl_module.h Thu Jun 01 10:55:04 2017 -0500 @@ -32,6 +32,7 @@ time_t session_timeout; + ngx_flag_t nocert; ngx_array_t *certificates; ngx_array_t *certificate_keys; ________________________________ CONFIDENTIALITY NOTICE: This email and any attachments are for the sole use of the intended recipient(s) and contain information that may be Garmin confidential and/or Garmin legally privileged. If you have received this email in error, please notify the sender by reply email and delete the message. Any disclosure, copying, distribution or use of this communication (including attachments) by someone other than the intended recipient is prohibited. Thank you. From Nate.Karstens at garmin.com Thu Jun 1 17:21:18 2017 From: Nate.Karstens at garmin.com (Karstens, Nate) Date: Thu, 1 Jun 2017 17:21:18 +0000 Subject: [PATCH 2 of 3] PSK: add configuration directives Message-ID: <145451D4E6785E4DA4DFA353A58CB7B2015E15B7FE@OLAWPA-EXMB04.ad.garmin.com> # HG changeset patch # User Nate Karstens # Date 1496332865 18000 # Thu Jun 01 11:01:05 2017 -0500 # Node ID 7aa7771191d61ef635478460017446bca1f6db55 # Parent a38066b79d71b6ecb62a9f7618afe2cf3ed8a4f9 PSK: add configuration directives Adds the directive "ssl_psk_dir" to the ngx_http_ssl_module. This allows the user to specify a folder that contains the set of PSKs that can be used to form a connection. Each key in the directory is read into memory and the file name is saved as the key identity. Also added the "ssl_psk_hash_max_size" and "ssl_psk_hash_bucket_size" directives to configure the hash that stores PSK data. This functionality can be easily tested with the OpenSSL s_client using the "-psk" and "-psk_identity" options. Signed-off-by: Nate Karstens diff -r a38066b79d71 -r 7aa7771191d6 contrib/vim/syntax/nginx.vim --- a/contrib/vim/syntax/nginx.vim Thu Jun 01 10:55:04 2017 -0500 +++ b/contrib/vim/syntax/nginx.vim Thu Jun 01 11:01:05 2017 -0500 @@ -551,6 +551,9 @@ syn keyword ngxDirective contained ssl_prefer_server_ciphers syn keyword ngxDirective contained ssl_preread syn keyword ngxDirective contained ssl_protocols +syn keyword ngxDirective contained ssl_psk_path +syn keyword ngxDirective contained ssl_psk_hash_bucket_size +syn keyword ngxDirective contained ssl_psk_hash_max_size syn keyword ngxDirective contained ssl_session_cache syn keyword ngxDirective contained ssl_session_ticket_key syn keyword ngxDirective contained ssl_session_tickets diff -r a38066b79d71 -r 7aa7771191d6 src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c Thu Jun 01 10:55:04 2017 -0500 +++ b/src/event/ngx_event_openssl.c Thu Jun 01 11:01:05 2017 -0500 @@ -23,6 +23,8 @@ static int ngx_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store); static void ngx_ssl_info_callback(const ngx_ssl_conn_t *ssl_conn, int where, int ret); +static unsigned int ngx_ssl_psk_callback(SSL *ssl, const char *identity, + unsigned char *psk, unsigned int max_psk_len); static void ngx_ssl_passwords_cleanup(void *data); static void ngx_ssl_handshake_handler(ngx_event_t *ev); static ngx_int_t ngx_ssl_handle_recv(ngx_connection_t *c, int n); @@ -114,6 +116,7 @@ int ngx_ssl_next_certificate_index; int ngx_ssl_certificate_name_index; int ngx_ssl_stapling_index; +int ngx_ssl_psk_index; ngx_int_t @@ -225,6 +228,13 @@ return NGX_ERROR; } + ngx_ssl_psk_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); + + if (ngx_ssl_psk_index == -1) { + ngx_ssl_error(NGX_LOG_ALERT, log, 0, "SSL_get_ex_new_index() failed"); + return NGX_ERROR; + } + return NGX_OK; } @@ -345,6 +355,7 @@ SSL_CTX_set_read_ahead(ssl->ctx, 1); SSL_CTX_set_info_callback(ssl->ctx, ngx_ssl_info_callback); + SSL_CTX_set_psk_server_callback(ssl->ctx, ngx_ssl_psk_callback); return NGX_OK; } @@ -875,6 +886,40 @@ } +static unsigned int ngx_ssl_psk_callback(SSL *ssl, const char *identity, + unsigned char *psk, unsigned int max_psk_len) +{ + SSL_CTX *ssl_ctx; + size_t identity_len; + ngx_hash_t *psk_hash; + ngx_uint_t key; + ngx_str_t *psk_str; + + ssl_ctx = SSL_get_SSL_CTX(ssl); + identity_len = strlen(identity); + + psk_hash = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_psk_index); + if (psk_hash == NULL) { + return 0; + } + + key = ngx_hash_key((u_char *)identity, identity_len); + + psk_str = ngx_hash_find(psk_hash, key, (u_char *)identity, identity_len); + if (psk_str == NULL) { + return 0; + } + + if (psk_str->len > max_psk_len) { + return 0; + } + + ngx_memcpy(psk, psk_str->data, psk_str->len); + + return psk_str->len; +} + + RSA * ngx_ssl_rsa512_key_callback(ngx_ssl_conn_t *ssl_conn, int is_export, int key_length) @@ -3137,6 +3182,158 @@ #endif +ngx_int_t +ngx_ssl_psk_path(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *path, + ngx_uint_t psk_hash_max_size, ngx_uint_t psk_hash_bucket_size) + +{ + ngx_err_t err; + ngx_uint_t level; + ngx_dir_t dir; + ngx_file_t file; + ssize_t read; + ngx_str_t *psk_str; + ngx_hash_keys_arrays_t psk_keys; + ngx_str_t key; + ngx_hash_t *psk_hash; + ngx_hash_init_t hash; + + if (path->len == 0) { + return NGX_OK; + } + + psk_keys.pool = cf->pool; + psk_keys.temp_pool = cf->temp_pool; + + ngx_hash_keys_array_init(&psk_keys, NGX_HASH_SMALL); + + if (ngx_open_dir(path, &dir) == NGX_ERROR) { + err = ngx_errno; + + level = (err == NGX_ENOENT + || err == NGX_ENOTDIR + || err == NGX_ENAMETOOLONG + || err == NGX_EACCES) ? NGX_LOG_ERR : NGX_LOG_CRIT; + + ngx_ssl_error(level, ssl->log, err, + ngx_open_dir_n " \"%s\" failed", path->data); + + return NGX_ERROR; + } + + for ( ;; ) { + ngx_set_errno(0); + + if (ngx_read_dir(&dir) == NGX_ERROR) { + err = ngx_errno; + + if (err == NGX_ENOMOREFILES) { + break; + } + + ngx_ssl_error(NGX_LOG_CRIT, ssl->log, err, + ngx_read_dir_n " \"%V\" failed", &path); + return NGX_ERROR; + } + + if (!ngx_de_is_file(&dir)) { + continue; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ssl->log, 0, + "loading PSK from file: \"%s\"", ngx_de_name(&dir)); + + file.name.len = ngx_de_namelen(&dir) + 2; + file.name.data = ngx_pnalloc(cf->pool, file.name.len); + if (file.name.data == NULL) { + return NGX_ERROR; + } + + ngx_sprintf(file.name.data, "%V/%s%Z", path, ngx_de_name(&dir)); + + file.log = ssl->log; + + file.fd = ngx_open_file(file.name.data, NGX_FILE_RDONLY, 0, 0); + if (file.fd == NGX_INVALID_FILE) { + err = ngx_errno; + ngx_ssl_error(NGX_LOG_CRIT, ssl->log, err, + ngx_open_file_n " \"%s\" failed", file.name.data); + return NGX_ERROR; + } + + if (ngx_fd_info(file.fd, &file.info) == NGX_FILE_ERROR) { + ngx_ssl_error(NGX_LOG_CRIT, ssl->log, ngx_errno, + ngx_fd_info_n " \"%s\" failed", file.name.data); + return NGX_ERROR; + } + + psk_str = ngx_palloc(cf->pool, sizeof(ngx_str_t)); + if (psk_str == NULL) { + return NGX_ERROR; + } + + psk_str->len = file.info.st_size; + psk_str->data = ngx_pnalloc(cf->pool, psk_str->len); + + read = ngx_read_file(&file, psk_str->data, psk_str->len, 0); + if (read == NGX_ERROR) { + ngx_ssl_error(NGX_LOG_CRIT, ssl->log, ngx_errno, + ngx_read_file_n " \"%s\" failed", file.name.data); + return NGX_ERROR; + } + + if ((size_t)read != psk_str->len) { + ngx_ssl_error(NGX_LOG_CRIT, ssl->log, 0, + ngx_read_file_n + " \"%s\" returned only %z bytes instead of %z", + file.name.data, read, psk_str->len); + return NGX_ERROR; + } + + if (ngx_close_file(file.fd) == NGX_FILE_ERROR) { + ngx_ssl_error(NGX_LOG_ALERT, ssl->log, ngx_errno, + ngx_close_file_n " \"%s\" failed", file.name.data); + } + + ngx_pfree(cf->pool, file.name.data); + + key.data = ngx_de_name(&dir); + key.len = ngx_de_namelen(&dir); + + ngx_hash_add_key(&psk_keys, &key, psk_str, NGX_HASH_READONLY_KEY); + } + + if (ngx_close_dir(&dir) == NGX_ERROR) { + ngx_log_error(NGX_LOG_CRIT, ssl->log, ngx_errno, + ngx_close_dir_n " \"%s\" failed", path->data); + } + + psk_hash = ngx_palloc(cf->pool, sizeof(ngx_hash_t)); + + hash.hash = psk_hash; + hash.key = ngx_hash_key; + hash.max_size = psk_hash_max_size; + hash.bucket_size = psk_hash_bucket_size; + hash.name = "psk_hash"; + hash.pool = cf->pool; + hash.temp_pool = cf->temp_pool; + + if (ngx_hash_init(&hash, psk_keys.keys.elts, psk_keys.keys.nelts) + != NGX_OK) + { + return NGX_ERROR; + } + + if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_psk_index, psk_hash) == 0) { + ngx_ssl_error(NGX_LOG_ALERT, ssl->log, 0, + "SSL_CTX_set_ex_data() failed"); + return NGX_ERROR; + } + + return NGX_OK; +} + + void ngx_ssl_cleanup_ctx(void *data) { diff -r a38066b79d71 -r 7aa7771191d6 src/event/ngx_event_openssl.h --- a/src/event/ngx_event_openssl.h Thu Jun 01 10:55:04 2017 -0500 +++ b/src/event/ngx_event_openssl.h Thu Jun 01 11:01:05 2017 -0500 @@ -171,6 +171,9 @@ ssize_t builtin_session_cache, ngx_shm_zone_t *shm_zone, time_t timeout); ngx_int_t ngx_ssl_session_ticket_keys(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_array_t *paths); +ngx_int_t ngx_ssl_psk_path(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *path, + ngx_uint_t psk_hash_max_size, + ngx_uint_t psk_hash_bucket_size); ngx_int_t ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, void *data); ngx_int_t ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c, ngx_uint_t flags); @@ -255,6 +258,7 @@ extern int ngx_ssl_next_certificate_index; extern int ngx_ssl_certificate_name_index; extern int ngx_ssl_stapling_index; +extern int ngx_ssl_psk_index; #endif /* _NGX_EVENT_OPENSSL_H_INCLUDED_ */ diff -r a38066b79d71 -r 7aa7771191d6 src/http/modules/ngx_http_ssl_module.c --- a/src/http/modules/ngx_http_ssl_module.c Thu Jun 01 10:55:04 2017 -0500 +++ b/src/http/modules/ngx_http_ssl_module.c Thu Jun 01 11:01:05 2017 -0500 @@ -240,6 +240,27 @@ NGX_HTTP_SRV_CONF_OFFSET, offsetof(ngx_http_ssl_srv_conf_t, stapling_verify), NULL }, + + { ngx_string("ssl_psk_path"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_ssl_srv_conf_t, psk_path), + NULL }, + + { ngx_string("ssl_psk_hash_max_size"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_ssl_srv_conf_t, psk_hash_max_size), + NULL }, + + { ngx_string("ssl_psk_hash_bucket_size"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_ssl_srv_conf_t, psk_hash_bucket_size), + NULL }, ngx_null_command }; @@ -546,6 +567,7 @@ * sscf->shm_zone = NULL; * sscf->stapling_file = { 0, NULL }; * sscf->stapling_responder = { 0, NULL }; + * sscf->psk_path = { 0, NULL }; */ sscf->enable = NGX_CONF_UNSET; @@ -563,6 +585,8 @@ sscf->session_ticket_keys = NGX_CONF_UNSET_PTR; sscf->stapling = NGX_CONF_UNSET; sscf->stapling_verify = NGX_CONF_UNSET; + sscf->psk_hash_max_size = NGX_CONF_UNSET_UINT; + sscf->psk_hash_bucket_size = NGX_CONF_UNSET_UINT; return sscf; } @@ -629,6 +653,14 @@ ngx_conf_merge_str_value(conf->stapling_responder, prev->stapling_responder, ""); + ngx_conf_merge_str_value(conf->psk_path, prev->psk_path, ""); + ngx_conf_merge_uint_value(conf->psk_hash_max_size, + prev->psk_hash_max_size, 512); + ngx_conf_merge_uint_value(conf->psk_hash_bucket_size, + prev->psk_hash_bucket_size, 64); + conf->psk_hash_bucket_size = ngx_align(conf->psk_hash_bucket_size, + ngx_cacheline_size); + conf->ssl.log = cf->log; if (!conf->nocert) { @@ -815,6 +847,13 @@ } + if (ngx_ssl_psk_path(cf, &conf->ssl, &conf->psk_path, + conf->psk_hash_max_size, conf->psk_hash_bucket_size) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + return NGX_CONF_OK; } diff -r a38066b79d71 -r 7aa7771191d6 src/http/modules/ngx_http_ssl_module.h --- a/src/http/modules/ngx_http_ssl_module.h Thu Jun 01 10:55:04 2017 -0500 +++ b/src/http/modules/ngx_http_ssl_module.h Thu Jun 01 11:01:05 2017 -0500 @@ -56,6 +56,10 @@ ngx_str_t stapling_file; ngx_str_t stapling_responder; + ngx_str_t psk_path; + ngx_uint_t psk_hash_max_size; + ngx_uint_t psk_hash_bucket_size; + u_char *file; ngx_uint_t line; } ngx_http_ssl_srv_conf_t; ________________________________ CONFIDENTIALITY NOTICE: This email and any attachments are for the sole use of the intended recipient(s) and contain information that may be Garmin confidential and/or Garmin legally privileged. If you have received this email in error, please notify the sender by reply email and delete the message. Any disclosure, copying, distribution or use of this communication (including attachments) by someone other than the intended recipient is prohibited. Thank you. From Nate.Karstens at garmin.com Thu Jun 1 17:21:35 2017 From: Nate.Karstens at garmin.com (Karstens, Nate) Date: Thu, 1 Jun 2017 17:21:35 +0000 Subject: [PATCH 3 of 3] PSK: add PSK identity variable Message-ID: <145451D4E6785E4DA4DFA353A58CB7B2015E15B80E@OLAWPA-EXMB04.ad.garmin.com> # HG changeset patch # User Nate Karstens # Date 1496332963 18000 # Thu Jun 01 11:02:43 2017 -0500 # Node ID cb09937f63834ab74b49a76b9b158dd0a5871309 # Parent 7aa7771191d61ef635478460017446bca1f6db55 PSK: add PSK identity variable Adds the variable $ssl_psk_identity to get the PSK identity used in a connnection secured with a PSK cipher suite. Signed-off-by: Nate Karstens diff -r 7aa7771191d6 -r cb09937f6383 src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c Thu Jun 01 11:01:05 2017 -0500 +++ b/src/event/ngx_event_openssl.c Thu Jun 01 11:02:43 2017 -0500 @@ -4286,6 +4286,33 @@ } +ngx_int_t +ngx_ssl_get_psk_identity(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s) +{ + const char *identity; + size_t len; + + identity = SSL_get_psk_identity(c->ssl->connection); + + if (identity == NULL) { + s->len = 0; + return NGX_OK; + } + + len = ngx_strlen(identity); + + s->data = ngx_pnalloc(pool, len); + if (s->data == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(s->data, identity, len); + s->len = len; + + return NGX_OK; +} + + static time_t ngx_ssl_parse_time( #if OPENSSL_VERSION_NUMBER > 0x10100000L diff -r 7aa7771191d6 -r cb09937f6383 src/event/ngx_event_openssl.h --- a/src/event/ngx_event_openssl.h Thu Jun 01 11:01:05 2017 -0500 +++ b/src/event/ngx_event_openssl.h Thu Jun 01 11:02:43 2017 -0500 @@ -235,6 +235,8 @@ ngx_str_t *s); ngx_int_t ngx_ssl_get_client_v_remain(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s); +ngx_int_t ngx_ssl_get_psk_identity(ngx_connection_t *c, ngx_pool_t *pool, + ngx_str_t *s); ngx_int_t ngx_ssl_handshake(ngx_connection_t *c); diff -r 7aa7771191d6 -r cb09937f6383 src/http/modules/ngx_http_ssl_module.c --- a/src/http/modules/ngx_http_ssl_module.c Thu Jun 01 11:01:05 2017 -0500 +++ b/src/http/modules/ngx_http_ssl_module.c Thu Jun 01 11:02:43 2017 -0500 @@ -357,6 +357,9 @@ { ngx_string("ssl_client_v_remain"), NULL, ngx_http_ssl_variable, (uintptr_t) ngx_ssl_get_client_v_remain, NGX_HTTP_VAR_CHANGEABLE, 0 }, + { ngx_string("ssl_psk_identity"), NULL, ngx_http_ssl_variable, + (uintptr_t) ngx_ssl_get_psk_identity, NGX_HTTP_VAR_CHANGEABLE, 0 }, + { ngx_null_string, NULL, NULL, 0, 0, 0 } }; ________________________________ CONFIDENTIALITY NOTICE: This email and any attachments are for the sole use of the intended recipient(s) and contain information that may be Garmin confidential and/or Garmin legally privileged. If you have received this email in error, please notify the sender by reply email and delete the message. Any disclosure, copying, distribution or use of this communication (including attachments) by someone other than the intended recipient is prohibited. Thank you. From vbart at nginx.com Thu Jun 1 17:22:13 2017 From: vbart at nginx.com (Valentin V. Bartenev) Date: Thu, 01 Jun 2017 20:22:13 +0300 Subject: [PATCH 2 of 4] HTTP/2: send SETTINGS ACK after applying all SETTINGS params In-Reply-To: References: <07adf0a7009c3244de4b.1493074103@piotrsikora.sfo.corp.google.com> Message-ID: <1519381.o8kJVBhGLJ@vbart-workstation> On Monday 24 April 2017 15:48:24 Piotr Sikora via nginx-devel wrote: > # HG changeset patch > # User Piotr Sikora > # Date 1493073310 25200 > # Mon Apr 24 15:35:10 2017 -0700 > # Node ID a8cfd4c454ff5433629bfd16444c6c71ee932fa1 > # Parent 07adf0a7009c3244de4b795c0c06927f4316a87f > HTTP/2: send SETTINGS ACK after applying all SETTINGS params. > > This avoids sending unnecessary SETTINGS ACK in case of PROTOCOL_ERROR. > > Signed-off-by: Piotr Sikora > > diff -r 07adf0a7009c -r a8cfd4c454ff src/http/v2/ngx_http_v2.c > --- a/src/http/v2/ngx_http_v2.c > +++ b/src/http/v2/ngx_http_v2.c > @@ -1972,8 +1972,6 @@ ngx_http_v2_state_settings(ngx_http_v2_c > return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); > } > > - ngx_http_v2_send_settings(h2c, 1); > - > return ngx_http_v2_state_settings_params(h2c, pos, end); > } > > @@ -2037,6 +2035,8 @@ ngx_http_v2_state_settings_params(ngx_ht > pos += NGX_HTTP_V2_SETTINGS_PARAM_SIZE; > } > > + ngx_http_v2_send_settings(h2c, 1); > + > if (adjustment) { > if (ngx_http_v2_adjust_windows(h2c, adjustment) != NGX_OK) { > return ngx_http_v2_connection_error(h2c, Looks good. wbr, Valentin V. Bartenev From mdounin at mdounin.ru Thu Jun 1 17:39:51 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Thu, 1 Jun 2017 20:39:51 +0300 Subject: Use primes for hashtable size In-Reply-To: References: <20170530130137.GT55433@mdounin.ru> Message-ID: <20170601173951.GO55433@mdounin.ru> Hello! On Thu, Jun 01, 2017 at 04:54:50PM +0500, Andrew Borodin wrote: > Hi, Maxim! > > 2017-05-30 18:01 GMT+05:00 Maxim Dounin : > > > > The maximum size of hash table as specified by the hinit->max_size > > field is indeed maximum size, and not the size of the hash table. > > Following code in the ngx_hash_init() will try hard to find to > > find out an optimal hash size for a given set of values within the > > maximum size specified, and will test all the prime numbers as > > well. > > > > I see no reasons to additionally limit the maximum size to a prime > > number. If you think there are some, please be more specific. > > > > You are right. I've modified patch to checkout primes first, then proceed > to "hard work" . Also I've kolhozed some perf prove of improvement. > This test creates a hash table of 5000 semirandom strings (not very random, > just bytes permutated). > On my Ubuntu VM without patch hash creation is 92-96ms, with patch it's > strictly 0. "hard work" search tries about 2k sizes before success, primes > search hits at second. > > Docs say some words about startup speed and I wanted to apply primes > somewhere, so here we go. Thanks, though suggested change will certainly modify current nginx (documented) approach of searching for minimum possible hash sizes. It might be a better solution for large hashes though, as currently optimized by using a larger start size: if (hinit->max_size > 10000 && nelts && hinit->max_size / nelts < 100) { start = hinit->max_size - 1000; } Not sure it is at all needed though, as I don't remember any "words about startup speed" in the documentation and startup speed of hashes wasn't a practical problem as far as I remember. -- Maxim Dounin http://nginx.org/ From vbart at nginx.com Thu Jun 1 17:51:59 2017 From: vbart at nginx.com (Valentin V. Bartenev) Date: Thu, 01 Jun 2017 20:51:59 +0300 Subject: [PATCH 3 of 4] HTTP/2: make SETTINGS ACK frame reusable In-Reply-To: References: <07adf0a7009c3244de4b.1493074103@piotrsikora.sfo.corp.google.com> Message-ID: <22008327.Bf6S9Lx5iZ@vbart-workstation> On Monday 24 April 2017 15:48:25 Piotr Sikora via nginx-devel wrote: > # HG changeset patch > # User Piotr Sikora > # Date 1493073310 25200 > # Mon Apr 24 15:35:10 2017 -0700 > # Node ID b8d7f4a4d5abb4a27a772910358e263d49c618ef > # Parent a8cfd4c454ff5433629bfd16444c6c71ee932fa1 > HTTP/2: make SETTINGS ACK frame reusable. > > Signed-off-by: Piotr Sikora > [..] > @@ -2495,8 +2503,8 @@ ngx_http_v2_send_settings(ngx_http_v2_co > ngx_http_v2_srv_conf_t *h2scf; > ngx_http_v2_out_frame_t *frame; > > - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, > - "http2 send SETTINGS frame ack:%ui", ack); > + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, > + "http2 send SETTINGS frame params:3"); > [..] I'm pretty sure that this number will be forgotten to change after adding new parameters. Since in another patch you are suggesting to add debug messages to print each parameter then they will be countable without a problem. Let it be just: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, "http2 send SETTINGS frame"); wbr, Valentin V. Bartenev From vbart at nginx.com Thu Jun 1 17:52:38 2017 From: vbart at nginx.com (Valentin V. Bartenev) Date: Thu, 01 Jun 2017 20:52:38 +0300 Subject: [PATCH 4 of 4] HTTP/2: don't send SETTINGS ACK before already queued DATA frames In-Reply-To: <3624fa075acac110a08c.1493074106@piotrsikora.sfo.corp.google.com> References: <07adf0a7009c3244de4b.1493074103@piotrsikora.sfo.corp.google.com> <3624fa075acac110a08c.1493074106@piotrsikora.sfo.corp.google.com> Message-ID: <3026243.Ae8Ev985dJ@vbart-workstation> On Monday 24 April 2017 15:48:26 Piotr Sikora via nginx-devel wrote: > # HG changeset patch > # User Piotr Sikora > # Date 1493073310 25200 > # Mon Apr 24 15:35:10 2017 -0700 > # Node ID 3624fa075acac110a08c0f1c928c545a58c5801f > # Parent b8d7f4a4d5abb4a27a772910358e263d49c618ef > HTTP/2: don't send SETTINGS ACK before already queued DATA frames. > > Previously, SETTINGS ACK was sent immediately upon receipt of SETTINGS > frame, before already queued DATA frames created using old SETTINGS. > > This incorrect behavior was source of interoperability issues, because > peers rely on the fact that new SETTINGS are in effect after receiving > SETTINGS ACK. > > Reported by Feng Li. > > Signed-off-by: Piotr Sikora > > diff -r b8d7f4a4d5ab -r 3624fa075aca src/http/v2/ngx_http_v2.c > --- a/src/http/v2/ngx_http_v2.c > +++ b/src/http/v2/ngx_http_v2.c > @@ -2043,7 +2043,7 @@ ngx_http_v2_state_settings_params(ngx_ht > return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR); > } > > - ngx_http_v2_queue_blocked_frame(h2c, frame); > + ngx_http_v2_queue_ordered_frame(h2c, frame); > > if (adjustment) { > if (ngx_http_v2_adjust_windows(h2c, adjustment) != NGX_OK) { > diff -r b8d7f4a4d5ab -r 3624fa075aca src/http/v2/ngx_http_v2.h > --- a/src/http/v2/ngx_http_v2.h > +++ b/src/http/v2/ngx_http_v2.h > @@ -261,6 +261,15 @@ ngx_http_v2_queue_blocked_frame(ngx_http > } > > > +static ngx_inline void > +ngx_http_v2_queue_ordered_frame(ngx_http_v2_connection_t *h2c, > + ngx_http_v2_out_frame_t *frame) > +{ > + frame->next = h2c->last_out; > + h2c->last_out = frame; > +} > + > + > void ngx_http_v2_init(ngx_event_t *rev); > void ngx_http_v2_request_headers_init(void); > Looks good. wbr, Valentin V. Bartenev From vbart at nginx.com Thu Jun 1 17:54:54 2017 From: vbart at nginx.com (Valentin V. Bartenev) Date: Thu, 01 Jun 2017 20:54:54 +0300 Subject: [PATCH 1 of 4] HTTP/2: emit new frames only after applying all SETTINGS params In-Reply-To: <8753794.qGPzJ25FTB@vbart-workstation> References: <07adf0a7009c3244de4b.1493074103@piotrsikora.sfo.corp.google.com> <8753794.qGPzJ25FTB@vbart-workstation> Message-ID: <2363492.pM8K820MxA@vbart-workstation> On Thursday 01 June 2017 17:48:25 Valentin V. Bartenev wrote: > On Monday 24 April 2017 15:48:23 Piotr Sikora via nginx-devel wrote: > > # HG changeset patch > > # User Piotr Sikora > > # Date 1493073310 25200 > > # Mon Apr 24 15:35:10 2017 -0700 > > # Node ID 07adf0a7009c3244de4b795c0c06927f4316a87f > > # Parent 2c4dbcd6f2e4c9c2a1eb8dc1f0d39c99975ae208 > > HTTP/2: emit new frames only after applying all SETTINGS params. > > > > Previously, new frames could be emitted in the middle of applying > > new (and already acknowledged) SETTINGS params, which is illegal. > > > > Signed-off-by: Piotr Sikora > > > > diff -r 2c4dbcd6f2e4 -r 07adf0a7009c src/http/v2/ngx_http_v2.c > > --- a/src/http/v2/ngx_http_v2.c > > +++ b/src/http/v2/ngx_http_v2.c > > @@ -1982,7 +1982,9 @@ static u_char * > > ngx_http_v2_state_settings_params(ngx_http_v2_connection_t *h2c, u_char *pos, > > u_char *end) > > { > > - ngx_uint_t id, value; > > + ngx_uint_t id, value, adjustment; > > The new initial window size can be lower than the previous one, > so the difference can be negative (that's why the delta parameter > of ngx_http_v2_adjust_windows() is ssize_t). > > Please consider the patch below: > > diff -r 00015416ae79 src/http/v2/ngx_http_v2.c > --- a/src/http/v2/ngx_http_v2.c Mon Apr 24 15:35:10 2017 -0700 > +++ b/src/http/v2/ngx_http_v2.c Thu Jun 01 17:45:37 2017 +0300 > @@ -1969,7 +1969,8 @@ static u_char * > ngx_http_v2_state_settings_params(ngx_http_v2_connection_t *h2c, u_char *pos, > u_char *end) > { > - ngx_uint_t id, value, adjustment; > + ssize_t window_delta; > + ngx_uint_t id, value; > > adjustment = 0; > [..] Err.. of course here should be "window_delta = 0;". wbr, Valentin V. Bartenev From borodin at octonica.com Thu Jun 1 17:57:09 2017 From: borodin at octonica.com (Andrew Borodin) Date: Thu, 1 Jun 2017 22:57:09 +0500 Subject: Use primes for hashtable size In-Reply-To: <20170601173951.GO55433@mdounin.ru> References: <20170530130137.GT55433@mdounin.ru> <20170601173951.GO55433@mdounin.ru> Message-ID: 2017-06-01 22:39 GMT+05:00 Maxim Dounin : > Thanks, though suggested change will certainly modify current > nginx (documented) approach of searching for minimum possible > hash sizes. > > It might be a better solution for large hashes though, as > currently optimized by using a larger start size: > > if (hinit->max_size > 10000 && nelts && hinit->max_size / nelts < 100) { > start = hinit->max_size - 1000; > } Yeah, maybe put the primes loop under if. > Not sure it is at all needed though, as I don't remember any > "words about startup speed" in the documentation and startup > speed of hashes wasn't a practical problem as far as I remember. Here https://nginx.ru/en/docs/http/server_names.html "if nginx?s start time is unacceptably long, try to increase server_names_hash_bucket_size" It's about allowing more hash collisions. But I do not insist, though :) I've done the patch jut for fun Best regards, Andrey Borodin, Octonica. From piotrsikora at google.com Thu Jun 1 21:51:58 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Thu, 01 Jun 2017 14:51:58 -0700 Subject: [PATCH 1 of 4] HTTP/2: emit new frames only after applying all SETTINGS params In-Reply-To: <07adf0a7009c3244de4b.1493074103@piotrsikora.sfo.corp.google.com> References: <07adf0a7009c3244de4b.1493074103@piotrsikora.sfo.corp.google.com> Message-ID: <1738ed9658e2a9a12370.1496353918@piotrsikora.sfo.corp.google.com> # HG changeset patch # User Piotr Sikora # Date 1493067124 25200 # Mon Apr 24 13:52:04 2017 -0700 # Node ID 1738ed9658e2a9a12370f4c828761a9fd058935d # Parent ab6ef3037840393752d82fac01ea1eb4f972301c HTTP/2: emit new frames only after applying all SETTINGS params. Previously, new frames could be emitted in the middle of applying new (and already acknowledged) SETTINGS params, which is illegal. Signed-off-by: Piotr Sikora diff -r ab6ef3037840 -r 1738ed9658e2 src/http/v2/ngx_http_v2.c --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -1969,8 +1969,11 @@ static u_char * ngx_http_v2_state_settings_params(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end) { + ssize_t window_delta; ngx_uint_t id, value; + window_delta = 0; + while (h2c->state.length) { if (end - pos < NGX_HTTP_V2_SETTINGS_PARAM_SIZE) { return ngx_http_v2_state_save(h2c, pos, end, @@ -1995,12 +1998,7 @@ ngx_http_v2_state_settings_params(ngx_ht NGX_HTTP_V2_FLOW_CTRL_ERROR); } - if (ngx_http_v2_adjust_windows(h2c, value - h2c->init_window) - != NGX_OK) - { - return ngx_http_v2_connection_error(h2c, - NGX_HTTP_V2_INTERNAL_ERROR); - } + window_delta = value - h2c->init_window; h2c->init_window = value; break; @@ -2028,6 +2026,13 @@ ngx_http_v2_state_settings_params(ngx_ht pos += NGX_HTTP_V2_SETTINGS_PARAM_SIZE; } + if (window_delta) { + if (ngx_http_v2_adjust_windows(h2c, window_delta) != NGX_OK) { + return ngx_http_v2_connection_error(h2c, + NGX_HTTP_V2_INTERNAL_ERROR); + } + } + return ngx_http_v2_state_complete(h2c, pos, end); } From piotrsikora at google.com Thu Jun 1 21:51:59 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Thu, 01 Jun 2017 14:51:59 -0700 Subject: [PATCH 2 of 4] HTTP/2: send SETTINGS ACK after applying all SETTINGS params In-Reply-To: <07adf0a7009c3244de4b.1493074103@piotrsikora.sfo.corp.google.com> References: <07adf0a7009c3244de4b.1493074103@piotrsikora.sfo.corp.google.com> Message-ID: # HG changeset patch # User Piotr Sikora # Date 1493067147 25200 # Mon Apr 24 13:52:27 2017 -0700 # Node ID d61e944f55e70a5a25c8a79bfc5c167b7f22d62e # Parent 1738ed9658e2a9a12370f4c828761a9fd058935d HTTP/2: send SETTINGS ACK after applying all SETTINGS params. This avoids sending unnecessary SETTINGS ACK in case of PROTOCOL_ERROR. Signed-off-by: Piotr Sikora diff -r 1738ed9658e2 -r d61e944f55e7 src/http/v2/ngx_http_v2.c --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -1959,8 +1959,6 @@ ngx_http_v2_state_settings(ngx_http_v2_c return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); } - ngx_http_v2_send_settings(h2c, 1); - return ngx_http_v2_state_settings_params(h2c, pos, end); } @@ -2026,6 +2024,8 @@ ngx_http_v2_state_settings_params(ngx_ht pos += NGX_HTTP_V2_SETTINGS_PARAM_SIZE; } + ngx_http_v2_send_settings(h2c, 1); + if (window_delta) { if (ngx_http_v2_adjust_windows(h2c, window_delta) != NGX_OK) { return ngx_http_v2_connection_error(h2c, From piotrsikora at google.com Thu Jun 1 21:52:00 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Thu, 01 Jun 2017 14:52:00 -0700 Subject: [PATCH 3 of 4] HTTP/2: make SETTINGS ACK frame reusable In-Reply-To: <07adf0a7009c3244de4b.1493074103@piotrsikora.sfo.corp.google.com> References: <07adf0a7009c3244de4b.1493074103@piotrsikora.sfo.corp.google.com> Message-ID: # HG changeset patch # User Piotr Sikora # Date 1493067017 25200 # Mon Apr 24 13:50:17 2017 -0700 # Node ID e00ba13ce421685981db6a98831409a234cc1e62 # Parent d61e944f55e70a5a25c8a79bfc5c167b7f22d62e HTTP/2: make SETTINGS ACK frame reusable. Signed-off-by: Piotr Sikora diff -r d61e944f55e7 -r e00ba13ce421 src/http/v2/ngx_http_v2.c --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -28,6 +28,7 @@ #define NGX_HTTP_V2_HTTP_1_1_REQUIRED 0xd /* frame sizes */ +#define NGX_HTTP_V2_SETTINGS_ACK_SIZE 0 #define NGX_HTTP_V2_RST_STREAM_SIZE 4 #define NGX_HTTP_V2_PRIORITY_SIZE 5 #define NGX_HTTP_V2_PING_SIZE 8 @@ -128,8 +129,7 @@ static ngx_http_v2_node_t *ngx_http_v2_g #define ngx_http_v2_index_size(h2scf) (h2scf->streams_index_mask + 1) #define ngx_http_v2_index(h2scf, sid) ((sid >> 1) & h2scf->streams_index_mask) -static ngx_int_t ngx_http_v2_send_settings(ngx_http_v2_connection_t *h2c, - ngx_uint_t ack); +static ngx_int_t ngx_http_v2_send_settings(ngx_http_v2_connection_t *h2c); static ngx_int_t ngx_http_v2_settings_frame_handler( ngx_http_v2_connection_t *h2c, ngx_http_v2_out_frame_t *frame); static ngx_int_t ngx_http_v2_send_window_update(ngx_http_v2_connection_t *h2c, @@ -269,7 +269,7 @@ ngx_http_v2_init(ngx_event_t *rev) return; } - if (ngx_http_v2_send_settings(h2c, 0) == NGX_ERROR) { + if (ngx_http_v2_send_settings(h2c) == NGX_ERROR) { ngx_http_close_connection(c); return; } @@ -1967,8 +1967,9 @@ static u_char * ngx_http_v2_state_settings_params(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end) { - ssize_t window_delta; - ngx_uint_t id, value; + ssize_t window_delta; + ngx_uint_t id, value; + ngx_http_v2_out_frame_t *frame; window_delta = 0; @@ -2024,7 +2025,14 @@ ngx_http_v2_state_settings_params(ngx_ht pos += NGX_HTTP_V2_SETTINGS_PARAM_SIZE; } - ngx_http_v2_send_settings(h2c, 1); + frame = ngx_http_v2_get_frame(h2c, NGX_HTTP_V2_SETTINGS_ACK_SIZE, + NGX_HTTP_V2_SETTINGS_FRAME, + NGX_HTTP_V2_ACK_FLAG, 0); + if (frame == NULL) { + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR); + } + + ngx_http_v2_queue_blocked_frame(h2c, frame); if (window_delta) { if (ngx_http_v2_adjust_windows(h2c, window_delta) != NGX_OK) { @@ -2476,7 +2484,7 @@ ngx_http_v2_parse_int(ngx_http_v2_connec static ngx_int_t -ngx_http_v2_send_settings(ngx_http_v2_connection_t *h2c, ngx_uint_t ack) +ngx_http_v2_send_settings(ngx_http_v2_connection_t *h2c) { size_t len; ngx_buf_t *buf; @@ -2484,8 +2492,8 @@ ngx_http_v2_send_settings(ngx_http_v2_co ngx_http_v2_srv_conf_t *h2scf; ngx_http_v2_out_frame_t *frame; - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, - "http2 send SETTINGS frame ack:%ui", ack); + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 send SETTINGS frame"); frame = ngx_palloc(h2c->pool, sizeof(ngx_http_v2_out_frame_t)); if (frame == NULL) { @@ -2497,7 +2505,7 @@ ngx_http_v2_send_settings(ngx_http_v2_co return NGX_ERROR; } - len = ack ? 0 : (sizeof(uint16_t) + sizeof(uint32_t)) * 3; + len = NGX_HTTP_V2_SETTINGS_PARAM_SIZE * 3; buf = ngx_create_temp_buf(h2c->pool, NGX_HTTP_V2_FRAME_HEADER_SIZE + len); if (buf == NULL) { @@ -2521,28 +2529,26 @@ ngx_http_v2_send_settings(ngx_http_v2_co buf->last = ngx_http_v2_write_len_and_type(buf->last, len, NGX_HTTP_V2_SETTINGS_FRAME); - *buf->last++ = ack ? NGX_HTTP_V2_ACK_FLAG : NGX_HTTP_V2_NO_FLAG; + *buf->last++ = NGX_HTTP_V2_NO_FLAG; buf->last = ngx_http_v2_write_sid(buf->last, 0); - if (!ack) { - h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx, - ngx_http_v2_module); - - buf->last = ngx_http_v2_write_uint16(buf->last, - NGX_HTTP_V2_MAX_STREAMS_SETTING); - buf->last = ngx_http_v2_write_uint32(buf->last, - h2scf->concurrent_streams); - - buf->last = ngx_http_v2_write_uint16(buf->last, + h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx, + ngx_http_v2_module); + + buf->last = ngx_http_v2_write_uint16(buf->last, + NGX_HTTP_V2_MAX_STREAMS_SETTING); + buf->last = ngx_http_v2_write_uint32(buf->last, + h2scf->concurrent_streams); + + buf->last = ngx_http_v2_write_uint16(buf->last, NGX_HTTP_V2_INIT_WINDOW_SIZE_SETTING); - buf->last = ngx_http_v2_write_uint32(buf->last, h2scf->preread_size); - - buf->last = ngx_http_v2_write_uint16(buf->last, - NGX_HTTP_V2_MAX_FRAME_SIZE_SETTING); - buf->last = ngx_http_v2_write_uint32(buf->last, - NGX_HTTP_V2_MAX_FRAME_SIZE); - } + buf->last = ngx_http_v2_write_uint32(buf->last, h2scf->preread_size); + + buf->last = ngx_http_v2_write_uint16(buf->last, + NGX_HTTP_V2_MAX_FRAME_SIZE_SETTING); + buf->last = ngx_http_v2_write_uint32(buf->last, + NGX_HTTP_V2_MAX_FRAME_SIZE); ngx_http_v2_queue_blocked_frame(h2c, frame); From piotrsikora at google.com Thu Jun 1 21:52:01 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Thu, 01 Jun 2017 14:52:01 -0700 Subject: [PATCH 4 of 4] HTTP/2: don't send SETTINGS ACK before already queued DATA frames In-Reply-To: <07adf0a7009c3244de4b.1493074103@piotrsikora.sfo.corp.google.com> References: <07adf0a7009c3244de4b.1493074103@piotrsikora.sfo.corp.google.com> Message-ID: <26c9e95a73295a344d39.1496353921@piotrsikora.sfo.corp.google.com> # HG changeset patch # User Piotr Sikora # Date 1493067070 25200 # Mon Apr 24 13:51:10 2017 -0700 # Node ID 26c9e95a73295a344d39ac5e6d62787d26989c82 # Parent e00ba13ce421685981db6a98831409a234cc1e62 HTTP/2: don't send SETTINGS ACK before already queued DATA frames. Previously, SETTINGS ACK was sent immediately upon receipt of SETTINGS frame, before already queued DATA frames created using old SETTINGS. This incorrect behavior was source of interoperability issues, because peers rely on the fact that new SETTINGS are in effect after receiving SETTINGS ACK. Reported by Feng Li. Signed-off-by: Piotr Sikora diff -r e00ba13ce421 -r 26c9e95a7329 src/http/v2/ngx_http_v2.c --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -2032,7 +2032,7 @@ ngx_http_v2_state_settings_params(ngx_ht return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR); } - ngx_http_v2_queue_blocked_frame(h2c, frame); + ngx_http_v2_queue_ordered_frame(h2c, frame); if (window_delta) { if (ngx_http_v2_adjust_windows(h2c, window_delta) != NGX_OK) { diff -r e00ba13ce421 -r 26c9e95a7329 src/http/v2/ngx_http_v2.h --- a/src/http/v2/ngx_http_v2.h +++ b/src/http/v2/ngx_http_v2.h @@ -261,6 +261,15 @@ ngx_http_v2_queue_blocked_frame(ngx_http } +static ngx_inline void +ngx_http_v2_queue_ordered_frame(ngx_http_v2_connection_t *h2c, + ngx_http_v2_out_frame_t *frame) +{ + frame->next = h2c->last_out; + h2c->last_out = frame; +} + + void ngx_http_v2_init(ngx_event_t *rev); void ngx_http_v2_request_headers_init(void); From piotrsikora at google.com Thu Jun 1 21:55:06 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Thu, 1 Jun 2017 14:55:06 -0700 Subject: [PATCH 1 of 4] HTTP/2: emit new frames only after applying all SETTINGS params In-Reply-To: <8753794.qGPzJ25FTB@vbart-workstation> References: <07adf0a7009c3244de4b.1493074103@piotrsikora.sfo.corp.google.com> <8753794.qGPzJ25FTB@vbart-workstation> Message-ID: Hey Valentin, > The new initial window size can be lower than the previous one, > so the difference can be negative (that's why the delta parameter > of ngx_http_v2_adjust_windows() is ssize_t). Oops, good catch, thanks! Funnily enough, the original patch worked just fine (at least on systems where size of ssize_t was equal to size of ngx_uint_t), since the value would overflow and result in the same negative number when casted to ssize_t. I updated patchset with suggested changes, please take a look. Best regards, Piotr Sikora From mat999 at gmail.com Fri Jun 2 00:56:31 2017 From: mat999 at gmail.com (Mathew Heard) Date: Fri, 2 Jun 2017 10:56:31 +1000 Subject: Use primes for hashtable size In-Reply-To: References: <20170530130137.GT55433@mdounin.ru> <20170601173951.GO55433@mdounin.ru> Message-ID: If this actually yields a decrease in start time while not introducing other effects we would use it. Our start time of a couple minutes is annoying at times. On Fri, Jun 2, 2017 at 3:57 AM, Andrew Borodin wrote: > 2017-06-01 22:39 GMT+05:00 Maxim Dounin : > > Thanks, though suggested change will certainly modify current > > nginx (documented) approach of searching for minimum possible > > hash sizes. > > > > It might be a better solution for large hashes though, as > > currently optimized by using a larger start size: > > > > if (hinit->max_size > 10000 && nelts && hinit->max_size / nelts < > 100) { > > start = hinit->max_size - 1000; > > } > > Yeah, maybe put the primes loop under if. > > > Not sure it is at all needed though, as I don't remember any > > "words about startup speed" in the documentation and startup > > speed of hashes wasn't a practical problem as far as I remember. > > > Here https://nginx.ru/en/docs/http/server_names.html > "if nginx?s start time is unacceptably long, try to increase > server_names_hash_bucket_size" It's about allowing more hash > collisions. > But I do not insist, though :) I've done the patch jut for fun > > Best regards, Andrey Borodin, Octonica. > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel > -------------- next part -------------- An HTML attachment was scrubbed... URL: From tommy.lindgren at gmail.com Fri Jun 2 03:43:38 2017 From: tommy.lindgren at gmail.com (Tommy Lindgren) Date: Fri, 02 Jun 2017 11:43:38 +0800 Subject: [PATCH] Dav: dav_request_buffering directive Message-ID: <149637501849.29398.12944513697412060423@argon> Hi, Currently the webdav module writes the request body to a temporary file and moves it to destination filepath once upload is complete. The patch below introduces dav_request_buffering. Default is "on". If "off", the request body will be written directly to destination filepath (unless destination file already exists, in which case 409 is returned). Rationale: We work with a system where http clients can upload large files (>100 GB, think raw uncompressed HD footage). We want to use nginx + the dav module for this. However, other clients want to access the uploaded files as soon as possible (even if the upload is still running). Thus we implemented "dav_request_buffering off". To make sure a client can't overwrite arbitrary files we protect the location with the secure_link module, i.e. the system has previously handed out a secure upload url (with unique filename to avoid collisions) to the client. Implementation details: The name "dav_request_buffering" is supposed to be analogous with "proxy_request_buffering" which hopefully make sense even though the implementation is very different. We extend ngx_http_request_s with request_body_temp_name, which the dav module sets when dav_request_buffering is off, and then ngx_create_temp_file writes directly to this filepath. request_body_in_clean_file is not set in this case. In other words, we instruct nginx core to write the "temporary" file to the final destination directly. Any chance seeing this patch/feature merged? Thanks, Tommy # HG changeset patch # User Tommy Lindgren # Date 1496048257 -7200 # Mon May 29 10:57:37 2017 +0200 # Node ID 4f4a483ca56229b7690f5d305bf056027005c7f2 # Parent ed1101bbf19f1edf300665b6398cd66d45b71577 Dav: introduced dav_request_buffering directive By default the dav module writes the request body to a temporary file and moves it to destination filepath when upload is complete. If dav_request_buffering is "off", the request body will be written directly to destination filepath (unless destination file already exists, in which case 409 is returned). diff --git a/src/core/ngx_file.c b/src/core/ngx_file.c --- a/src/core/ngx_file.c +++ b/src/core/ngx_file.c @@ -150,6 +150,22 @@ ngx_create_temp_file(ngx_file_t *file, n ngx_pool_cleanup_t *cln; ngx_pool_cleanup_file_t *clnf; + if (file->name.data != NULL) { + file->fd = ngx_open_tempfile(file->name.data, 1, access); + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, file->log, 0, + "forced temp fd:%d", file->fd); + + if (file->fd == NGX_INVALID_FILE) { + ngx_log_error(NGX_LOG_ERR, file->log, ngx_errno, + ngx_open_tempfile_n " \"%s\" failed", + file->name.data); + return NGX_ERROR; + } + + return NGX_OK; + } + if (file->name.len) { name = file->name; levels = 0; diff --git a/src/http/modules/ngx_http_dav_module.c b/src/http/modules/ngx_http_dav_module.c --- a/src/http/modules/ngx_http_dav_module.c +++ b/src/http/modules/ngx_http_dav_module.c @@ -23,6 +23,7 @@ typedef struct { ngx_uint_t access; ngx_uint_t min_delete_depth; ngx_flag_t create_full_put_path; + ngx_flag_t dav_request_buffering; } ngx_http_dav_loc_conf_t; @@ -104,6 +105,13 @@ static ngx_command_t ngx_http_dav_comma offsetof(ngx_http_dav_loc_conf_t, access), NULL }, + { ngx_string("dav_request_buffering"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_dav_loc_conf_t, dav_request_buffering), + NULL }, + ngx_null_command }; @@ -142,8 +150,12 @@ ngx_module_t ngx_http_dav_module = { static ngx_int_t ngx_http_dav_handler(ngx_http_request_t *r) { + size_t root; + ngx_str_t path; ngx_int_t rc; ngx_http_dav_loc_conf_t *dlcf; + ngx_file_info_t fi; + ngx_err_t err; dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module); @@ -169,10 +181,39 @@ ngx_http_dav_handler(ngx_http_request_t r->request_body_in_file_only = 1; r->request_body_in_persistent_file = 1; - r->request_body_in_clean_file = 1; r->request_body_file_group_access = 1; r->request_body_file_log_level = 0; + if (dlcf->dav_request_buffering) { + r->request_body_in_clean_file = 1; + } else { + if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + if (ngx_file_info(path.data, &fi) != NGX_FILE_ERROR) { + return NGX_HTTP_CONFLICT; + } + + if (dlcf->create_full_put_path) { + err = ngx_create_full_path(path.data, ngx_dir_access(dlcf->access)); + if (err) { + ngx_log_error(NGX_LOG_CRIT, r->connection->log, err, + ngx_create_dir_n " \"%s\" failed", path.data); + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + } + + r->request_body_temp_name.len = path.len; + r->request_body_temp_name.data = ngx_pnalloc(r->pool, path.len + 1); + + if (r->request_body_temp_name.data == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + ngx_memcpy(r->request_body_temp_name.data, path.data, path.len); + } + rc = ngx_http_read_client_request_body(r, ngx_http_dav_put_handler); if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { @@ -213,6 +254,16 @@ ngx_http_dav_put_handler(ngx_http_reques ngx_ext_rename_file_t ext; ngx_http_dav_loc_conf_t *dlcf; + dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module); + + if (!dlcf->dav_request_buffering) { + r->headers_out.content_length_n = 0; + r->headers_out.status = NGX_HTTP_CREATED; + r->header_only = 1; + ngx_http_finalize_request(r, ngx_http_send_header(r)); + return; + } + if (r->request_body == NULL || r->request_body->temp_file == NULL) { ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return; @@ -251,8 +302,6 @@ ngx_http_dav_put_handler(ngx_http_reques } } - dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module); - ext.access = dlcf->access; ext.path_access = dlcf->access; ext.time = -1; @@ -1115,6 +1164,7 @@ ngx_http_dav_create_loc_conf(ngx_conf_t conf->min_delete_depth = NGX_CONF_UNSET_UINT; conf->access = NGX_CONF_UNSET_UINT; conf->create_full_put_path = NGX_CONF_UNSET; + conf->dav_request_buffering = NGX_CONF_UNSET; return conf; } @@ -1137,6 +1187,9 @@ ngx_http_dav_merge_loc_conf(ngx_conf_t * ngx_conf_merge_value(conf->create_full_put_path, prev->create_full_put_path, 0); + ngx_conf_merge_value(conf->dav_request_buffering, + prev->dav_request_buffering, 1); + return NGX_CONF_OK; } diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h --- a/src/http/ngx_http_request.h +++ b/src/http/ngx_http_request.h @@ -481,6 +481,7 @@ struct ngx_http_request_s { unsigned request_body_file_group_access:1; unsigned request_body_file_log_level:3; unsigned request_body_no_buffering:1; + ngx_str_t request_body_temp_name; unsigned subrequest_in_memory:1; unsigned waited:1; diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c --- a/src/http/ngx_http_request_body.c +++ b/src/http/ngx_http_request_body.c @@ -450,6 +450,7 @@ ngx_http_write_request_body(ngx_http_req tf->file.fd = NGX_INVALID_FILE; tf->file.log = r->connection->log; tf->path = clcf->client_body_temp_path; + tf->file.name = r->request_body_temp_name; tf->pool = r->pool; tf->warn = "a client request body is buffered to a temporary file"; tf->log_level = r->request_body_file_log_level; From piotrsikora at google.com Fri Jun 2 09:04:08 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Fri, 02 Jun 2017 02:04:08 -0700 Subject: [PATCH 3 of 3] Headers filter: added "add_trailer" directive In-Reply-To: References: Message-ID: <52201fff87e8c5a09628.1496394248@piotrsikora.sfo.corp.google.com> # HG changeset patch # User Piotr Sikora # Date 1490351854 25200 # Fri Mar 24 03:37:34 2017 -0700 # Node ID 52201fff87e8c5a096287cf206cdcc14c0d81ce7 # Parent e84aa49c5bc7a3250d4844b581e4bf3ed42db5f5 Headers filter: added "add_trailer" directive. Trailers added using this directive are evaluated after response body is processed by output filters (but before it's written to the wire), so it's possible to use variables calculated from the response body as the trailer value. Signed-off-by: Piotr Sikora diff -r e84aa49c5bc7 -r 52201fff87e8 src/http/modules/ngx_http_headers_filter_module.c --- a/src/http/modules/ngx_http_headers_filter_module.c +++ b/src/http/modules/ngx_http_headers_filter_module.c @@ -48,6 +48,7 @@ typedef struct { time_t expires_time; ngx_http_complex_value_t *expires_value; ngx_array_t *headers; + ngx_array_t *trailers; } ngx_http_headers_conf_t; @@ -72,6 +73,8 @@ static char *ngx_http_headers_expires(ng void *conf); static char *ngx_http_headers_add(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_http_headers_add_trailer(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); static ngx_http_set_header_t ngx_http_set_headers[] = { @@ -108,6 +111,14 @@ static ngx_command_t ngx_http_headers_f 0, NULL }, + { ngx_string("add_trailer"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF + |NGX_CONF_TAKE23, + ngx_http_headers_add_trailer, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + ngx_null_command }; @@ -144,20 +155,30 @@ ngx_module_t ngx_http_headers_filter_mo static ngx_http_output_header_filter_pt ngx_http_next_header_filter; +static ngx_http_output_body_filter_pt ngx_http_next_body_filter; static ngx_int_t ngx_http_headers_filter(ngx_http_request_t *r) { - ngx_str_t value; - ngx_uint_t i, safe_status; - ngx_http_header_val_t *h; - ngx_http_headers_conf_t *conf; + u_char *p, *data; + size_t len; + ngx_str_t value; + ngx_uint_t i, safe_status; + ngx_table_elt_t *t; + ngx_http_header_val_t *h; + ngx_http_headers_conf_t *conf; + ngx_http_core_loc_conf_t *clcf; + + if (r != r->main) { + return ngx_http_next_header_filter(r); + } conf = ngx_http_get_module_loc_conf(r, ngx_http_headers_filter_module); - if ((conf->expires == NGX_HTTP_EXPIRES_OFF && conf->headers == NULL) - || r != r->main) + if (conf->expires == NGX_HTTP_EXPIRES_OFF + && conf->headers == NULL + && conf->trailers == NULL) { return ngx_http_next_header_filter(r); } @@ -206,11 +227,162 @@ ngx_http_headers_filter(ngx_http_request } } + if (conf->trailers) { + + if (r->header_only + || r->headers_out.status == NGX_HTTP_NOT_MODIFIED + || r->headers_out.status == NGX_HTTP_NO_CONTENT + || r->headers_out.status < NGX_HTTP_OK + || r->method == NGX_HTTP_HEAD) + { + return ngx_http_next_header_filter(r); + } + + if (r->http_version < NGX_HTTP_VERSION_20) { + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (!clcf->chunked_transfer_encoding) { + return ngx_http_next_header_filter(r); + } + } + + len = 0; + + h = conf->trailers->elts; + for (i = 0; i < conf->trailers->nelts; i++) { + + if (!safe_status && !h[i].always) { + continue; + } + + if (h[i].value.value.len) { + len += h[i].key.len + sizeof(", ") - 1; + } + } + + if (len == 0) { + return ngx_http_next_header_filter(r); + } + + len -= sizeof(", ") - 1; + + data = ngx_pnalloc(r->pool, len); + if (data == NULL) { + return NGX_ERROR; + } + + t = ngx_list_push(&r->headers_out.headers); + if (t == NULL) { + return NGX_ERROR; + } + + p = data; + + h = conf->trailers->elts; + for (i = 0; i < conf->trailers->nelts; i++) { + + if (!safe_status && !h[i].always) { + continue; + } + + if (h[i].value.value.len) { + p = ngx_copy(p, h[i].key.data, h[i].key.len); + + if (p == data + len) { + break; + } + + *p++ = ','; *p++ = ' '; + } + } + + ngx_str_set(&t->key, "Trailer"); + t->value.data = data; + t->value.len = len; + t->hash = 1; + + r->expect_trailers = 1; + } + return ngx_http_next_header_filter(r); } static ngx_int_t +ngx_http_trailers_filter(ngx_http_request_t *r, ngx_chain_t *in) +{ + ngx_str_t value; + ngx_uint_t i, safe_status; + ngx_chain_t *cl; + ngx_table_elt_t *t; + ngx_http_header_val_t *h; + ngx_http_headers_conf_t *conf; + + conf = ngx_http_get_module_loc_conf(r, ngx_http_headers_filter_module); + + if (in == NULL + || conf->trailers == NULL + || !r->expect_trailers + || r->header_only) + { + return ngx_http_next_body_filter(r, in); + } + + for (cl = in; cl; cl = cl->next) { + if (cl->buf->last_buf) { + break; + } + } + + if (cl == NULL) { + return ngx_http_next_body_filter(r, in); + } + + switch (r->headers_out.status) { + + case NGX_HTTP_OK: + case NGX_HTTP_CREATED: + case NGX_HTTP_PARTIAL_CONTENT: + case NGX_HTTP_MOVED_PERMANENTLY: + case NGX_HTTP_MOVED_TEMPORARILY: + case NGX_HTTP_SEE_OTHER: + case NGX_HTTP_TEMPORARY_REDIRECT: + safe_status = 1; + break; + + default: + safe_status = 0; + break; + } + + h = conf->trailers->elts; + for (i = 0; i < conf->trailers->nelts; i++) { + + if (!safe_status && !h[i].always) { + continue; + } + + if (ngx_http_complex_value(r, &h[i].value, &value) != NGX_OK) { + return NGX_ERROR; + } + + if (value.len) { + t = ngx_list_push(&r->headers_out.trailers); + if (t == NULL) { + return NGX_ERROR; + } + + t->key = h[i].key; + t->value = value; + t->hash = 1; + } + } + + return ngx_http_next_body_filter(r, in); +} + + +static ngx_int_t ngx_http_set_expires(ngx_http_request_t *r, ngx_http_headers_conf_t *conf) { char *err; @@ -557,6 +729,7 @@ ngx_http_headers_create_conf(ngx_conf_t * set by ngx_pcalloc(): * * conf->headers = NULL; + * conf->trailers = NULL; * conf->expires_time = 0; * conf->expires_value = NULL; */ @@ -587,6 +760,10 @@ ngx_http_headers_merge_conf(ngx_conf_t * conf->headers = prev->headers; } + if (conf->trailers == NULL) { + conf->trailers = prev->trailers; + } + return NGX_CONF_OK; } @@ -597,6 +774,9 @@ ngx_http_headers_filter_init(ngx_conf_t ngx_http_next_header_filter = ngx_http_top_header_filter; ngx_http_top_header_filter = ngx_http_headers_filter; + ngx_http_next_body_filter = ngx_http_top_body_filter; + ngx_http_top_body_filter = ngx_http_trailers_filter; + return NGX_OK; } @@ -741,3 +921,63 @@ ngx_http_headers_add(ngx_conf_t *cf, ngx return NGX_CONF_OK; } + + +static char * +ngx_http_headers_add_trailer(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_headers_conf_t *hcf = conf; + + ngx_str_t *value; + ngx_http_header_val_t *hv; + ngx_http_compile_complex_value_t ccv; + + value = cf->args->elts; + + if (hcf->trailers == NULL) { + hcf->trailers = ngx_array_create(cf->pool, 1, + sizeof(ngx_http_header_val_t)); + if (hcf->trailers == NULL) { + return NGX_CONF_ERROR; + } + } + + hv = ngx_array_push(hcf->trailers); + if (hv == NULL) { + return NGX_CONF_ERROR; + } + + hv->key = value[1]; + hv->handler = NULL; + hv->offset = 0; + hv->always = 0; + + if (value[2].len == 0) { + ngx_memzero(&hv->value, sizeof(ngx_http_complex_value_t)); + + } else { + ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &value[2]; + ccv.complex_value = &hv->value; + + if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + } + + if (cf->args->nelts == 3) { + return NGX_CONF_OK; + } + + if (ngx_strcmp(value[3].data, "always") != 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[3]); + return NGX_CONF_ERROR; + } + + hv->always = 1; + + return NGX_CONF_OK; +} From piotrsikora at google.com Fri Jun 2 09:04:06 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Fri, 02 Jun 2017 02:04:06 -0700 Subject: [PATCH 1 of 3] Added support for trailers in HTTP responses In-Reply-To: References: Message-ID: # HG changeset patch # User Piotr Sikora # Date 1490351854 25200 # Fri Mar 24 03:37:34 2017 -0700 # Node ID b0a910ad494158427ba102bdac71ce01d0667f72 # Parent 716852cce9136d977b81a2d1b8b6f9fbca0dce49 Added support for trailers in HTTP responses. Example: ngx_table_elt_t *h; h = ngx_list_push(&r->headers_out.trailers); if (h == NULL) { return NGX_ERROR; } ngx_str_set(&h->key, "Fun"); ngx_str_set(&h->value, "with trailers"); h->hash = ngx_hash_key_lc(h->key.data, h->key.len); The code above adds "Fun: with trailers" trailer to the response to the request with "TE: trailers" header (which indicates support for trailers). Modules that want to emit trailers must set r->expect_trailers = 1, otherwise they are going to be ignored. This change also adds $sent_trailer_* variables. Signed-off-by: Piotr Sikora diff -r 716852cce913 -r b0a910ad4941 src/http/modules/ngx_http_chunked_filter_module.c --- a/src/http/modules/ngx_http_chunked_filter_module.c +++ b/src/http/modules/ngx_http_chunked_filter_module.c @@ -17,6 +17,8 @@ typedef struct { static ngx_int_t ngx_http_chunked_filter_init(ngx_conf_t *cf); +static ngx_chain_t *ngx_http_chunked_create_trailers(ngx_http_request_t *r, + ngx_uint_t emit_crlf); static ngx_http_module_t ngx_http_chunked_filter_module_ctx = { @@ -69,28 +71,31 @@ ngx_http_chunked_header_filter(ngx_http_ return ngx_http_next_header_filter(r); } - if (r->headers_out.content_length_n == -1) { + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (clcf->chunked_transfer_encoding && r->expect_trailers) { + ngx_http_clear_content_length(r); + r->chunked = 1; + + } else if (r->headers_out.content_length_n == -1) { if (r->http_version < NGX_HTTP_VERSION_11) { r->keepalive = 0; + } else if (clcf->chunked_transfer_encoding) { + r->chunked = 1; + } else { - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + r->keepalive = 0; + } + } - if (clcf->chunked_transfer_encoding) { - r->chunked = 1; + if (r->chunked) { + ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_chunked_filter_ctx_t)); + if (ctx == NULL) { + return NGX_ERROR; + } - ctx = ngx_pcalloc(r->pool, - sizeof(ngx_http_chunked_filter_ctx_t)); - if (ctx == NULL) { - return NGX_ERROR; - } - - ngx_http_set_ctx(r, ctx, ngx_http_chunked_filter_module); - - } else { - r->keepalive = 0; - } - } + ngx_http_set_ctx(r, ctx, ngx_http_chunked_filter_module); } return ngx_http_next_header_filter(r); @@ -179,28 +184,38 @@ ngx_http_chunked_body_filter(ngx_http_re } if (cl->buf->last_buf) { - tl = ngx_chain_get_free_buf(r->pool, &ctx->free); - if (tl == NULL) { - return NGX_ERROR; + + if (r->expect_trailers) { + tl = ngx_http_chunked_create_trailers(r, size ? 1 : 0); + + } else { + tl = NULL; } - b = tl->buf; + if (tl == NULL) { + tl = ngx_chain_get_free_buf(r->pool, &ctx->free); + if (tl == NULL) { + return NGX_ERROR; + } - b->tag = (ngx_buf_tag_t) &ngx_http_chunked_filter_module; - b->temporary = 0; - b->memory = 1; - b->last_buf = 1; - b->pos = (u_char *) CRLF "0" CRLF CRLF; - b->last = b->pos + 7; + b = tl->buf; - cl->buf->last_buf = 0; + b->tag = (ngx_buf_tag_t) &ngx_http_chunked_filter_module; + b->temporary = 0; + b->memory = 1; + b->last_buf = 1; + b->pos = (u_char *) CRLF "0" CRLF CRLF; + b->last = b->pos + 7; + + cl->buf->last_buf = 0; + + if (size == 0) { + b->pos += 2; + } + } *ll = tl; - if (size == 0) { - b->pos += 2; - } - } else if (size > 0) { tl = ngx_chain_get_free_buf(r->pool, &ctx->free); if (tl == NULL) { @@ -230,6 +245,118 @@ ngx_http_chunked_body_filter(ngx_http_re } +static ngx_chain_t * +ngx_http_chunked_create_trailers(ngx_http_request_t *r, ngx_uint_t emit_crlf) +{ + size_t len; + ngx_buf_t *b; + ngx_uint_t i; + ngx_chain_t *cl; + ngx_list_part_t *part; + ngx_table_elt_t *header; + ngx_http_chunked_filter_ctx_t *ctx; + + len = 0; + + part = &r->headers_out.trailers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].hash == 0) { + continue; + } + + len += header[i].key.len + sizeof(": ") - 1 + + header[i].value.len + sizeof(CRLF) - 1; + } + + if (len == 0) { + return NULL; + } + + if (emit_crlf) { + len += sizeof(CRLF) - 1; + } + + len += sizeof("0") - 1 + sizeof(CRLF) - 1 + sizeof(CRLF) - 1; + + ctx = ngx_http_get_module_ctx(r, ngx_http_chunked_filter_module); + + cl = ngx_chain_get_free_buf(r->pool, &ctx->free); + if (cl == NULL) { + return NULL; + } + + b = cl->buf; + + b->tag = (ngx_buf_tag_t) &ngx_http_chunked_filter_module; + b->temporary = 0; + b->memory = 1; + b->last_buf = 1; + + b->start = ngx_palloc(r->pool, len); + if (b->start == NULL) { + return NULL; + } + + b->end = b->last + len; + b->pos = b->start; + b->last = b->start; + + if (emit_crlf) { + *b->last++ = CR; *b->last++ = LF; + } + + *b->last++ = '0'; + *b->last++ = CR; *b->last++ = LF; + + part = &r->headers_out.trailers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].hash == 0) { + continue; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http trailer: \"%V: %V\"", + &header[i].key, &header[i].value); + + b->last = ngx_copy(b->last, header[i].key.data, header[i].key.len); + *b->last++ = ':'; *b->last++ = ' '; + + b->last = ngx_copy(b->last, header[i].value.data, header[i].value.len); + *b->last++ = CR; *b->last++ = LF; + } + + *b->last++ = CR; *b->last++ = LF; + + return cl; +} + + static ngx_int_t ngx_http_chunked_filter_init(ngx_conf_t *cf) { diff -r 716852cce913 -r b0a910ad4941 src/http/ngx_http_core_module.c --- a/src/http/ngx_http_core_module.c +++ b/src/http/ngx_http_core_module.c @@ -2484,6 +2484,13 @@ ngx_http_subrequest(ngx_http_request_t * return NGX_ERROR; } + if (ngx_list_init(&sr->headers_out.trailers, r->pool, 4, + sizeof(ngx_table_elt_t)) + != NGX_OK) + { + return NGX_ERROR; + } + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); sr->main_conf = cscf->ctx->main_conf; sr->srv_conf = cscf->ctx->srv_conf; diff -r 716852cce913 -r b0a910ad4941 src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -562,6 +562,14 @@ ngx_http_create_request(ngx_connection_t return NULL; } + if (ngx_list_init(&r->headers_out.trailers, r->pool, 4, + sizeof(ngx_table_elt_t)) + != NGX_OK) + { + ngx_destroy_pool(r->pool); + return NULL; + } + r->ctx = ngx_pcalloc(r->pool, sizeof(void *) * ngx_http_max_module); if (r->ctx == NULL) { ngx_destroy_pool(r->pool); diff -r 716852cce913 -r b0a910ad4941 src/http/ngx_http_request.h --- a/src/http/ngx_http_request.h +++ b/src/http/ngx_http_request.h @@ -252,6 +252,7 @@ typedef struct { typedef struct { ngx_list_t headers; + ngx_list_t trailers; ngx_uint_t status; ngx_str_t status_line; @@ -514,6 +515,7 @@ struct ngx_http_request_s { unsigned pipeline:1; unsigned chunked:1; unsigned header_only:1; + unsigned expect_trailers:1; unsigned keepalive:1; unsigned lingering_close:1; unsigned discard_body:1; diff -r 716852cce913 -r b0a910ad4941 src/http/ngx_http_variables.c --- a/src/http/ngx_http_variables.c +++ b/src/http/ngx_http_variables.c @@ -38,6 +38,8 @@ static ngx_int_t ngx_http_variable_unkno ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_unknown_header_out(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_unknown_trailer_out(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_request_line(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_cookie(ngx_http_request_t *r, @@ -365,6 +367,9 @@ static ngx_http_variable_t ngx_http_cor { ngx_string("sent_http_"), NULL, ngx_http_variable_unknown_header_out, 0, NGX_HTTP_VAR_PREFIX, 0 }, + { ngx_string("sent_trailer_"), NULL, ngx_http_variable_unknown_trailer_out, + 0, NGX_HTTP_VAR_PREFIX, 0 }, + { ngx_string("cookie_"), NULL, ngx_http_variable_cookie, 0, NGX_HTTP_VAR_PREFIX, 0 }, @@ -934,6 +939,16 @@ ngx_http_variable_unknown_header_out(ngx } +static ngx_int_t +ngx_http_variable_unknown_trailer_out(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + return ngx_http_variable_unknown_header(v, (ngx_str_t *) data, + &r->headers_out.trailers.part, + sizeof("sent_trailer_") - 1); +} + + ngx_int_t ngx_http_variable_unknown_header(ngx_http_variable_value_t *v, ngx_str_t *var, ngx_list_part_t *part, size_t prefix) From piotrsikora at google.com Fri Jun 2 09:04:07 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Fri, 02 Jun 2017 02:04:07 -0700 Subject: [PATCH 2 of 3] HTTP/2: added support for trailers in HTTP responses In-Reply-To: References: Message-ID: # HG changeset patch # User Piotr Sikora # Date 1493191954 25200 # Wed Apr 26 00:32:34 2017 -0700 # Node ID e84aa49c5bc7a3250d4844b581e4bf3ed42db5f5 # Parent b0a910ad494158427ba102bdac71ce01d0667f72 HTTP/2: added support for trailers in HTTP responses. Signed-off-by: Piotr Sikora diff -r b0a910ad4941 -r e84aa49c5bc7 src/http/v2/ngx_http_v2_filter_module.c --- a/src/http/v2/ngx_http_v2_filter_module.c +++ b/src/http/v2/ngx_http_v2_filter_module.c @@ -50,13 +50,17 @@ #define NGX_HTTP_V2_SERVER_INDEX 54 #define NGX_HTTP_V2_VARY_INDEX 59 +#define NGX_HTTP_V2_FRAME_ERROR (ngx_http_v2_out_frame_t *) -1 + static u_char *ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len, u_char *tmp, ngx_uint_t lower); static u_char *ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix, ngx_uint_t value); static ngx_http_v2_out_frame_t *ngx_http_v2_create_headers_frame( - ngx_http_request_t *r, u_char *pos, u_char *end); + ngx_http_request_t *r, u_char *pos, u_char *end, ngx_uint_t fin); +static ngx_http_v2_out_frame_t *ngx_http_v2_create_trailers_frame( + ngx_http_request_t *r); static ngx_chain_t *ngx_http_v2_send_chain(ngx_connection_t *fc, ngx_chain_t *in, off_t limit); @@ -612,7 +616,7 @@ ngx_http_v2_header_filter(ngx_http_reque header[i].value.len, tmp); } - frame = ngx_http_v2_create_headers_frame(r, start, pos); + frame = ngx_http_v2_create_headers_frame(r, start, pos, r->header_only); if (frame == NULL) { return NGX_ERROR; } @@ -636,6 +640,126 @@ ngx_http_v2_header_filter(ngx_http_reque } +static ngx_http_v2_out_frame_t * +ngx_http_v2_create_trailers_frame(ngx_http_request_t *r) +{ + u_char *pos, *start, *tmp; + size_t len, tmp_len; + ngx_uint_t i; + ngx_list_part_t *part; + ngx_table_elt_t *header; + ngx_http_v2_out_frame_t *frame; + + len = 0; + tmp_len = 0; + + part = &r->headers_out.trailers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].hash == 0) { + continue; + } + + if (header[i].key.len > NGX_HTTP_V2_MAX_FIELD) { + ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, + "too long response trailer name: \"%V\"", + &header[i].key); + + return NGX_HTTP_V2_FRAME_ERROR; + } + + if (header[i].value.len > NGX_HTTP_V2_MAX_FIELD) { + ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, + "too long response trailer value: \"%V: %V\"", + &header[i].key, &header[i].value); + + return NGX_HTTP_V2_FRAME_ERROR; + } + + len += 1 + NGX_HTTP_V2_INT_OCTETS + header[i].key.len + + NGX_HTTP_V2_INT_OCTETS + header[i].value.len; + + if (header[i].key.len > tmp_len) { + tmp_len = header[i].key.len; + } + + if (header[i].value.len > tmp_len) { + tmp_len = header[i].value.len; + } + } + + if (len == 0) { + return NULL; + } + + tmp = ngx_palloc(r->pool, tmp_len); + pos = ngx_pnalloc(r->pool, len); + + if (pos == NULL || tmp == NULL) { + return NGX_HTTP_V2_FRAME_ERROR; + } + + start = pos; + + part = &r->headers_out.trailers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].hash == 0) { + continue; + } + +#if (NGX_DEBUG) + if (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP) { + ngx_strlow(tmp, header[i].key.data, header[i].key.len); + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http2 output trailer: \"%*s: %V\"", + header[i].key.len, tmp, &header[i].value); + } +#endif + + *pos++ = 0; + + pos = ngx_http_v2_write_name(pos, header[i].key.data, + header[i].key.len, tmp); + + pos = ngx_http_v2_write_value(pos, header[i].value.data, + header[i].value.len, tmp); + } + + frame = ngx_http_v2_create_headers_frame(r, start, pos, 1); + if (frame == NULL) { + return NGX_HTTP_V2_FRAME_ERROR; + } + + return frame; +} + + static u_char * ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len, u_char *tmp, ngx_uint_t lower) @@ -686,7 +810,7 @@ ngx_http_v2_write_int(u_char *pos, ngx_u static ngx_http_v2_out_frame_t * ngx_http_v2_create_headers_frame(ngx_http_request_t *r, u_char *pos, - u_char *end) + u_char *end, ngx_uint_t fin) { u_char type, flags; size_t rest, frame_size; @@ -707,12 +831,12 @@ ngx_http_v2_create_headers_frame(ngx_htt frame->stream = stream; frame->length = rest; frame->blocked = 1; - frame->fin = r->header_only; + frame->fin = fin; ll = &frame->first; type = NGX_HTTP_V2_HEADERS_FRAME; - flags = r->header_only ? NGX_HTTP_V2_END_STREAM_FLAG : NGX_HTTP_V2_NO_FLAG; + flags = fin ? NGX_HTTP_V2_END_STREAM_FLAG : NGX_HTTP_V2_NO_FLAG; frame_size = stream->connection->frame_size; for ( ;; ) { @@ -776,7 +900,7 @@ ngx_http_v2_create_headers_frame(ngx_htt continue; } - b->last_buf = r->header_only; + b->last_buf = fin; cl->next = NULL; frame->last = cl; @@ -798,7 +922,7 @@ ngx_http_v2_send_chain(ngx_connection_t ngx_http_request_t *r; ngx_http_v2_stream_t *stream; ngx_http_v2_loc_conf_t *h2lcf; - ngx_http_v2_out_frame_t *frame; + ngx_http_v2_out_frame_t *frame, *trailers; ngx_http_v2_connection_t *h2c; r = fc->data; @@ -872,6 +996,8 @@ ngx_http_v2_send_chain(ngx_connection_t frame_size = (h2lcf->chunk_size < h2c->frame_size) ? h2lcf->chunk_size : h2c->frame_size; + trailers = NULL; + #if (NGX_SUPPRESS_WARN) cl = NULL; #endif @@ -934,17 +1060,36 @@ ngx_http_v2_send_chain(ngx_connection_t size -= rest; } - frame = ngx_http_v2_filter_get_data_frame(stream, frame_size, out, cl); - if (frame == NULL) { - return NGX_CHAIN_ERROR; + if (cl->buf->last_buf && r->expect_trailers) { + trailers = ngx_http_v2_create_trailers_frame(r); + if (trailers == NGX_HTTP_V2_FRAME_ERROR) { + return NGX_CHAIN_ERROR; + } + + if (trailers) { + cl->buf->last_buf = 0; + } } - ngx_http_v2_queue_frame(h2c, frame); + if (frame_size || cl->buf->last_buf) { + frame = ngx_http_v2_filter_get_data_frame(stream, frame_size, out, + cl); + if (frame == NULL) { + return NGX_CHAIN_ERROR; + } - h2c->send_window -= frame_size; + ngx_http_v2_queue_frame(h2c, frame); - stream->send_window -= frame_size; - stream->queued++; + h2c->send_window -= frame_size; + + stream->send_window -= frame_size; + stream->queued++; + } + + if (trailers) { + ngx_http_v2_queue_frame(h2c, trailers); + stream->queued++; + } if (in == NULL) { break; From piotrsikora at google.com Fri Jun 2 09:05:47 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Fri, 2 Jun 2017 02:05:47 -0700 Subject: [PATCH 1 of 3] HTTP: add support for trailers in HTTP responses In-Reply-To: <20170503140018.GN43932@mdounin.ru> References: <8af81a0d66c0f69bcf50.1491211931@piotrsikora.sfo.corp.google.com> <20170421174201.GA43932@mdounin.ru> <20170427184623.GD43932@mdounin.ru> <20170503140018.GN43932@mdounin.ru> Message-ID: Hey Maxim, > In your patch, you test r->expect_trailers in two places in > chunked filter: > > 1. when you decide whether to use chunked encoding or not, in > ngx_http_chunked_header_filter(); > > 2. when you generate trailer, in ngx_http_chunked_body_filter(). > > I mostly agree with (1) (I would like see it configurable too, but > it's probably up to a module which adds trailers to decide, so > don't belong to this particular patch), but don't see any reasons > for (2). This is done to avoid unexpected behavior when r->expect_trailers isn't set, i.e. if module added trailers, but didn't set r->expect_trailers, then without (2) trailers would be emitted only if Content-Encoding was already chunked, but not otherwise. Testing r->expect_trailers in both (1) and (2) ensures that Trailers are emitted in consistent manner, without depending on unrelated factors, like gzip support by the client, etc. > Certainly merging is a bad idea either. May be the solution would > be avoid sending trailers for header-only requests consistently > for both HTTP/1.1 and HTTP/2. Done, I removed trailers from headers-only HTTP/2 responses. As mentioned earlier, I also removed "TE: trailers" requirement and merged whole trailer-part into one buffer, in case of trailers. Best regards, Piotr Sikora From piotrsikora at google.com Fri Jun 2 09:10:07 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Fri, 2 Jun 2017 02:10:07 -0700 Subject: [PATCH 2 of 3] Headers filter: add "add_trailer" directive In-Reply-To: <20170421174207.GB43932@mdounin.ru> References: <5bab17ebe2b1f8ec42cf.1491211932@piotrsikora.sfo.corp.google.com> <20170421174207.GB43932@mdounin.ru> Message-ID: Hey Maxim, > This introduces a layering violation between the headers filter > and the chunked filter. I moved trailer generation to headers filter, let me know if that works for you. Unfortunately, because of that change, trailers are evaluated earlier in the process, and some variables from body filters that run after headers filter (like $gzip_ratio) are not usable anymore. > There should be a space between "NULL" and "}". It might worth > fixing the style in previously exiting directives in a separate > commit. Done. > Predicting conditions which will result in header-only response > looks really fragile. As well as checks for chunked transfer > encoding. Sending "Trailer" header in responses that cannot have response body is known to result in interoperability issues, so I'd rather leave it, see: https://github.com/nodejs/node/issues/2842 > Note: this can result in an access to uninitialized memory if > ngx_pnalloc() fails and $sent_http_trailer used in logs. Fixed. > Note that at this point we do not know if the trailer in question > is going to be present in the particular response or not, as > trailers with empty values are not returned (and this is often > used to only return some headers in some responses, at least with > add_header). But we know that it might be present. "Trailer" header is an indicator. > It might be a better idea to avoid generating the > "Trailer" header automatically and let users to control this with > add_header instead, and/or introduce an option to control this. That sounds like a terrible idea, which will result in always missing "Trailer" header. > Note well that with the approach taken it will be very > inconvenient for other modules to add their own trailers, due to > both a) layering violation introduced, which makes headers filter > special, and b) no easy way to add anything to the Trailer header > generated. This is well-formatted header, so other modules can emit their own "Trailer" headers, or append to it. Also, I'm don't see how this is different from virtually any other header in NGINX. > There is no need to set hash for output headers, just t->hash = 1 > is good enough. Actually, it's not "good enough". Using "hash = 1" has absolutely no performance benefit and it results in full search instead of simple hash comparison during header lookup, because some headers won't have hash set to proper value. Anyway, I'm going set it to "1" to avoid side-tracking this discussion. Best regards, Piotr Sikora From piotrsikora at google.com Fri Jun 2 09:15:30 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Fri, 2 Jun 2017 02:15:30 -0700 Subject: [PATCH 3 of 3] Upstream: add support for trailers in HTTP responses In-Reply-To: <20170421174211.GC43932@mdounin.ru> References: <488c59bd49dcb1503144.1491211933@piotrsikora.sfo.corp.google.com> <20170421174211.GC43932@mdounin.ru> Message-ID: Hey Maxim, > Overral, this patch looks at most half-ready, as it doesn't even > try to implement sending trailers (r->expect_trailers is never > set), lacks any support for trailers in the cache, and so on. Actually, it's pretty much ready... r->expect_trailers is supposed to be set by upstream modules (i.e. proxy or 3rd-party upstream module), and not by the upstream module itself. But I can see how it can be hard to review only part of the feature, so I'm going to drop this for now (mostly to speed up the turnaround of code reviews), and I'll send this later, along HTTP/2 patchset that uses those changes. Best regards, Piotr Sikora From pluknet at nginx.com Fri Jun 2 09:56:29 2017 From: pluknet at nginx.com (Sergey Kandaurov) Date: Fri, 02 Jun 2017 09:56:29 +0000 Subject: [nginx] Configure: enabled rpath for NetBSD. Message-ID: details: http://hg.nginx.org/nginx/rev/639e48c382a6 branches: changeset: 7021:639e48c382a6 user: Sergey Kandaurov date: Fri Jun 02 12:55:31 2017 +0300 description: Configure: enabled rpath for NetBSD. diffstat: auto/os/conf | 8 ++++++++ 1 files changed, 8 insertions(+), 0 deletions(-) diffs (18 lines): diff -r 716852cce913 -r 639e48c382a6 auto/os/conf --- a/auto/os/conf Thu Jun 01 15:44:23 2017 +0300 +++ b/auto/os/conf Fri Jun 02 12:55:31 2017 +0300 @@ -41,6 +41,14 @@ case "$NGX_PLATFORM" in ' ;; + NetBSD:*) + CORE_INCS="$UNIX_INCS" + CORE_DEPS="$UNIX_DEPS $POSIX_DEPS" + CORE_SRCS="$UNIX_SRCS" + + NGX_RPATH=YES + ;; + HP-UX:*) # HP/UX have=NGX_HPUX . auto/have_headers From mdounin at mdounin.ru Fri Jun 2 11:46:03 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Fri, 2 Jun 2017 14:46:03 +0300 Subject: Use primes for hashtable size In-Reply-To: References: <20170530130137.GT55433@mdounin.ru> <20170601173951.GO55433@mdounin.ru> Message-ID: <20170602114603.GR55433@mdounin.ru> Hello! On Fri, Jun 02, 2017 at 10:56:31AM +1000, Mathew Heard wrote: > If this actually yields a decrease in start time while not introducing > other effects we would use it. Our start time of a couple minutes is > annoying at times. Do you have any details of what contributes to the start time in your case? In general, nginx trades start time to faster operation once started, and trying to build minimal hash is an example of this practice. If it results in unacceptable start times we certainly should optimize it, though I don't think I've seen such cases in my practice. Most time-consuming things during start I've seen so far are: - multiple DNS names in the configuration and slow system resolver; - multiple SSL certificates; - very large geo{} maps. -- Maxim Dounin http://nginx.org/ From mat999 at gmail.com Fri Jun 2 11:50:18 2017 From: mat999 at gmail.com (Mathew Heard) Date: Fri, 2 Jun 2017 21:50:18 +1000 Subject: Use primes for hashtable size In-Reply-To: <20170602114603.GR55433@mdounin.ru> References: <20170530130137.GT55433@mdounin.ru> <20170601173951.GO55433@mdounin.ru> <20170602114603.GR55433@mdounin.ru> Message-ID: We are loading around 10,000 - 15,000 server_names per server. We also have a fair number of SSL certificates and at-least one big geo map as well which probably do contribute. At around 2,000 - 3,000 we hit our first issues with server_name and had to alter the hash table max. Which brought the loading speed back up (which has slowly regressed as we got bigger) Honestly I don't consider it unacceptable, but if this patch comes with no runtime performance penalty why wouldnt we want it? There are advantages to faster startup (incident recovery, quicker reconfiguration etc) On Fri, Jun 2, 2017 at 9:46 PM, Maxim Dounin wrote: > Hello! > > On Fri, Jun 02, 2017 at 10:56:31AM +1000, Mathew Heard wrote: > > > If this actually yields a decrease in start time while not introducing > > other effects we would use it. Our start time of a couple minutes is > > annoying at times. > > Do you have any details of what contributes to the start time in > your case? > > In general, nginx trades start time to faster operation once > started, and trying to build minimal hash is an example of this > practice. If it results in unacceptable start times we certainly > should optimize it, though I don't think I've seen such cases in > my practice. > > Most time-consuming things during start I've seen so far are: > > - multiple DNS names in the configuration and slow system > resolver; > > - multiple SSL certificates; > > - very large geo{} maps. > > -- > Maxim Dounin > http://nginx.org/ > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel > -------------- next part -------------- An HTML attachment was scrubbed... URL: From vbart at nginx.com Fri Jun 2 12:08:26 2017 From: vbart at nginx.com (Valentin Bartenev) Date: Fri, 02 Jun 2017 12:08:26 +0000 Subject: [nginx] HTTP/2: emit new frames only after applying all SETTINGS params. Message-ID: details: http://hg.nginx.org/nginx/rev/645ed7112a01 branches: changeset: 7022:645ed7112a01 user: Piotr Sikora date: Fri Jun 02 15:05:20 2017 +0300 description: HTTP/2: emit new frames only after applying all SETTINGS params. Previously, new frames could be emitted in the middle of applying new (and already acknowledged) SETTINGS params, which is illegal. Signed-off-by: Piotr Sikora diffstat: src/http/v2/ngx_http_v2.c | 17 +++++++++++------ 1 files changed, 11 insertions(+), 6 deletions(-) diffs (43 lines): diff -r 639e48c382a6 -r 645ed7112a01 src/http/v2/ngx_http_v2.c --- a/src/http/v2/ngx_http_v2.c Fri Jun 02 12:55:31 2017 +0300 +++ b/src/http/v2/ngx_http_v2.c Fri Jun 02 15:05:20 2017 +0300 @@ -1969,8 +1969,11 @@ static u_char * ngx_http_v2_state_settings_params(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end) { + ssize_t window_delta; ngx_uint_t id, value; + window_delta = 0; + while (h2c->state.length) { if (end - pos < NGX_HTTP_V2_SETTINGS_PARAM_SIZE) { return ngx_http_v2_state_save(h2c, pos, end, @@ -1995,12 +1998,7 @@ ngx_http_v2_state_settings_params(ngx_ht NGX_HTTP_V2_FLOW_CTRL_ERROR); } - if (ngx_http_v2_adjust_windows(h2c, value - h2c->init_window) - != NGX_OK) - { - return ngx_http_v2_connection_error(h2c, - NGX_HTTP_V2_INTERNAL_ERROR); - } + window_delta = value - h2c->init_window; h2c->init_window = value; break; @@ -2028,6 +2026,13 @@ ngx_http_v2_state_settings_params(ngx_ht pos += NGX_HTTP_V2_SETTINGS_PARAM_SIZE; } + if (window_delta) { + if (ngx_http_v2_adjust_windows(h2c, window_delta) != NGX_OK) { + return ngx_http_v2_connection_error(h2c, + NGX_HTTP_V2_INTERNAL_ERROR); + } + } + return ngx_http_v2_state_complete(h2c, pos, end); } From vbart at nginx.com Fri Jun 2 12:08:29 2017 From: vbart at nginx.com (Valentin Bartenev) Date: Fri, 02 Jun 2017 12:08:29 +0000 Subject: [nginx] HTTP/2: send SETTINGS ACK after applying all SETTINGS params. Message-ID: details: http://hg.nginx.org/nginx/rev/859d80f57aab branches: changeset: 7023:859d80f57aab user: Piotr Sikora date: Fri Jun 02 15:05:24 2017 +0300 description: HTTP/2: send SETTINGS ACK after applying all SETTINGS params. This avoids sending unnecessary SETTINGS ACK in case of PROTOCOL_ERROR. Signed-off-by: Piotr Sikora diffstat: src/http/v2/ngx_http_v2.c | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diffs (21 lines): diff -r 645ed7112a01 -r 859d80f57aab src/http/v2/ngx_http_v2.c --- a/src/http/v2/ngx_http_v2.c Fri Jun 02 15:05:20 2017 +0300 +++ b/src/http/v2/ngx_http_v2.c Fri Jun 02 15:05:24 2017 +0300 @@ -1959,8 +1959,6 @@ ngx_http_v2_state_settings(ngx_http_v2_c return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); } - ngx_http_v2_send_settings(h2c, 1); - return ngx_http_v2_state_settings_params(h2c, pos, end); } @@ -2026,6 +2024,8 @@ ngx_http_v2_state_settings_params(ngx_ht pos += NGX_HTTP_V2_SETTINGS_PARAM_SIZE; } + ngx_http_v2_send_settings(h2c, 1); + if (window_delta) { if (ngx_http_v2_adjust_windows(h2c, window_delta) != NGX_OK) { return ngx_http_v2_connection_error(h2c, From vbart at nginx.com Fri Jun 2 12:08:31 2017 From: vbart at nginx.com (Valentin Bartenev) Date: Fri, 02 Jun 2017 12:08:31 +0000 Subject: [nginx] HTTP/2: make SETTINGS ACK frame reusable. Message-ID: details: http://hg.nginx.org/nginx/rev/79de0d2aa432 branches: changeset: 7024:79de0d2aa432 user: Piotr Sikora date: Fri Jun 02 15:05:28 2017 +0300 description: HTTP/2: make SETTINGS ACK frame reusable. Signed-off-by: Piotr Sikora diffstat: src/http/v2/ngx_http_v2.c | 62 +++++++++++++++++++++++++--------------------- 1 files changed, 34 insertions(+), 28 deletions(-) diffs (132 lines): diff -r 859d80f57aab -r 79de0d2aa432 src/http/v2/ngx_http_v2.c --- a/src/http/v2/ngx_http_v2.c Fri Jun 02 15:05:24 2017 +0300 +++ b/src/http/v2/ngx_http_v2.c Fri Jun 02 15:05:28 2017 +0300 @@ -28,6 +28,7 @@ #define NGX_HTTP_V2_HTTP_1_1_REQUIRED 0xd /* frame sizes */ +#define NGX_HTTP_V2_SETTINGS_ACK_SIZE 0 #define NGX_HTTP_V2_RST_STREAM_SIZE 4 #define NGX_HTTP_V2_PRIORITY_SIZE 5 #define NGX_HTTP_V2_PING_SIZE 8 @@ -128,8 +129,7 @@ static ngx_http_v2_node_t *ngx_http_v2_g #define ngx_http_v2_index_size(h2scf) (h2scf->streams_index_mask + 1) #define ngx_http_v2_index(h2scf, sid) ((sid >> 1) & h2scf->streams_index_mask) -static ngx_int_t ngx_http_v2_send_settings(ngx_http_v2_connection_t *h2c, - ngx_uint_t ack); +static ngx_int_t ngx_http_v2_send_settings(ngx_http_v2_connection_t *h2c); static ngx_int_t ngx_http_v2_settings_frame_handler( ngx_http_v2_connection_t *h2c, ngx_http_v2_out_frame_t *frame); static ngx_int_t ngx_http_v2_send_window_update(ngx_http_v2_connection_t *h2c, @@ -269,7 +269,7 @@ ngx_http_v2_init(ngx_event_t *rev) return; } - if (ngx_http_v2_send_settings(h2c, 0) == NGX_ERROR) { + if (ngx_http_v2_send_settings(h2c) == NGX_ERROR) { ngx_http_close_connection(c); return; } @@ -1967,8 +1967,9 @@ static u_char * ngx_http_v2_state_settings_params(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end) { - ssize_t window_delta; - ngx_uint_t id, value; + ssize_t window_delta; + ngx_uint_t id, value; + ngx_http_v2_out_frame_t *frame; window_delta = 0; @@ -2024,7 +2025,14 @@ ngx_http_v2_state_settings_params(ngx_ht pos += NGX_HTTP_V2_SETTINGS_PARAM_SIZE; } - ngx_http_v2_send_settings(h2c, 1); + frame = ngx_http_v2_get_frame(h2c, NGX_HTTP_V2_SETTINGS_ACK_SIZE, + NGX_HTTP_V2_SETTINGS_FRAME, + NGX_HTTP_V2_ACK_FLAG, 0); + if (frame == NULL) { + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR); + } + + ngx_http_v2_queue_blocked_frame(h2c, frame); if (window_delta) { if (ngx_http_v2_adjust_windows(h2c, window_delta) != NGX_OK) { @@ -2476,7 +2484,7 @@ ngx_http_v2_parse_int(ngx_http_v2_connec static ngx_int_t -ngx_http_v2_send_settings(ngx_http_v2_connection_t *h2c, ngx_uint_t ack) +ngx_http_v2_send_settings(ngx_http_v2_connection_t *h2c) { size_t len; ngx_buf_t *buf; @@ -2484,8 +2492,8 @@ ngx_http_v2_send_settings(ngx_http_v2_co ngx_http_v2_srv_conf_t *h2scf; ngx_http_v2_out_frame_t *frame; - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, - "http2 send SETTINGS frame ack:%ui", ack); + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 send SETTINGS frame"); frame = ngx_palloc(h2c->pool, sizeof(ngx_http_v2_out_frame_t)); if (frame == NULL) { @@ -2497,7 +2505,7 @@ ngx_http_v2_send_settings(ngx_http_v2_co return NGX_ERROR; } - len = ack ? 0 : (sizeof(uint16_t) + sizeof(uint32_t)) * 3; + len = NGX_HTTP_V2_SETTINGS_PARAM_SIZE * 3; buf = ngx_create_temp_buf(h2c->pool, NGX_HTTP_V2_FRAME_HEADER_SIZE + len); if (buf == NULL) { @@ -2521,28 +2529,26 @@ ngx_http_v2_send_settings(ngx_http_v2_co buf->last = ngx_http_v2_write_len_and_type(buf->last, len, NGX_HTTP_V2_SETTINGS_FRAME); - *buf->last++ = ack ? NGX_HTTP_V2_ACK_FLAG : NGX_HTTP_V2_NO_FLAG; + *buf->last++ = NGX_HTTP_V2_NO_FLAG; buf->last = ngx_http_v2_write_sid(buf->last, 0); - if (!ack) { - h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx, - ngx_http_v2_module); - - buf->last = ngx_http_v2_write_uint16(buf->last, - NGX_HTTP_V2_MAX_STREAMS_SETTING); - buf->last = ngx_http_v2_write_uint32(buf->last, - h2scf->concurrent_streams); - - buf->last = ngx_http_v2_write_uint16(buf->last, + h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx, + ngx_http_v2_module); + + buf->last = ngx_http_v2_write_uint16(buf->last, + NGX_HTTP_V2_MAX_STREAMS_SETTING); + buf->last = ngx_http_v2_write_uint32(buf->last, + h2scf->concurrent_streams); + + buf->last = ngx_http_v2_write_uint16(buf->last, NGX_HTTP_V2_INIT_WINDOW_SIZE_SETTING); - buf->last = ngx_http_v2_write_uint32(buf->last, h2scf->preread_size); - - buf->last = ngx_http_v2_write_uint16(buf->last, - NGX_HTTP_V2_MAX_FRAME_SIZE_SETTING); - buf->last = ngx_http_v2_write_uint32(buf->last, - NGX_HTTP_V2_MAX_FRAME_SIZE); - } + buf->last = ngx_http_v2_write_uint32(buf->last, h2scf->preread_size); + + buf->last = ngx_http_v2_write_uint16(buf->last, + NGX_HTTP_V2_MAX_FRAME_SIZE_SETTING); + buf->last = ngx_http_v2_write_uint32(buf->last, + NGX_HTTP_V2_MAX_FRAME_SIZE); ngx_http_v2_queue_blocked_frame(h2c, frame); From vbart at nginx.com Fri Jun 2 12:08:34 2017 From: vbart at nginx.com (Valentin Bartenev) Date: Fri, 02 Jun 2017 12:08:34 +0000 Subject: [nginx] HTTP/2: don't send SETTINGS ACK before already queued DATA frames. Message-ID: details: http://hg.nginx.org/nginx/rev/7206c3630310 branches: changeset: 7025:7206c3630310 user: Piotr Sikora date: Fri Jun 02 15:05:32 2017 +0300 description: HTTP/2: don't send SETTINGS ACK before already queued DATA frames. Previously, SETTINGS ACK was sent immediately upon receipt of SETTINGS frame, before already queued DATA frames created using old SETTINGS. This incorrect behavior was source of interoperability issues, because peers rely on the fact that new SETTINGS are in effect after receiving SETTINGS ACK. Reported by Feng Li. Signed-off-by: Piotr Sikora diffstat: src/http/v2/ngx_http_v2.c | 2 +- src/http/v2/ngx_http_v2.h | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletions(-) diffs (31 lines): diff -r 79de0d2aa432 -r 7206c3630310 src/http/v2/ngx_http_v2.c --- a/src/http/v2/ngx_http_v2.c Fri Jun 02 15:05:28 2017 +0300 +++ b/src/http/v2/ngx_http_v2.c Fri Jun 02 15:05:32 2017 +0300 @@ -2032,7 +2032,7 @@ ngx_http_v2_state_settings_params(ngx_ht return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR); } - ngx_http_v2_queue_blocked_frame(h2c, frame); + ngx_http_v2_queue_ordered_frame(h2c, frame); if (window_delta) { if (ngx_http_v2_adjust_windows(h2c, window_delta) != NGX_OK) { diff -r 79de0d2aa432 -r 7206c3630310 src/http/v2/ngx_http_v2.h --- a/src/http/v2/ngx_http_v2.h Fri Jun 02 15:05:28 2017 +0300 +++ b/src/http/v2/ngx_http_v2.h Fri Jun 02 15:05:32 2017 +0300 @@ -261,6 +261,15 @@ ngx_http_v2_queue_blocked_frame(ngx_http } +static ngx_inline void +ngx_http_v2_queue_ordered_frame(ngx_http_v2_connection_t *h2c, + ngx_http_v2_out_frame_t *frame) +{ + frame->next = h2c->last_out; + h2c->last_out = frame; +} + + void ngx_http_v2_init(ngx_event_t *rev); void ngx_http_v2_request_headers_init(void); From vbart at nginx.com Fri Jun 2 12:11:06 2017 From: vbart at nginx.com (Valentin V. Bartenev) Date: Fri, 02 Jun 2017 15:11:06 +0300 Subject: [PATCH 1 of 4] HTTP/2: emit new frames only after applying all SETTINGS params In-Reply-To: References: <07adf0a7009c3244de4b.1493074103@piotrsikora.sfo.corp.google.com> <8753794.qGPzJ25FTB@vbart-workstation> Message-ID: <2128928.XChoDM9hEe@vbart-workstation> On Thursday 01 June 2017 14:55:06 Piotr Sikora via nginx-devel wrote: > Hey Valentin, > > > The new initial window size can be lower than the previous one, > > so the difference can be negative (that's why the delta parameter > > of ngx_http_v2_adjust_windows() is ssize_t). > > Oops, good catch, thanks! > > Funnily enough, the original patch worked just fine (at least on > systems where size of ssize_t was equal to size of ngx_uint_t), since > the value would overflow and result in the same negative number when > casted to ssize_t. > > I updated patchset with suggested changes, please take a look. > The whole patchset was committed. Thank you. wbr, Valentin V. Bartenev From mdounin at mdounin.ru Fri Jun 2 12:30:35 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Fri, 2 Jun 2017 15:30:35 +0300 Subject: [PATCH 1 of 3] HTTP: add support for trailers in HTTP responses In-Reply-To: References: <8af81a0d66c0f69bcf50.1491211931@piotrsikora.sfo.corp.google.com> <20170421174201.GA43932@mdounin.ru> <20170427184623.GD43932@mdounin.ru> <20170503140018.GN43932@mdounin.ru> Message-ID: <20170602123035.GT55433@mdounin.ru> Hello! On Fri, Jun 02, 2017 at 02:05:47AM -0700, Piotr Sikora via nginx-devel wrote: > Hey Maxim, > > > In your patch, you test r->expect_trailers in two places in > > chunked filter: > > > > 1. when you decide whether to use chunked encoding or not, in > > ngx_http_chunked_header_filter(); > > > > 2. when you generate trailer, in ngx_http_chunked_body_filter(). > > > > I mostly agree with (1) (I would like see it configurable too, but > > it's probably up to a module which adds trailers to decide, so > > don't belong to this particular patch), but don't see any reasons > > for (2). > > This is done to avoid unexpected behavior when r->expect_trailers > isn't set, i.e. if module added trailers, but didn't set > r->expect_trailers, then without (2) trailers would be emitted only if > Content-Encoding was already chunked, but not otherwise. > > Testing r->expect_trailers in both (1) and (2) ensures that Trailers > are emitted in consistent manner, without depending on unrelated > factors, like gzip support by the client, etc. I see two problems here: a. There may be use cases when forcing chunked encoding is not desired, but emitting trailers if it is used still makes sense. b. Nothing stops modules from changing r->expect_trailers when the response header was already sent and it is already too late to switch to chunked transfer encoding. Moreover, this will naturally happen with any module which is simply following the requirement to set r->expect_trailers to 1 as in your commit log. So (a) makes (2) excessively limiting, and (b) makes it useless. -- Maxim Dounin http://nginx.org/ From aricos at protonmail.com Fri Jun 2 13:25:31 2017 From: aricos at protonmail.com (Aris) Date: Fri, 02 Jun 2017 09:25:31 -0400 Subject: NGINX-RTMP: control/redirect/publisher command example Message-ID: Hi! I'm trying to redirect an input stream in app live to app forward using the control/redirect/publisher command without any success. Here is my conf file. application live { live on; allow publish all; allow play all; } application forward { live on; allow publish all; allow play all; push rtmp://localhost:8080/live/test05; } Could anyone give an example on how to use control/redirect/publisher command for the above config? Thanks. -------------- next part -------------- An HTML attachment was scrubbed... URL: From mdounin at mdounin.ru Fri Jun 2 15:36:07 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Fri, 2 Jun 2017 18:36:07 +0300 Subject: [PATCH 1 of 3] Added support for trailers in HTTP responses In-Reply-To: References: Message-ID: <20170602153607.GU55433@mdounin.ru> Hello! On Fri, Jun 02, 2017 at 02:04:06AM -0700, Piotr Sikora via nginx-devel wrote: > # HG changeset patch > # User Piotr Sikora > # Date 1490351854 25200 > # Fri Mar 24 03:37:34 2017 -0700 > # Node ID b0a910ad494158427ba102bdac71ce01d0667f72 > # Parent 716852cce9136d977b81a2d1b8b6f9fbca0dce49 > Added support for trailers in HTTP responses. > > Example: > > ngx_table_elt_t *h; > > h = ngx_list_push(&r->headers_out.trailers); > if (h == NULL) { > return NGX_ERROR; > } > > ngx_str_set(&h->key, "Fun"); > ngx_str_set(&h->value, "with trailers"); > h->hash = ngx_hash_key_lc(h->key.data, h->key.len); > > The code above adds "Fun: with trailers" trailer to the response to > the request with "TE: trailers" header (which indicates support for > trailers). Note: the "TE: trailers" requirement is no longer present in the code. > > Modules that want to emit trailers must set r->expect_trailers = 1, > otherwise they are going to be ignored. > > This change also adds $sent_trailer_* variables. > > Signed-off-by: Piotr Sikora > > diff -r 716852cce913 -r b0a910ad4941 src/http/modules/ngx_http_chunked_filter_module.c > --- a/src/http/modules/ngx_http_chunked_filter_module.c > +++ b/src/http/modules/ngx_http_chunked_filter_module.c > @@ -17,6 +17,8 @@ typedef struct { > > > static ngx_int_t ngx_http_chunked_filter_init(ngx_conf_t *cf); > +static ngx_chain_t *ngx_http_chunked_create_trailers(ngx_http_request_t *r, > + ngx_uint_t emit_crlf); > > > static ngx_http_module_t ngx_http_chunked_filter_module_ctx = { > @@ -69,28 +71,31 @@ ngx_http_chunked_header_filter(ngx_http_ > return ngx_http_next_header_filter(r); > } > > - if (r->headers_out.content_length_n == -1) { > + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); > + > + if (clcf->chunked_transfer_encoding && r->expect_trailers) { > + ngx_http_clear_content_length(r); > + r->chunked = 1; > + This code results in using chunked encoding for HTTP/1.0 when trailers are expected. Such behaviour is explicitly forbidden by the HTTP/1.1 specification, and will very likely result in problems (we've seen lots of such problems with broken backends when there were no HTTP/1.1 support in the proxy module). Something like this should be a better solution: if (r->headers_out.content_length_n == -1 || r->expect_trailers) { clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); if (r->http_version >= NGX_HTTP_VERSION_11 && clcf->chunked_transfer_encoding) { if (r->expect_trailers) { ngx_http_clear_content_length(r); } r->chunked = 1; ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_chunked_filter_ctx_t)); if (ctx == NULL) { return NGX_ERROR; } ngx_http_set_ctx(r, ctx, ngx_http_chunked_filter_module); } else if (r->headers_out.content_length_n == -1) { r->keepalive = 0; } } > + } else if (r->headers_out.content_length_n == -1) { > if (r->http_version < NGX_HTTP_VERSION_11) { > r->keepalive = 0; > > + } else if (clcf->chunked_transfer_encoding) { > + r->chunked = 1; > + > } else { > - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); > + r->keepalive = 0; > + } > + } > > - if (clcf->chunked_transfer_encoding) { > - r->chunked = 1; > + if (r->chunked) { > + ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_chunked_filter_ctx_t)); > + if (ctx == NULL) { > + return NGX_ERROR; > + } > > - ctx = ngx_pcalloc(r->pool, > - sizeof(ngx_http_chunked_filter_ctx_t)); > - if (ctx == NULL) { > - return NGX_ERROR; > - } > - > - ngx_http_set_ctx(r, ctx, ngx_http_chunked_filter_module); > - > - } else { > - r->keepalive = 0; > - } > - } > + ngx_http_set_ctx(r, ctx, ngx_http_chunked_filter_module); > } > > return ngx_http_next_header_filter(r); > @@ -179,28 +184,38 @@ ngx_http_chunked_body_filter(ngx_http_re > } > > if (cl->buf->last_buf) { > - tl = ngx_chain_get_free_buf(r->pool, &ctx->free); > - if (tl == NULL) { > - return NGX_ERROR; > + > + if (r->expect_trailers) { > + tl = ngx_http_chunked_create_trailers(r, size ? 1 : 0); See the previous thread about the r->expect_trailers test here. > + > + } else { > + tl = NULL; > } > > - b = tl->buf; > + if (tl == NULL) { > + tl = ngx_chain_get_free_buf(r->pool, &ctx->free); > + if (tl == NULL) { > + return NGX_ERROR; > + } Instead of providing two separate code paths for "with trailer headers" and "without trailer headers", it might be better and more readable to generate last-chunk in one function regardless of whether trailer headers are present or not. It will also make error handling better: as of now, an allocation error in ngx_http_chunked_create_trailers() will result in "no trailers" code path being tried instead of returning an unconditional error. > > - b->tag = (ngx_buf_tag_t) &ngx_http_chunked_filter_module; > - b->temporary = 0; > - b->memory = 1; > - b->last_buf = 1; > - b->pos = (u_char *) CRLF "0" CRLF CRLF; > - b->last = b->pos + 7; > + b = tl->buf; > > - cl->buf->last_buf = 0; > + b->tag = (ngx_buf_tag_t) &ngx_http_chunked_filter_module; > + b->temporary = 0; > + b->memory = 1; > + b->last_buf = 1; > + b->pos = (u_char *) CRLF "0" CRLF CRLF; > + b->last = b->pos + 7; > + > + cl->buf->last_buf = 0; > + > + if (size == 0) { > + b->pos += 2; > + } > + } > > *ll = tl; > > - if (size == 0) { > - b->pos += 2; > - } > - > } else if (size > 0) { > tl = ngx_chain_get_free_buf(r->pool, &ctx->free); > if (tl == NULL) { > @@ -230,6 +245,118 @@ ngx_http_chunked_body_filter(ngx_http_re > } > > > +static ngx_chain_t * > +ngx_http_chunked_create_trailers(ngx_http_request_t *r, ngx_uint_t emit_crlf) > +{ > + size_t len; > + ngx_buf_t *b; > + ngx_uint_t i; > + ngx_chain_t *cl; > + ngx_list_part_t *part; > + ngx_table_elt_t *header; > + ngx_http_chunked_filter_ctx_t *ctx; > + > + len = 0; > + > + part = &r->headers_out.trailers.part; > + header = part->elts; > + > + for (i = 0; /* void */; i++) { > + > + if (i >= part->nelts) { > + if (part->next == NULL) { > + break; > + } > + > + part = part->next; > + header = part->elts; > + i = 0; > + } > + > + if (header[i].hash == 0) { > + continue; > + } > + > + len += header[i].key.len + sizeof(": ") - 1 > + + header[i].value.len + sizeof(CRLF) - 1; > + } > + > + if (len == 0) { > + return NULL; See above, generating here an empty last-chunk might be better approach. > + } > + > + if (emit_crlf) { > + len += sizeof(CRLF) - 1; > + } It might worth to always add CRLF, and skip it in the ngx_http_chunked_body_filter() filter if not needed. > + > + len += sizeof("0") - 1 + sizeof(CRLF) - 1 + sizeof(CRLF) - 1; There is no need to write sizeof() so many times, just len += sizeof(CRLF "0" CRLF CRLF) - 1; would be enough. > + > + ctx = ngx_http_get_module_ctx(r, ngx_http_chunked_filter_module); > + > + cl = ngx_chain_get_free_buf(r->pool, &ctx->free); > + if (cl == NULL) { > + return NULL; > + } > + > + b = cl->buf; > + > + b->tag = (ngx_buf_tag_t) &ngx_http_chunked_filter_module; > + b->temporary = 0; > + b->memory = 1; > + b->last_buf = 1; > + > + b->start = ngx_palloc(r->pool, len); > + if (b->start == NULL) { > + return NULL; > + } > + > + b->end = b->last + len; > + b->pos = b->start; > + b->last = b->start; > + > + if (emit_crlf) { > + *b->last++ = CR; *b->last++ = LF; > + } > + > + *b->last++ = '0'; > + *b->last++ = CR; *b->last++ = LF; > + > + part = &r->headers_out.trailers.part; > + header = part->elts; > + > + for (i = 0; /* void */; i++) { > + > + if (i >= part->nelts) { > + if (part->next == NULL) { > + break; > + } > + > + part = part->next; > + header = part->elts; > + i = 0; > + } > + > + if (header[i].hash == 0) { > + continue; > + } > + > + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, > + "http trailer: \"%V: %V\"", > + &header[i].key, &header[i].value); > + > + b->last = ngx_copy(b->last, header[i].key.data, header[i].key.len); > + *b->last++ = ':'; *b->last++ = ' '; > + > + b->last = ngx_copy(b->last, header[i].value.data, header[i].value.len); > + *b->last++ = CR; *b->last++ = LF; > + } > + > + *b->last++ = CR; *b->last++ = LF; > + > + return cl; > +} > + > + > static ngx_int_t > ngx_http_chunked_filter_init(ngx_conf_t *cf) > { > diff -r 716852cce913 -r b0a910ad4941 src/http/ngx_http_core_module.c > --- a/src/http/ngx_http_core_module.c > +++ b/src/http/ngx_http_core_module.c > @@ -2484,6 +2484,13 @@ ngx_http_subrequest(ngx_http_request_t * > return NGX_ERROR; > } > > + if (ngx_list_init(&sr->headers_out.trailers, r->pool, 4, > + sizeof(ngx_table_elt_t)) > + != NGX_OK) > + { > + return NGX_ERROR; > + } > + > cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); > sr->main_conf = cscf->ctx->main_conf; > sr->srv_conf = cscf->ctx->srv_conf; > diff -r 716852cce913 -r b0a910ad4941 src/http/ngx_http_request.c > --- a/src/http/ngx_http_request.c > +++ b/src/http/ngx_http_request.c > @@ -562,6 +562,14 @@ ngx_http_create_request(ngx_connection_t > return NULL; > } > > + if (ngx_list_init(&r->headers_out.trailers, r->pool, 4, > + sizeof(ngx_table_elt_t)) > + != NGX_OK) > + { > + ngx_destroy_pool(r->pool); > + return NULL; > + } > + > r->ctx = ngx_pcalloc(r->pool, sizeof(void *) * ngx_http_max_module); > if (r->ctx == NULL) { > ngx_destroy_pool(r->pool); > diff -r 716852cce913 -r b0a910ad4941 src/http/ngx_http_request.h > --- a/src/http/ngx_http_request.h > +++ b/src/http/ngx_http_request.h > @@ -252,6 +252,7 @@ typedef struct { > > typedef struct { > ngx_list_t headers; > + ngx_list_t trailers; > > ngx_uint_t status; > ngx_str_t status_line; > @@ -514,6 +515,7 @@ struct ngx_http_request_s { > unsigned pipeline:1; > unsigned chunked:1; > unsigned header_only:1; > + unsigned expect_trailers:1; > unsigned keepalive:1; > unsigned lingering_close:1; > unsigned discard_body:1; > diff -r 716852cce913 -r b0a910ad4941 src/http/ngx_http_variables.c > --- a/src/http/ngx_http_variables.c > +++ b/src/http/ngx_http_variables.c > @@ -38,6 +38,8 @@ static ngx_int_t ngx_http_variable_unkno > ngx_http_variable_value_t *v, uintptr_t data); > static ngx_int_t ngx_http_variable_unknown_header_out(ngx_http_request_t *r, > ngx_http_variable_value_t *v, uintptr_t data); > +static ngx_int_t ngx_http_variable_unknown_trailer_out(ngx_http_request_t *r, > + ngx_http_variable_value_t *v, uintptr_t data); > static ngx_int_t ngx_http_variable_request_line(ngx_http_request_t *r, > ngx_http_variable_value_t *v, uintptr_t data); > static ngx_int_t ngx_http_variable_cookie(ngx_http_request_t *r, > @@ -365,6 +367,9 @@ static ngx_http_variable_t ngx_http_cor > { ngx_string("sent_http_"), NULL, ngx_http_variable_unknown_header_out, > 0, NGX_HTTP_VAR_PREFIX, 0 }, > > + { ngx_string("sent_trailer_"), NULL, ngx_http_variable_unknown_trailer_out, > + 0, NGX_HTTP_VAR_PREFIX, 0 }, > + > { ngx_string("cookie_"), NULL, ngx_http_variable_cookie, > 0, NGX_HTTP_VAR_PREFIX, 0 }, > > @@ -934,6 +939,16 @@ ngx_http_variable_unknown_header_out(ngx > } > > > +static ngx_int_t > +ngx_http_variable_unknown_trailer_out(ngx_http_request_t *r, > + ngx_http_variable_value_t *v, uintptr_t data) > +{ > + return ngx_http_variable_unknown_header(v, (ngx_str_t *) data, > + &r->headers_out.trailers.part, > + sizeof("sent_trailer_") - 1); > +} > + > + > ngx_int_t > ngx_http_variable_unknown_header(ngx_http_variable_value_t *v, ngx_str_t *var, > ngx_list_part_t *part, size_t prefix) > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel -- Maxim Dounin http://nginx.org/ From mdounin at mdounin.ru Fri Jun 2 16:20:39 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Fri, 2 Jun 2017 19:20:39 +0300 Subject: [PATCH 2 of 3] Headers filter: add "add_trailer" directive In-Reply-To: References: <5bab17ebe2b1f8ec42cf.1491211932@piotrsikora.sfo.corp.google.com> <20170421174207.GB43932@mdounin.ru> Message-ID: <20170602162039.GV55433@mdounin.ru> Hello! On Fri, Jun 02, 2017 at 02:10:07AM -0700, Piotr Sikora via nginx-devel wrote: > Hey Maxim, > > > This introduces a layering violation between the headers filter > > and the chunked filter. > > I moved trailer generation to headers filter, let me know if that works for you. > > Unfortunately, because of that change, trailers are evaluated earlier > in the process, and some variables from body filters that run after > headers filter (like $gzip_ratio) are not usable anymore. This looks better. If there is a need to evaluate trailers later, a separate module could be added to a later stage, like it is done with ngx_http_range_header_filter vs. ngx_http_range_body_filter. [...] > > Predicting conditions which will result in header-only response > > looks really fragile. As well as checks for chunked transfer > > encoding. > > Sending "Trailer" header in responses that cannot have response body > is known to result in interoperability issues, so I'd rather leave it, > see: > https://github.com/nodejs/node/issues/2842 So may be not generating "Trailer" is a way to go? It doesn't seem to be really needed. (Just for the record, with the first patch fixed to avoid using chunked with HTTP/1.0, the "Trailer" header is expectedly still added with HTTP/1.0. This confirms the idea that the approach choosen is somewhat fragile.) [...] > > Note that at this point we do not know if the trailer in question > > is going to be present in the particular response or not, as > > trailers with empty values are not returned (and this is often > > used to only return some headers in some responses, at least with > > add_header). > > But we know that it might be present. > > "Trailer" header is an indicator. The question is: if we need this indicator to be sent to a particular client. For example, if you are using trailers to pass additional logging information to your own frontends, and use something like geo $mine { 127.0.0.1/8 1; } map $mine $x_request_time { 1 $request_time; } add_trailer X-Response-Time $x_request_time; to send the information to your frontends, but not other clients, you probably don't want the X-Response-Time trailer to be indicated to other clients. > > It might be a better idea to avoid generating the > > "Trailer" header automatically and let users to control this with > > add_header instead, and/or introduce an option to control this. > > That sounds like a terrible idea, which will result in always missing > "Trailer" header. Acutally I don't see how it's a problem, given that "Trailer" is not something required. Moreover, it seems to be not needed or even harmful in most of the use cases discussed. [...] -- Maxim Dounin http://nginx.org/ From piotrsikora at google.com Sat Jun 3 03:32:26 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Fri, 2 Jun 2017 20:32:26 -0700 Subject: [PATCH 1 of 3] HTTP: add support for trailers in HTTP responses In-Reply-To: <20170602123035.GT55433@mdounin.ru> References: <8af81a0d66c0f69bcf50.1491211931@piotrsikora.sfo.corp.google.com> <20170421174201.GA43932@mdounin.ru> <20170427184623.GD43932@mdounin.ru> <20170503140018.GN43932@mdounin.ru> <20170602123035.GT55433@mdounin.ru> Message-ID: Hey Maxim, > I see two problems here: > > a. There may be use cases when forcing chunked encoding is not > desired, but emitting trailers if it is used still makes sense. Like what, exactly? Also, gzip module forces chunked encoding and it works just fine. I don't see why are you making this such a big deal out of this. > b. Nothing stops modules from changing r->expect_trailers when the > response header was already sent and it is already too late to > switch to chunked transfer encoding. Moreover, this will > naturally happen with any module which is simply following the > requirement to set r->expect_trailers to 1 as in your commit log. Same is true for majority of ngx_http_request_t fields, i.e. bad things can happen if some module misuses them. > So (a) makes (2) excessively limiting, and (b) makes it useless. I disagree. Removing this check results in less consistent behavior and doesn't solve any real problems. Having said that, I'm going to remove it, since I don't want to spend another few months arguing about this... Best regards, Piotr Sikora From piotrsikora at google.com Sat Jun 3 03:32:31 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Fri, 2 Jun 2017 20:32:31 -0700 Subject: [PATCH 1 of 3] Added support for trailers in HTTP responses In-Reply-To: <20170602153607.GU55433@mdounin.ru> References: <20170602153607.GU55433@mdounin.ru> Message-ID: Hey Maxim, > Note: the "TE: trailers" requirement is no longer present in the > code. Good catch, thanks! > This code results in using chunked encoding for HTTP/1.0 when > trailers are expected. Such behaviour is explicitly forbidden by > the HTTP/1.1 specification, and will very likely result in > problems (we've seen lots of such problems with broken backends > when there were no HTTP/1.1 support in the proxy module). Oops, this regression is a result of removal of r->accept_trailers, which previously disallowed trailers in HTTP/1.0 requests. > Something like this should be a better solution: > > if (r->headers_out.content_length_n == -1 > || r->expect_trailers) > { > clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); > > if (r->http_version >= NGX_HTTP_VERSION_11 > && clcf->chunked_transfer_encoding) > { > if (r->expect_trailers) { > ngx_http_clear_content_length(r); > } > > r->chunked = 1; > > ctx = ngx_pcalloc(r->pool, > sizeof(ngx_http_chunked_filter_ctx_t)); > if (ctx == NULL) { > return NGX_ERROR; > } > > ngx_http_set_ctx(r, ctx, ngx_http_chunked_filter_module); > > } else if (r->headers_out.content_length_n == -1) { > r->keepalive = 0; > } > } Applied with small style changes. > Instead of providing two separate code paths for "with trailer > headers" and "without trailer headers", it might be better and > more readable to generate last-chunk in one function regardless of > whether trailer headers are present or not. > > It will also make error handling better: as of now, an allocation > error in ngx_http_chunked_create_trailers() will result in "no > trailers" code path being tried instead of returning an > unconditional error. Done. > There is no need to write sizeof() so many times, just > > len += sizeof(CRLF "0" CRLF CRLF) - 1; > > would be enough. Done. Best regards, Piotr Sikora From piotrsikora at google.com Sat Jun 3 03:33:45 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Fri, 02 Jun 2017 20:33:45 -0700 Subject: [PATCH 1 of 3] Added support for trailers in HTTP responses In-Reply-To: References: Message-ID: <41c09a2fd90410e25ad8.1496460825@piotrsikora.sfo.corp.google.com> # HG changeset patch # User Piotr Sikora # Date 1490351854 25200 # Fri Mar 24 03:37:34 2017 -0700 # Node ID 41c09a2fd90410e25ad8515793bd48028001c954 # Parent 716852cce9136d977b81a2d1b8b6f9fbca0dce49 Added support for trailers in HTTP responses. Example: ngx_table_elt_t *h; h = ngx_list_push(&r->headers_out.trailers); if (h == NULL) { return NGX_ERROR; } ngx_str_set(&h->key, "Fun"); ngx_str_set(&h->value, "with trailers"); h->hash = ngx_hash_key_lc(h->key.data, h->key.len); The code above adds "Fun: with trailers" trailer to the response. Modules that want to emit trailers must set r->expect_trailers = 1 in header filter, otherwise they might not be emitted for HTTP/1.1 responses that aren't already chunked. This change also adds $sent_trailer_* variables. Signed-off-by: Piotr Sikora diff -r 716852cce913 -r 41c09a2fd904 src/http/modules/ngx_http_chunked_filter_module.c --- a/src/http/modules/ngx_http_chunked_filter_module.c +++ b/src/http/modules/ngx_http_chunked_filter_module.c @@ -17,6 +17,7 @@ typedef struct { static ngx_int_t ngx_http_chunked_filter_init(ngx_conf_t *cf); +static ngx_chain_t *ngx_http_chunked_create_trailers(ngx_http_request_t *r); static ngx_http_module_t ngx_http_chunked_filter_module_ctx = { @@ -69,27 +70,28 @@ ngx_http_chunked_header_filter(ngx_http_ return ngx_http_next_header_filter(r); } - if (r->headers_out.content_length_n == -1) { - if (r->http_version < NGX_HTTP_VERSION_11) { + if (r->headers_out.content_length_n == -1 || r->expect_trailers) { + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (r->http_version >= NGX_HTTP_VERSION_11 + && clcf->chunked_transfer_encoding) + { + if (r->expect_trailers) { + ngx_http_clear_content_length(r); + } + + r->chunked = 1; + + ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_chunked_filter_ctx_t)); + if (ctx == NULL) { + return NGX_ERROR; + } + + ngx_http_set_ctx(r, ctx, ngx_http_chunked_filter_module); + + } else if (r->headers_out.content_length_n == -1) { r->keepalive = 0; - - } else { - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - - if (clcf->chunked_transfer_encoding) { - r->chunked = 1; - - ctx = ngx_pcalloc(r->pool, - sizeof(ngx_http_chunked_filter_ctx_t)); - if (ctx == NULL) { - return NGX_ERROR; - } - - ngx_http_set_ctx(r, ctx, ngx_http_chunked_filter_module); - - } else { - r->keepalive = 0; - } } } @@ -179,26 +181,17 @@ ngx_http_chunked_body_filter(ngx_http_re } if (cl->buf->last_buf) { - tl = ngx_chain_get_free_buf(r->pool, &ctx->free); + tl = ngx_http_chunked_create_trailers(r); if (tl == NULL) { return NGX_ERROR; } - b = tl->buf; - - b->tag = (ngx_buf_tag_t) &ngx_http_chunked_filter_module; - b->temporary = 0; - b->memory = 1; - b->last_buf = 1; - b->pos = (u_char *) CRLF "0" CRLF CRLF; - b->last = b->pos + 7; - cl->buf->last_buf = 0; *ll = tl; if (size == 0) { - b->pos += 2; + tl->buf->pos += 2; } } else if (size > 0) { @@ -230,6 +223,105 @@ ngx_http_chunked_body_filter(ngx_http_re } +static ngx_chain_t * +ngx_http_chunked_create_trailers(ngx_http_request_t *r) +{ + size_t len; + ngx_buf_t *b; + ngx_uint_t i; + ngx_chain_t *cl; + ngx_list_part_t *part; + ngx_table_elt_t *header; + ngx_http_chunked_filter_ctx_t *ctx; + + len = sizeof(CRLF "0" CRLF CRLF) - 1; + + part = &r->headers_out.trailers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].hash == 0) { + continue; + } + + len += header[i].key.len + sizeof(": ") - 1 + + header[i].value.len + sizeof(CRLF) - 1; + } + + ctx = ngx_http_get_module_ctx(r, ngx_http_chunked_filter_module); + + cl = ngx_chain_get_free_buf(r->pool, &ctx->free); + if (cl == NULL) { + return NULL; + } + + b = cl->buf; + + b->tag = (ngx_buf_tag_t) &ngx_http_chunked_filter_module; + b->temporary = 0; + b->memory = 1; + b->last_buf = 1; + + b->start = ngx_palloc(r->pool, len); + if (b->start == NULL) { + return NULL; + } + + b->end = b->last + len; + b->pos = b->start; + b->last = b->start; + + *b->last++ = CR; *b->last++ = LF; + *b->last++ = '0'; + *b->last++ = CR; *b->last++ = LF; + + part = &r->headers_out.trailers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].hash == 0) { + continue; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http trailer: \"%V: %V\"", + &header[i].key, &header[i].value); + + b->last = ngx_copy(b->last, header[i].key.data, header[i].key.len); + *b->last++ = ':'; *b->last++ = ' '; + + b->last = ngx_copy(b->last, header[i].value.data, header[i].value.len); + *b->last++ = CR; *b->last++ = LF; + } + + *b->last++ = CR; *b->last++ = LF; + + return cl; +} + + static ngx_int_t ngx_http_chunked_filter_init(ngx_conf_t *cf) { diff -r 716852cce913 -r 41c09a2fd904 src/http/ngx_http_core_module.c --- a/src/http/ngx_http_core_module.c +++ b/src/http/ngx_http_core_module.c @@ -2484,6 +2484,13 @@ ngx_http_subrequest(ngx_http_request_t * return NGX_ERROR; } + if (ngx_list_init(&sr->headers_out.trailers, r->pool, 4, + sizeof(ngx_table_elt_t)) + != NGX_OK) + { + return NGX_ERROR; + } + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); sr->main_conf = cscf->ctx->main_conf; sr->srv_conf = cscf->ctx->srv_conf; diff -r 716852cce913 -r 41c09a2fd904 src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -562,6 +562,14 @@ ngx_http_create_request(ngx_connection_t return NULL; } + if (ngx_list_init(&r->headers_out.trailers, r->pool, 4, + sizeof(ngx_table_elt_t)) + != NGX_OK) + { + ngx_destroy_pool(r->pool); + return NULL; + } + r->ctx = ngx_pcalloc(r->pool, sizeof(void *) * ngx_http_max_module); if (r->ctx == NULL) { ngx_destroy_pool(r->pool); diff -r 716852cce913 -r 41c09a2fd904 src/http/ngx_http_request.h --- a/src/http/ngx_http_request.h +++ b/src/http/ngx_http_request.h @@ -252,6 +252,7 @@ typedef struct { typedef struct { ngx_list_t headers; + ngx_list_t trailers; ngx_uint_t status; ngx_str_t status_line; @@ -514,6 +515,7 @@ struct ngx_http_request_s { unsigned pipeline:1; unsigned chunked:1; unsigned header_only:1; + unsigned expect_trailers:1; unsigned keepalive:1; unsigned lingering_close:1; unsigned discard_body:1; diff -r 716852cce913 -r 41c09a2fd904 src/http/ngx_http_variables.c --- a/src/http/ngx_http_variables.c +++ b/src/http/ngx_http_variables.c @@ -38,6 +38,8 @@ static ngx_int_t ngx_http_variable_unkno ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_unknown_header_out(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_unknown_trailer_out(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_request_line(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_cookie(ngx_http_request_t *r, @@ -365,6 +367,9 @@ static ngx_http_variable_t ngx_http_cor { ngx_string("sent_http_"), NULL, ngx_http_variable_unknown_header_out, 0, NGX_HTTP_VAR_PREFIX, 0 }, + { ngx_string("sent_trailer_"), NULL, ngx_http_variable_unknown_trailer_out, + 0, NGX_HTTP_VAR_PREFIX, 0 }, + { ngx_string("cookie_"), NULL, ngx_http_variable_cookie, 0, NGX_HTTP_VAR_PREFIX, 0 }, @@ -934,6 +939,16 @@ ngx_http_variable_unknown_header_out(ngx } +static ngx_int_t +ngx_http_variable_unknown_trailer_out(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + return ngx_http_variable_unknown_header(v, (ngx_str_t *) data, + &r->headers_out.trailers.part, + sizeof("sent_trailer_") - 1); +} + + ngx_int_t ngx_http_variable_unknown_header(ngx_http_variable_value_t *v, ngx_str_t *var, ngx_list_part_t *part, size_t prefix) From piotrsikora at google.com Sat Jun 3 03:33:46 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Fri, 02 Jun 2017 20:33:46 -0700 Subject: [PATCH 2 of 3] HTTP/2: added support for trailers in HTTP responses In-Reply-To: References: Message-ID: <8d74ff6c2015180f5c1f.1496460826@piotrsikora.sfo.corp.google.com> # HG changeset patch # User Piotr Sikora # Date 1493191954 25200 # Wed Apr 26 00:32:34 2017 -0700 # Node ID 8d74ff6c2015180f5c1f399f492214d7d0a52b3f # Parent 41c09a2fd90410e25ad8515793bd48028001c954 HTTP/2: added support for trailers in HTTP responses. Signed-off-by: Piotr Sikora diff -r 41c09a2fd904 -r 8d74ff6c2015 src/http/v2/ngx_http_v2_filter_module.c --- a/src/http/v2/ngx_http_v2_filter_module.c +++ b/src/http/v2/ngx_http_v2_filter_module.c @@ -50,13 +50,17 @@ #define NGX_HTTP_V2_SERVER_INDEX 54 #define NGX_HTTP_V2_VARY_INDEX 59 +#define NGX_HTTP_V2_FRAME_ERROR (ngx_http_v2_out_frame_t *) -1 + static u_char *ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len, u_char *tmp, ngx_uint_t lower); static u_char *ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix, ngx_uint_t value); static ngx_http_v2_out_frame_t *ngx_http_v2_create_headers_frame( - ngx_http_request_t *r, u_char *pos, u_char *end); + ngx_http_request_t *r, u_char *pos, u_char *end, ngx_uint_t fin); +static ngx_http_v2_out_frame_t *ngx_http_v2_create_trailers_frame( + ngx_http_request_t *r); static ngx_chain_t *ngx_http_v2_send_chain(ngx_connection_t *fc, ngx_chain_t *in, off_t limit); @@ -612,7 +616,7 @@ ngx_http_v2_header_filter(ngx_http_reque header[i].value.len, tmp); } - frame = ngx_http_v2_create_headers_frame(r, start, pos); + frame = ngx_http_v2_create_headers_frame(r, start, pos, r->header_only); if (frame == NULL) { return NGX_ERROR; } @@ -636,6 +640,126 @@ ngx_http_v2_header_filter(ngx_http_reque } +static ngx_http_v2_out_frame_t * +ngx_http_v2_create_trailers_frame(ngx_http_request_t *r) +{ + u_char *pos, *start, *tmp; + size_t len, tmp_len; + ngx_uint_t i; + ngx_list_part_t *part; + ngx_table_elt_t *header; + ngx_http_v2_out_frame_t *frame; + + len = 0; + tmp_len = 0; + + part = &r->headers_out.trailers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].hash == 0) { + continue; + } + + if (header[i].key.len > NGX_HTTP_V2_MAX_FIELD) { + ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, + "too long response trailer name: \"%V\"", + &header[i].key); + + return NGX_HTTP_V2_FRAME_ERROR; + } + + if (header[i].value.len > NGX_HTTP_V2_MAX_FIELD) { + ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, + "too long response trailer value: \"%V: %V\"", + &header[i].key, &header[i].value); + + return NGX_HTTP_V2_FRAME_ERROR; + } + + len += 1 + NGX_HTTP_V2_INT_OCTETS + header[i].key.len + + NGX_HTTP_V2_INT_OCTETS + header[i].value.len; + + if (header[i].key.len > tmp_len) { + tmp_len = header[i].key.len; + } + + if (header[i].value.len > tmp_len) { + tmp_len = header[i].value.len; + } + } + + if (len == 0) { + return NULL; + } + + tmp = ngx_palloc(r->pool, tmp_len); + pos = ngx_pnalloc(r->pool, len); + + if (pos == NULL || tmp == NULL) { + return NGX_HTTP_V2_FRAME_ERROR; + } + + start = pos; + + part = &r->headers_out.trailers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].hash == 0) { + continue; + } + +#if (NGX_DEBUG) + if (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP) { + ngx_strlow(tmp, header[i].key.data, header[i].key.len); + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http2 output trailer: \"%*s: %V\"", + header[i].key.len, tmp, &header[i].value); + } +#endif + + *pos++ = 0; + + pos = ngx_http_v2_write_name(pos, header[i].key.data, + header[i].key.len, tmp); + + pos = ngx_http_v2_write_value(pos, header[i].value.data, + header[i].value.len, tmp); + } + + frame = ngx_http_v2_create_headers_frame(r, start, pos, 1); + if (frame == NULL) { + return NGX_HTTP_V2_FRAME_ERROR; + } + + return frame; +} + + static u_char * ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len, u_char *tmp, ngx_uint_t lower) @@ -686,7 +810,7 @@ ngx_http_v2_write_int(u_char *pos, ngx_u static ngx_http_v2_out_frame_t * ngx_http_v2_create_headers_frame(ngx_http_request_t *r, u_char *pos, - u_char *end) + u_char *end, ngx_uint_t fin) { u_char type, flags; size_t rest, frame_size; @@ -707,12 +831,12 @@ ngx_http_v2_create_headers_frame(ngx_htt frame->stream = stream; frame->length = rest; frame->blocked = 1; - frame->fin = r->header_only; + frame->fin = fin; ll = &frame->first; type = NGX_HTTP_V2_HEADERS_FRAME; - flags = r->header_only ? NGX_HTTP_V2_END_STREAM_FLAG : NGX_HTTP_V2_NO_FLAG; + flags = fin ? NGX_HTTP_V2_END_STREAM_FLAG : NGX_HTTP_V2_NO_FLAG; frame_size = stream->connection->frame_size; for ( ;; ) { @@ -776,7 +900,7 @@ ngx_http_v2_create_headers_frame(ngx_htt continue; } - b->last_buf = r->header_only; + b->last_buf = fin; cl->next = NULL; frame->last = cl; @@ -798,7 +922,7 @@ ngx_http_v2_send_chain(ngx_connection_t ngx_http_request_t *r; ngx_http_v2_stream_t *stream; ngx_http_v2_loc_conf_t *h2lcf; - ngx_http_v2_out_frame_t *frame; + ngx_http_v2_out_frame_t *frame, *trailers; ngx_http_v2_connection_t *h2c; r = fc->data; @@ -872,6 +996,8 @@ ngx_http_v2_send_chain(ngx_connection_t frame_size = (h2lcf->chunk_size < h2c->frame_size) ? h2lcf->chunk_size : h2c->frame_size; + trailers = NULL; + #if (NGX_SUPPRESS_WARN) cl = NULL; #endif @@ -934,17 +1060,36 @@ ngx_http_v2_send_chain(ngx_connection_t size -= rest; } - frame = ngx_http_v2_filter_get_data_frame(stream, frame_size, out, cl); - if (frame == NULL) { - return NGX_CHAIN_ERROR; + if (cl->buf->last_buf) { + trailers = ngx_http_v2_create_trailers_frame(r); + if (trailers == NGX_HTTP_V2_FRAME_ERROR) { + return NGX_CHAIN_ERROR; + } + + if (trailers) { + cl->buf->last_buf = 0; + } } - ngx_http_v2_queue_frame(h2c, frame); + if (frame_size || cl->buf->last_buf) { + frame = ngx_http_v2_filter_get_data_frame(stream, frame_size, out, + cl); + if (frame == NULL) { + return NGX_CHAIN_ERROR; + } - h2c->send_window -= frame_size; + ngx_http_v2_queue_frame(h2c, frame); - stream->send_window -= frame_size; - stream->queued++; + h2c->send_window -= frame_size; + + stream->send_window -= frame_size; + stream->queued++; + } + + if (trailers) { + ngx_http_v2_queue_frame(h2c, trailers); + stream->queued++; + } if (in == NULL) { break; From piotrsikora at google.com Sat Jun 3 03:33:47 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Fri, 02 Jun 2017 20:33:47 -0700 Subject: [PATCH 3 of 3] Headers filter: added "add_trailer" directive In-Reply-To: References: Message-ID: # HG changeset patch # User Piotr Sikora # Date 1490351854 25200 # Fri Mar 24 03:37:34 2017 -0700 # Node ID acdc80c0d4ef8aa2519e2882ff1a3bd4a316ad81 # Parent 8d74ff6c2015180f5c1f399f492214d7d0a52b3f Headers filter: added "add_trailer" directive. Trailers added using this directive are evaluated after response body is processed by output filters (but before it's written to the wire), so it's possible to use variables calculated from the response body as the trailer value. Signed-off-by: Piotr Sikora diff -r 8d74ff6c2015 -r acdc80c0d4ef src/http/modules/ngx_http_headers_filter_module.c --- a/src/http/modules/ngx_http_headers_filter_module.c +++ b/src/http/modules/ngx_http_headers_filter_module.c @@ -48,6 +48,7 @@ typedef struct { time_t expires_time; ngx_http_complex_value_t *expires_value; ngx_array_t *headers; + ngx_array_t *trailers; } ngx_http_headers_conf_t; @@ -72,6 +73,8 @@ static char *ngx_http_headers_expires(ng void *conf); static char *ngx_http_headers_add(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_http_headers_add_trailer(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); static ngx_http_set_header_t ngx_http_set_headers[] = { @@ -108,6 +111,14 @@ static ngx_command_t ngx_http_headers_f 0, NULL }, + { ngx_string("add_trailer"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF + |NGX_CONF_TAKE23, + ngx_http_headers_add_trailer, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + ngx_null_command }; @@ -144,6 +155,7 @@ ngx_module_t ngx_http_headers_filter_mo static ngx_http_output_header_filter_pt ngx_http_next_header_filter; +static ngx_http_output_body_filter_pt ngx_http_next_body_filter; static ngx_int_t @@ -154,10 +166,15 @@ ngx_http_headers_filter(ngx_http_request ngx_http_header_val_t *h; ngx_http_headers_conf_t *conf; + if (r != r->main) { + return ngx_http_next_header_filter(r); + } + conf = ngx_http_get_module_loc_conf(r, ngx_http_headers_filter_module); - if ((conf->expires == NGX_HTTP_EXPIRES_OFF && conf->headers == NULL) - || r != r->main) + if (conf->expires == NGX_HTTP_EXPIRES_OFF + && conf->headers == NULL + && conf->trailers == NULL) { return ngx_http_next_header_filter(r); } @@ -206,11 +223,103 @@ ngx_http_headers_filter(ngx_http_request } } + if (conf->trailers) { + h = conf->trailers->elts; + for (i = 0; i < conf->trailers->nelts; i++) { + + if (!safe_status && !h[i].always) { + continue; + } + + if (h[i].value.value.len) { + r->expect_trailers = 1; + break; + } + } + } + return ngx_http_next_header_filter(r); } static ngx_int_t +ngx_http_trailers_filter(ngx_http_request_t *r, ngx_chain_t *in) +{ + ngx_str_t value; + ngx_uint_t i, safe_status; + ngx_chain_t *cl; + ngx_table_elt_t *t; + ngx_http_header_val_t *h; + ngx_http_headers_conf_t *conf; + + conf = ngx_http_get_module_loc_conf(r, ngx_http_headers_filter_module); + + if (in == NULL + || conf->trailers == NULL + || !r->expect_trailers + || r->header_only) + { + return ngx_http_next_body_filter(r, in); + } + + for (cl = in; cl; cl = cl->next) { + if (cl->buf->last_buf) { + break; + } + } + + if (cl == NULL) { + return ngx_http_next_body_filter(r, in); + } + + switch (r->headers_out.status) { + + case NGX_HTTP_OK: + case NGX_HTTP_CREATED: + case NGX_HTTP_NO_CONTENT: + case NGX_HTTP_PARTIAL_CONTENT: + case NGX_HTTP_MOVED_PERMANENTLY: + case NGX_HTTP_MOVED_TEMPORARILY: + case NGX_HTTP_SEE_OTHER: + case NGX_HTTP_NOT_MODIFIED: + case NGX_HTTP_TEMPORARY_REDIRECT: + case NGX_HTTP_PERMANENT_REDIRECT: + safe_status = 1; + break; + + default: + safe_status = 0; + break; + } + + h = conf->trailers->elts; + for (i = 0; i < conf->trailers->nelts; i++) { + + if (!safe_status && !h[i].always) { + continue; + } + + if (ngx_http_complex_value(r, &h[i].value, &value) != NGX_OK) { + return NGX_ERROR; + } + + if (value.len) { + t = ngx_list_push(&r->headers_out.trailers); + if (t == NULL) { + return NGX_ERROR; + } + + t->key = h[i].key; + t->value = value; + t->hash = 1; + } + } + + return ngx_http_next_body_filter(r, in); +} + + +static ngx_int_t ngx_http_set_expires(ngx_http_request_t *r, ngx_http_headers_conf_t *conf) { char *err; @@ -557,6 +666,7 @@ ngx_http_headers_create_conf(ngx_conf_t * set by ngx_pcalloc(): * * conf->headers = NULL; + * conf->trailers = NULL; * conf->expires_time = 0; * conf->expires_value = NULL; */ @@ -587,6 +697,10 @@ ngx_http_headers_merge_conf(ngx_conf_t * conf->headers = prev->headers; } + if (conf->trailers == NULL) { + conf->trailers = prev->trailers; + } + return NGX_CONF_OK; } @@ -597,6 +711,9 @@ ngx_http_headers_filter_init(ngx_conf_t ngx_http_next_header_filter = ngx_http_top_header_filter; ngx_http_top_header_filter = ngx_http_headers_filter; + ngx_http_next_body_filter = ngx_http_top_body_filter; + ngx_http_top_body_filter = ngx_http_trailers_filter; + return NGX_OK; } @@ -741,3 +858,63 @@ ngx_http_headers_add(ngx_conf_t *cf, ngx return NGX_CONF_OK; } + + +static char * +ngx_http_headers_add_trailer(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_headers_conf_t *hcf = conf; + + ngx_str_t *value; + ngx_http_header_val_t *hv; + ngx_http_compile_complex_value_t ccv; + + value = cf->args->elts; + + if (hcf->trailers == NULL) { + hcf->trailers = ngx_array_create(cf->pool, 1, + sizeof(ngx_http_header_val_t)); + if (hcf->trailers == NULL) { + return NGX_CONF_ERROR; + } + } + + hv = ngx_array_push(hcf->trailers); + if (hv == NULL) { + return NGX_CONF_ERROR; + } + + hv->key = value[1]; + hv->handler = NULL; + hv->offset = 0; + hv->always = 0; + + if (value[2].len == 0) { + ngx_memzero(&hv->value, sizeof(ngx_http_complex_value_t)); + + } else { + ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &value[2]; + ccv.complex_value = &hv->value; + + if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + } + + if (cf->args->nelts == 3) { + return NGX_CONF_OK; + } + + if (ngx_strcmp(value[3].data, "always") != 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[3]); + return NGX_CONF_ERROR; + } + + hv->always = 1; + + return NGX_CONF_OK; +} From piotrsikora at google.com Sat Jun 3 03:32:35 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Fri, 2 Jun 2017 20:32:35 -0700 Subject: [PATCH 2 of 3] Headers filter: add "add_trailer" directive In-Reply-To: <20170602162039.GV55433@mdounin.ru> References: <5bab17ebe2b1f8ec42cf.1491211932@piotrsikora.sfo.corp.google.com> <20170421174207.GB43932@mdounin.ru> <20170602162039.GV55433@mdounin.ru> Message-ID: Hey Maxim, > (Just for the record, with the first patch fixed to avoid using > chunked with HTTP/1.0, the "Trailer" header is expectedly still > added with HTTP/1.0. This confirms the idea that the approach > choosen is somewhat fragile.) It confirms no such thing. The only thing it confirms is that making major changes during code review for a code that was written almost a year ago is an error-prone process. > The question is: if we need this indicator to be sent to a > particular client. > > For example, if you are using trailers to pass additional logging > information to your own frontends, and use something like > > geo $mine { > 127.0.0.1/8 1; > } > > map $mine $x_request_time { > 1 $request_time; > } > > add_trailer X-Response-Time $x_request_time; > > to send the information to your frontends, but not other clients, > you probably don't want the X-Response-Time trailer to be > indicated to other clients. In such setup, clients would probably talk to frontends. > Acutally I don't see how it's a problem, given that "Trailer" is > not something required. Moreover, it seems to be not needed or > even harmful in most of the use cases discussed. I don't think it's harmful, but I'm not aware of any clients that _require_ "Trailer" header, so I'm going to skip this whole discussion and just remove it. You might always re-add it later if needed. Best regards, Piotr Sikora From borodin at octonica.com Sat Jun 3 06:00:44 2017 From: borodin at octonica.com (Andrew Borodin) Date: Sat, 3 Jun 2017 11:00:44 +0500 Subject: Use primes for hashtable size In-Reply-To: <20170602114603.GR55433@mdounin.ru> References: <20170530130137.GT55433@mdounin.ru> <20170601173951.GO55433@mdounin.ru> <20170602114603.GR55433@mdounin.ru> Message-ID: Hello world! 2017-06-02 16:46 GMT+05:00 Maxim Dounin : > Hello! > > On Fri, Jun 02, 2017 at 10:56:31AM +1000, Mathew Heard wrote: > >> If this actually yields a decrease in start time while not introducing >> other effects we would use it. Our start time of a couple minutes is >> annoying at times. > > Do you have any details of what contributes to the start time in > your case? > > In general, nginx trades start time to faster operation once > started, and trying to build minimal hash is an example of this > practice. If it results in unacceptable start times we certainly > should optimize it, though I don't think I've seen such cases in > my practice. > > Most time-consuming things during start I've seen so far are: > > - multiple DNS names in the configuration and slow system > resolver; > > - multiple SSL certificates; > > - very large geo{} maps. Mathew could you plz make some profiling? As I see it: 1. htop during nginx start, if you see spikes of CPU proceed to 2, else end; 2. perf top during nginx start, if you see ngx_hash_init proceed to 3, else send here top slow functions 3. measure ngxinx start time with and without this patch Maybe, we could crunch some of these 2 minutes of startup. Thank you for help. Best regards, Andrey Borodin, Octonica. From piotrsikora at google.com Sun Jun 4 03:03:57 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Sat, 03 Jun 2017 20:03:57 -0700 Subject: [PATCH] Proxy: always emit "Host" header first Message-ID: # HG changeset patch # User Piotr Sikora # Date 1489618489 25200 # Wed Mar 15 15:54:49 2017 -0700 # Node ID e472b23fdc387943ea90fb2f0ae415d9d104edc7 # Parent 716852cce9136d977b81a2d1b8b6f9fbca0dce49 Proxy: always emit "Host" header first. Signed-off-by: Piotr Sikora diff -r 716852cce913 -r e472b23fdc38 src/http/modules/ngx_http_proxy_module.c --- a/src/http/modules/ngx_http_proxy_module.c +++ b/src/http/modules/ngx_http_proxy_module.c @@ -3412,7 +3412,7 @@ ngx_http_proxy_init_headers(ngx_conf_t * uintptr_t *code; ngx_uint_t i; ngx_array_t headers_names, headers_merged; - ngx_keyval_t *src, *s, *h; + ngx_keyval_t *host, *src, *s, *h; ngx_hash_key_t *hk; ngx_hash_init_t hash; ngx_http_script_compile_t sc; @@ -3444,11 +3444,33 @@ ngx_http_proxy_init_headers(ngx_conf_t * return NGX_ERROR; } + h = default_headers; + + if (h->key.len != sizeof("Host") - 1 + || ngx_strcasecmp(h->key.data, (u_char *) "Host") != 0) + { + return NGX_ERROR; + } + + host = ngx_array_push(&headers_merged); + if (host == NULL) { + return NGX_ERROR; + } + + *host = *h++; + if (conf->headers_source) { src = conf->headers_source->elts; for (i = 0; i < conf->headers_source->nelts; i++) { + if (src[i].key.len == sizeof("Host") - 1 + && ngx_strcasecmp(src[i].key.data, (u_char *) "Host") == 0) + { + *host = src[i]; + continue; + } + s = ngx_array_push(&headers_merged); if (s == NULL) { return NGX_ERROR; @@ -3458,8 +3480,6 @@ ngx_http_proxy_init_headers(ngx_conf_t * } } - h = default_headers; - while (h->key.len) { src = headers_merged.elts; From piotrsikora at google.com Sun Jun 4 03:04:00 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Sat, 03 Jun 2017 20:04:00 -0700 Subject: [PATCH] Proxy: split configured header names and values Message-ID: # HG changeset patch # User Piotr Sikora # Date 1489618535 25200 # Wed Mar 15 15:55:35 2017 -0700 # Node ID ff79d6887fc92d0344eac3e87339583265241e36 # Parent 716852cce9136d977b81a2d1b8b6f9fbca0dce49 Proxy: split configured header names and values. Previously, each configured header was represented in one of two ways, depending on whether or not its value included any variables. If the value didn't include any variables, then it would be represented as as a single script that contained complete header line with HTTP/1.1 delimiters, i.e.: "Header: value\r\n" But if the value included any variables, then it would be represented as a series of three scripts: first contained header name and the ":" delimiter, second evaluated to header value, and third contained only "\r\n", i.e.: "Header:" "$value" "\r\n" This commit changes that, so that each configured header is represented as a series of two scripts: first contains only header name, and second contains (or evaluates to) only header value, i.e.: "Header" "$value" or "Header" "value" This not only makes things more consistent, but also allows header name and value to be accessed separately. Signed-off-by: Piotr Sikora diff -r 716852cce913 -r ff79d6887fc9 src/http/modules/ngx_http_proxy_module.c --- a/src/http/modules/ngx_http_proxy_module.c +++ b/src/http/modules/ngx_http_proxy_module.c @@ -1144,6 +1144,7 @@ static ngx_int_t ngx_http_proxy_create_request(ngx_http_request_t *r) { size_t len, uri_len, loc_len, body_len; + size_t key_len, val_len; uintptr_t escape; ngx_buf_t *b; ngx_str_t method; @@ -1258,10 +1259,17 @@ ngx_http_proxy_create_request(ngx_http_r le.flushed = 1; while (*(uintptr_t *) le.ip) { - while (*(uintptr_t *) le.ip) { + lcode = *(ngx_http_script_len_code_pt *) le.ip; + key_len = lcode(&le); + + for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) { lcode = *(ngx_http_script_len_code_pt *) le.ip; - len += lcode(&le); } + + if (val_len) { + len += key_len + sizeof(": ") - 1 + val_len + sizeof(CRLF) - 1; + } + le.ip += sizeof(uintptr_t); } @@ -1363,28 +1371,32 @@ ngx_http_proxy_create_request(ngx_http_r while (*(uintptr_t *) le.ip) { lcode = *(ngx_http_script_len_code_pt *) le.ip; - - /* skip the header line name length */ (void) lcode(&le); - if (*(ngx_http_script_len_code_pt *) le.ip) { - - for (len = 0; *(uintptr_t *) le.ip; len += lcode(&le)) { - lcode = *(ngx_http_script_len_code_pt *) le.ip; - } - - e.skip = (len == sizeof(CRLF) - 1) ? 1 : 0; - - } else { - e.skip = 0; + for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) { + lcode = *(ngx_http_script_len_code_pt *) le.ip; } le.ip += sizeof(uintptr_t); + e.skip = (val_len == 0) ? 1 : 0; + + code = *(ngx_http_script_code_pt *) e.ip; + code((ngx_http_script_engine_t *) &e); + + if (!e.skip) { + *e.pos++ = ':'; *e.pos++ = ' '; + } + while (*(uintptr_t *) e.ip) { code = *(ngx_http_script_code_pt *) e.ip; code((ngx_http_script_engine_t *) &e); } + + if (!e.skip) { + *e.pos++ = CR; *e.pos++ = LF; + } + e.ip += sizeof(uintptr_t); } @@ -3498,6 +3510,30 @@ ngx_http_proxy_init_headers(ngx_conf_t * continue; } + copy = ngx_array_push_n(headers->lengths, + sizeof(ngx_http_script_copy_code_t)); + if (copy == NULL) { + return NGX_ERROR; + } + + copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code; + copy->len = src[i].key.len; + + size = (sizeof(ngx_http_script_copy_code_t) + + src[i].key.len + sizeof(uintptr_t) - 1) + & ~(sizeof(uintptr_t) - 1); + + copy = ngx_array_push_n(headers->values, size); + if (copy == NULL) { + return NGX_ERROR; + } + + copy->code = ngx_http_script_copy_code; + copy->len = src[i].key.len; + + p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t); + ngx_memcpy(p, src[i].key.data, src[i].key.len); + if (ngx_http_script_variables_count(&src[i].value) == 0) { copy = ngx_array_push_n(headers->lengths, sizeof(ngx_http_script_copy_code_t)); @@ -3507,14 +3543,10 @@ ngx_http_proxy_init_headers(ngx_conf_t * copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code; - copy->len = src[i].key.len + sizeof(": ") - 1 - + src[i].value.len + sizeof(CRLF) - 1; - + copy->len = src[i].value.len; size = (sizeof(ngx_http_script_copy_code_t) - + src[i].key.len + sizeof(": ") - 1 - + src[i].value.len + sizeof(CRLF) - 1 - + sizeof(uintptr_t) - 1) + + src[i].value.len + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1); copy = ngx_array_push_n(headers->values, size); @@ -3523,45 +3555,12 @@ ngx_http_proxy_init_headers(ngx_conf_t * } copy->code = ngx_http_script_copy_code; - copy->len = src[i].key.len + sizeof(": ") - 1 - + src[i].value.len + sizeof(CRLF) - 1; + copy->len = src[i].value.len; p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t); - - p = ngx_cpymem(p, src[i].key.data, src[i].key.len); - *p++ = ':'; *p++ = ' '; - p = ngx_cpymem(p, src[i].value.data, src[i].value.len); - *p++ = CR; *p = LF; + ngx_memcpy(p, src[i].value.data, src[i].value.len); } else { - copy = ngx_array_push_n(headers->lengths, - sizeof(ngx_http_script_copy_code_t)); - if (copy == NULL) { - return NGX_ERROR; - } - - copy->code = (ngx_http_script_code_pt) - ngx_http_script_copy_len_code; - copy->len = src[i].key.len + sizeof(": ") - 1; - - - size = (sizeof(ngx_http_script_copy_code_t) - + src[i].key.len + sizeof(": ") - 1 + sizeof(uintptr_t) - 1) - & ~(sizeof(uintptr_t) - 1); - - copy = ngx_array_push_n(headers->values, size); - if (copy == NULL) { - return NGX_ERROR; - } - - copy->code = ngx_http_script_copy_code; - copy->len = src[i].key.len + sizeof(": ") - 1; - - p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t); - p = ngx_cpymem(p, src[i].key.data, src[i].key.len); - *p++ = ':'; *p = ' '; - - ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); sc.cf = cf; @@ -3573,33 +3572,6 @@ ngx_http_proxy_init_headers(ngx_conf_t * if (ngx_http_script_compile(&sc) != NGX_OK) { return NGX_ERROR; } - - - copy = ngx_array_push_n(headers->lengths, - sizeof(ngx_http_script_copy_code_t)); - if (copy == NULL) { - return NGX_ERROR; - } - - copy->code = (ngx_http_script_code_pt) - ngx_http_script_copy_len_code; - copy->len = sizeof(CRLF) - 1; - - - size = (sizeof(ngx_http_script_copy_code_t) - + sizeof(CRLF) - 1 + sizeof(uintptr_t) - 1) - & ~(sizeof(uintptr_t) - 1); - - copy = ngx_array_push_n(headers->values, size); - if (copy == NULL) { - return NGX_ERROR; - } - - copy->code = ngx_http_script_copy_code; - copy->len = sizeof(CRLF) - 1; - - p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t); - *p++ = CR; *p = LF; } code = ngx_array_push_n(headers->lengths, sizeof(uintptr_t)); From piotrsikora at google.com Sun Jun 4 03:04:02 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Sat, 03 Jun 2017 20:04:02 -0700 Subject: [PATCH] Proxy: add "proxy_ssl_alpn" directive Message-ID: <7733d946e2651a2486a5.1496545442@piotrsikora.sfo.corp.google.com> # HG changeset patch # User Piotr Sikora # Date 1489621682 25200 # Wed Mar 15 16:48:02 2017 -0700 # Node ID 7733d946e2651a2486a53d912703e2dfaea30421 # Parent 716852cce9136d977b81a2d1b8b6f9fbca0dce49 Proxy: add "proxy_ssl_alpn" directive. ALPN is used here only to indicate which version of the HTTP protocol is going to be used and we doesn't verify that upstream agreed to it. Please note that upstream is allowed to reject SSL connection with a fatal "no_application_protocol" alert if it doesn't support it. Signed-off-by: Piotr Sikora diff -r 716852cce913 -r 7733d946e265 src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -654,6 +654,29 @@ ngx_ssl_ciphers(ngx_conf_t *cf, ngx_ssl_ ngx_int_t +ngx_ssl_alpn_protos(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *protos) +{ +#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation + + if (SSL_CTX_set_alpn_protos(ssl->ctx, protos->data, protos->len) != 0) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "SSL_CTX_set_alpn_protos() failed"); + return NGX_ERROR; + } + + return NGX_OK; + +#else + + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "nginx was built with OpenSSL that lacks ALPN support"); + return NGX_ERROR; + +#endif +} + + +ngx_int_t ngx_ssl_client_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert, ngx_int_t depth) { diff -r 716852cce913 -r 7733d946e265 src/event/ngx_event_openssl.h --- a/src/event/ngx_event_openssl.h +++ b/src/event/ngx_event_openssl.h @@ -153,6 +153,8 @@ ngx_int_t ngx_ssl_certificate(ngx_conf_t ngx_str_t *cert, ngx_str_t *key, ngx_array_t *passwords); ngx_int_t ngx_ssl_ciphers(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *ciphers, ngx_uint_t prefer_server_ciphers); +ngx_int_t ngx_ssl_alpn_protos(ngx_conf_t *cf, ngx_ssl_t *ssl, + ngx_str_t *protos); ngx_int_t ngx_ssl_client_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert, ngx_int_t depth); ngx_int_t ngx_ssl_trusted_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, diff -r 716852cce913 -r 7733d946e265 src/http/modules/ngx_http_proxy_module.c --- a/src/http/modules/ngx_http_proxy_module.c +++ b/src/http/modules/ngx_http_proxy_module.c @@ -652,6 +652,13 @@ static ngx_command_t ngx_http_proxy_com offsetof(ngx_http_proxy_loc_conf_t, ssl_ciphers), NULL }, + { ngx_string("proxy_ssl_alpn"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_proxy_loc_conf_t, upstream.ssl_alpn), + NULL }, + { ngx_string("proxy_ssl_name"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_http_set_complex_value_slot, @@ -2882,6 +2889,7 @@ ngx_http_proxy_create_loc_conf(ngx_conf_ conf->upstream.intercept_errors = NGX_CONF_UNSET; #if (NGX_HTTP_SSL) + conf->upstream.ssl_alpn = NGX_CONF_UNSET; conf->upstream.ssl_session_reuse = NGX_CONF_UNSET; conf->upstream.ssl_server_name = NGX_CONF_UNSET; conf->upstream.ssl_verify = NGX_CONF_UNSET; @@ -3212,6 +3220,8 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t conf->upstream.ssl_name = prev->upstream.ssl_name; } + ngx_conf_merge_value(conf->upstream.ssl_alpn, + prev->upstream.ssl_alpn, 0); ngx_conf_merge_value(conf->upstream.ssl_server_name, prev->upstream.ssl_server_name, 0); ngx_conf_merge_value(conf->upstream.ssl_verify, @@ -4320,6 +4330,7 @@ ngx_http_proxy_lowat_check(ngx_conf_t *c static ngx_int_t ngx_http_proxy_set_ssl(ngx_conf_t *cf, ngx_http_proxy_loc_conf_t *plcf) { + ngx_str_t alpn; ngx_pool_cleanup_t *cln; plcf->upstream.ssl = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_t)); @@ -4366,6 +4377,24 @@ ngx_http_proxy_set_ssl(ngx_conf_t *cf, n return NGX_ERROR; } + if (plcf->upstream.ssl_alpn) { + + switch (plcf->http_version) { + + case NGX_HTTP_VERSION_10: + ngx_str_set(&alpn, NGX_HTTP_10_ALPN_ADVERTISE); + break; + + case NGX_HTTP_VERSION_11: + ngx_str_set(&alpn, NGX_HTTP_11_ALPN_ADVERTISE); + break; + } + + if (ngx_ssl_alpn_protos(cf, plcf->upstream.ssl, &alpn) != NGX_OK) { + return NGX_ERROR; + } + } + if (plcf->upstream.ssl_verify) { if (plcf->ssl_trusted_certificate.len == 0) { ngx_log_error(NGX_LOG_EMERG, cf->log, 0, diff -r 716852cce913 -r 7733d946e265 src/http/modules/ngx_http_ssl_module.c --- a/src/http/modules/ngx_http_ssl_module.c +++ b/src/http/modules/ngx_http_ssl_module.c @@ -17,8 +17,6 @@ typedef ngx_int_t (*ngx_ssl_variable_han #define NGX_DEFAULT_CIPHERS "HIGH:!aNULL:!MD5" #define NGX_DEFAULT_ECDH_CURVE "auto" -#define NGX_HTTP_NPN_ADVERTISE "\x08http/1.1" - #ifdef TLSEXT_TYPE_application_layer_protocol_negotiation static int ngx_http_ssl_alpn_select(ngx_ssl_conn_t *ssl_conn, diff -r 716852cce913 -r 7733d946e265 src/http/ngx_http.h --- a/src/http/ngx_http.h +++ b/src/http/ngx_http.h @@ -13,6 +13,11 @@ #include +#define NGX_HTTP_10_ALPN_ADVERTISE "\x08http/1.0" +#define NGX_HTTP_11_ALPN_ADVERTISE "\x08http/1.1" +#define NGX_HTTP_NPN_ADVERTISE NGX_HTTP_11_ALPN_ADVERTISE + + typedef struct ngx_http_request_s ngx_http_request_t; typedef struct ngx_http_upstream_s ngx_http_upstream_t; typedef struct ngx_http_cache_s ngx_http_cache_t; diff -r 716852cce913 -r 7733d946e265 src/http/ngx_http_upstream.h --- a/src/http/ngx_http_upstream.h +++ b/src/http/ngx_http_upstream.h @@ -224,6 +224,7 @@ typedef struct { #if (NGX_HTTP_SSL || NGX_COMPAT) ngx_ssl_t *ssl; + ngx_flag_t ssl_alpn; ngx_flag_t ssl_session_reuse; ngx_http_complex_value_t *ssl_name; From piotrsikora at google.com Sun Jun 4 03:04:05 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Sat, 03 Jun 2017 20:04:05 -0700 Subject: [PATCH] Upstream: ignore read-readiness if request wasn't sent Message-ID: # HG changeset patch # User Piotr Sikora # Date 1491296505 25200 # Tue Apr 04 02:01:45 2017 -0700 # Node ID bff5ac3da350d8d9225d4204d8aded90fb670f3f # Parent 716852cce9136d977b81a2d1b8b6f9fbca0dce49 Upstream: ignore read-readiness if request wasn't sent. Signed-off-by: Piotr Sikora diff -r 716852cce913 -r bff5ac3da350 src/http/ngx_http_upstream.c --- a/src/http/ngx_http_upstream.c +++ b/src/http/ngx_http_upstream.c @@ -2179,8 +2179,12 @@ ngx_http_upstream_process_header(ngx_htt return; } - if (!u->request_sent && ngx_http_upstream_test_connect(c) != NGX_OK) { - ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR); + if (!u->request_sent) { + if (ngx_http_upstream_test_connect(c) != NGX_OK) { + ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR); + return; + } + return; } From piotrsikora at google.com Sun Jun 4 03:04:07 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Sat, 03 Jun 2017 20:04:07 -0700 Subject: [PATCH] Output chain: propagate flush and last_buf flags to send_chain() Message-ID: <2a48b9b6e67d91594c17.1496545447@piotrsikora.sfo.corp.google.com> # HG changeset patch # User Piotr Sikora # Date 1491708381 25200 # Sat Apr 08 20:26:21 2017 -0700 # Node ID 2a48b9b6e67d91594c1787ebf721daebf5f88c91 # Parent 716852cce9136d977b81a2d1b8b6f9fbca0dce49 Output chain: propagate flush and last_buf flags to send_chain(). Signed-off-by: Piotr Sikora diff -r 716852cce913 -r 2a48b9b6e67d src/core/ngx_output_chain.c --- a/src/core/ngx_output_chain.c +++ b/src/core/ngx_output_chain.c @@ -658,6 +658,7 @@ ngx_chain_writer(void *data, ngx_chain_t ngx_chain_writer_ctx_t *ctx = data; off_t size; + ngx_uint_t flush; ngx_chain_t *cl, *ln, *chain; ngx_connection_t *c; @@ -689,9 +690,10 @@ ngx_chain_writer(void *data, ngx_chain_t size += ngx_buf_size(in->buf); - ngx_log_debug2(NGX_LOG_DEBUG_CORE, c->log, 0, - "chain writer buf fl:%d s:%uO", - in->buf->flush, ngx_buf_size(in->buf)); + ngx_log_debug3(NGX_LOG_DEBUG_CORE, c->log, 0, + "chain writer buf fl:%d l:%d s:%uO", + in->buf->flush, in->buf->last_buf, + ngx_buf_size(in->buf)); cl = ngx_alloc_chain_link(ctx->pool); if (cl == NULL) { @@ -707,6 +709,8 @@ ngx_chain_writer(void *data, ngx_chain_t ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0, "chain writer in: %p", ctx->out); + flush = 0; + for (cl = ctx->out; cl; cl = cl->next) { #if 1 @@ -732,9 +736,13 @@ ngx_chain_writer(void *data, ngx_chain_t #endif size += ngx_buf_size(cl->buf); + + if (cl->buf->flush || cl->buf->last_buf) { + flush = 1; + } } - if (size == 0 && !c->buffered) { + if (size == 0 && !flush && !c->buffered) { return NGX_OK; } From borodin at octonica.com Sun Jun 4 18:22:43 2017 From: borodin at octonica.com (Andrew Borodin) Date: Sun, 4 Jun 2017 23:22:43 +0500 Subject: Use primes for hashtable size In-Reply-To: References: <20170530130137.GT55433@mdounin.ru> <20170601173951.GO55433@mdounin.ru> <20170602114603.GR55433@mdounin.ru> Message-ID: 2017-06-04 8:35 GMT+05:00 Mathew Heard : > Sorry as the slow starts only occur on the production servers, and I don't > have the time currently to try and replicate elsewhere I can't profile it. > > I know a portion is from the hashtable size as we have gradually increased > it over the years with a positive effect on startup time. Last we tested > setting the bucket size to 128 yields a startup time decrease, but a slight > runtime performance decrease. OK. This seems reasonable. Are your hash sizes more than 10000? In it's present form, patch can radically reduce time to create hashtable, but withdraws guaranty about minimum possible hashtable size. But, for numbers more than 10k there is no such guarantee already, and, actually, patch can produce more compact hashtable. Hence, there is no downsides of using fast size search for tables larger than 10k entries. Will this form of a patch be viable to you? Best regards, Andrey Borodin. From xeioex at nginx.com Mon Jun 5 12:03:42 2017 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Mon, 05 Jun 2017 12:03:42 +0000 Subject: [njs] Fixed possible buffer overrun during numbers parsing. Message-ID: details: http://hg.nginx.org/njs/rev/a782bc08b927 branches: changeset: 350:a782bc08b927 user: Dmitry Volyntsev date: Wed May 31 20:42:15 2017 +0300 description: Fixed possible buffer overrun during numbers parsing. diffstat: njs/njs_number.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diffs (12 lines): diff -r 559d256dd65b -r a782bc08b927 njs/njs_number.c --- a/njs/njs_number.c Wed May 31 20:36:01 2017 +0300 +++ b/njs/njs_number.c Wed May 31 20:42:15 2017 +0300 @@ -101,7 +101,7 @@ njs_number_dec_parse(u_char **start, u_c p++; } - if (*p == '.') { + if (p < end && *p == '.') { frac = 0; scale = 1; From xeioex at nginx.com Mon Jun 5 12:03:43 2017 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Mon, 05 Jun 2017 12:03:43 +0000 Subject: [njs] Added support of scientific notation literals. Message-ID: details: http://hg.nginx.org/njs/rev/02a59fe82c7a branches: changeset: 351:02a59fe82c7a user: Dmitry Volyntsev date: Mon Jun 05 14:59:28 2017 +0300 description: Added support of scientific notation literals. diffstat: njs/njs_number.c | 46 +++++++++++++++++- njs/test/njs_unit_test.c | 110 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 152 insertions(+), 4 deletions(-) diffs (197 lines): diff -r a782bc08b927 -r 02a59fe82c7a njs/njs_number.c --- a/njs/njs_number.c Wed May 31 20:42:15 2017 +0300 +++ b/njs/njs_number.c Mon Jun 05 14:59:28 2017 +0300 @@ -80,10 +80,9 @@ njs_value_to_index(njs_value_t *value) double njs_number_dec_parse(u_char **start, u_char *end) { - u_char c, *p; - double num, frac, scale; - - /* TODO: "1e2" */ + u_char c, *e, *p; + double num, frac, scale, exponent; + nxt_bool_t minus; p = *start; @@ -121,6 +120,45 @@ njs_number_dec_parse(u_char **start, u_c num += frac / scale; } + e = p + 1; + + if (e < end && (*p == 'e' || *p == 'E')) { + minus = 0; + + if (e + 1 < end) { + if (*e == '-') { + e++; + minus = 1; + + } else if (*e == '+') { + e++; + } + } + + /* Values below '0' become >= 208. */ + c = *e - '0'; + + if (nxt_fast_path(c <= 9)) { + exponent = c; + p = e + 1; + + while (p < end) { + /* Values below '0' become >= 208. */ + c = *p - '0'; + + if (nxt_slow_path(c > 9)) { + break; + } + + exponent = exponent * 10 + c; + p++; + } + + exponent = minus ? -exponent : exponent; + num = num * pow(10.0, exponent); + } + } + *start = p; return num; diff -r a782bc08b927 -r 02a59fe82c7a njs/test/njs_unit_test.c --- a/njs/test/njs_unit_test.c Wed May 31 20:42:15 2017 +0300 +++ b/njs/test/njs_unit_test.c Mon Jun 05 14:59:28 2017 +0300 @@ -150,6 +150,62 @@ static njs_unit_test_t njs_test[] = { nxt_string("\n +1"), nxt_string("1") }, + /* Scientific notation. */ + + { nxt_string("0e0"), + nxt_string("0") }, + + { nxt_string("0.0e0"), + nxt_string("0") }, + + { nxt_string("1e0"), + nxt_string("1") }, + + { nxt_string("1e1"), + nxt_string("10") }, + + { nxt_string("1.e01"), + nxt_string("10") }, + + { nxt_string("5.7e1"), + nxt_string("57") }, + + { nxt_string("5.7e-1"), + nxt_string("0.570000") }, + + { nxt_string("-5.7e-1"), + nxt_string("-0.570000") }, + + { nxt_string("1.1e-01"), + nxt_string("0.110000") }, + + { nxt_string("5.7e-2"), + nxt_string("0.057000") }, + + { nxt_string("1.1e+01"), + nxt_string("11") }, + + { nxt_string("1e9"), + nxt_string("1000000000") }, + + { nxt_string("1.0e308"), + nxt_string("1e+308") }, + + { nxt_string("1e"), + nxt_string("SyntaxError: Unexpected token \"e\" in 1") }, + + { nxt_string("1.e"), + nxt_string("SyntaxError: Unexpected token \"e\" in 1") }, + + { nxt_string("1e+"), + nxt_string("SyntaxError: Unexpected token \"e\" in 1") }, + + { nxt_string("1.e-"), + nxt_string("SyntaxError: Unexpected token \"e\" in 1") }, + + { nxt_string("1eZ"), + nxt_string("SyntaxError: Unexpected token \"eZ\" in 1") }, + /* Indexes. */ { nxt_string("var a = []; a[-1] = 2; a[-1] == a['-1']"), @@ -299,6 +355,33 @@ static njs_unit_test_t njs_test[] = { nxt_string("5 - '0x2 z'"), nxt_string("NaN") }, + { nxt_string("12 - '5.7e1'"), + nxt_string("-45") }, + + { nxt_string("12 - '5.e1'"), + nxt_string("-38") }, + + { nxt_string("12 - '5.7e+01'"), + nxt_string("-45") }, + + { nxt_string("12 - '5.7e-01'"), + nxt_string("11.43") }, + + { nxt_string("12 - ' 5.7e1 '"), + nxt_string("-45") }, + + { nxt_string("12 - '5.7e'"), + nxt_string("NaN") }, + + { nxt_string("12 - '5.7e+'"), + nxt_string("NaN") }, + + { nxt_string("12 - '5.7e-'"), + nxt_string("NaN") }, + + { nxt_string("12 - ' 5.7e1 z'"), + nxt_string("NaN") }, + { nxt_string("5 - '0x'"), nxt_string("NaN") }, @@ -7210,6 +7293,33 @@ static njs_unit_test_t njs_test[] = { nxt_string("parseFloat('Infinit')"), nxt_string("NaN") }, + { nxt_string("parseFloat('5.7e1')"), + nxt_string("57") }, + + { nxt_string("parseFloat('-5.7e-1')"), + nxt_string("-0.570000") }, + + { nxt_string("parseFloat('-5.e-1')"), + nxt_string("-0.500000") }, + + { nxt_string("parseFloat('5.7e+01')"), + nxt_string("57") }, + + { nxt_string("parseFloat(' 5.7e+01abc')"), + nxt_string("57") }, + + { nxt_string("parseFloat('-5.7e-1abc')"), + nxt_string("-0.570000") }, + + { nxt_string("parseFloat('-5.7e')"), + nxt_string("-5.7") }, + + { nxt_string("parseFloat('-5.7e+')"), + nxt_string("-5.7") }, + + { nxt_string("parseFloat('-5.7e+abc')"), + nxt_string("-5.7") }, + /* Trick: number to boolean. */ { nxt_string("var a = 0; !!a"), From mdounin at mdounin.ru Mon Jun 5 13:09:16 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Mon, 5 Jun 2017 16:09:16 +0300 Subject: PSK Support In-Reply-To: <145451D4E6785E4DA4DFA353A58CB7B2015E15B7E3@OLAWPA-EXMB04.ad.garmin.com> References: <145451D4E6785E4DA4DFA353A58CB7B2015E15B7E3@OLAWPA-EXMB04.ad.garmin.com> Message-ID: <20170605130916.GX55433@mdounin.ru> Hello! On Thu, Jun 01, 2017 at 05:20:53PM +0000, Karstens, Nate wrote: > Greetings, > > I'm about push 3 patches that add support for PSK TLS cipher > suites to nginx and thought it would be good to discuss the > feature itself in a separate thread. > > First, PSK support is useful in certain environments that are > not conducive to a full public key infrastructure. The > environment I'm personally working with is the recreational > boating market; we are developing a new industry standard that > relies on HTTPS, secured by PSK, for much of its underlying > security protocol. I think this would also be useful to the IoT > market. A quick search shows that some other users have been > interested in this feature: > > https://forum.nginx.org/read.php?2,272443,272443 > https://stackoverflow.com/questions/22513641/pre-shared-keys-tls-psk-nginx-configuration I have mixed feelings about PSK. On the one hand, it is expected to be better for constrained devices and also may have various performance benefits for internal connections. On the other hand, using a shared key is a big step backwards compared to public-key cryptography. > After applying the patches, one can enable PSK support by adding > a few directives to their nginx.conf: > > 1) "ssl_nocert" -- This disables checks for a certificate within > nginx. By default these checks are enabled because most users > will need a certificate. This is analogous to the "-nocert" > option in the OpenSSL s_server. > 2) "ssl_psk_path" -- This is a local folder that contains all of > the valid PSKs. Each file in the folder is loaded into memory as > a PSK, and its file name is used as the PSK identity. When the > client connects it specifies the identity of the PSK it is using > for the connection. The server looks up the key using hash of > the loaded PSKs and if the keys match then the TLS handshake is > successful. Note that the identity of the PSK is made available > in the variable $ssl_psk_identity. > 3) Add some PSK ciphers to the "ssl_ciphers" directive. Some comments, in no particular order: - the "ssl_nocert" directive looks strange / unneeded, there should be a better way to do this (ssl_certificate with a special value "off"? just assume a certificate is not needed if there are PSK secrets? always require a certificate, and let users who don't need specify a dummy one?); - "ssl_psk_path" seems to be overcomplicated, and yet non-flexible as any change to keys requires configuration reloads; - the only server software with PSK support seems to be stunnel, it might be good to be compatible with the form it uses for PSK secrets (a file with "IDENTITY:KEY" lines); - probably eventually there should be a proxy_ssl_* counterpart, though it is perfectly ok to don't have it for now. -- Maxim Dounin http://nginx.org/ From Nate.Karstens at garmin.com Mon Jun 5 14:08:15 2017 From: Nate.Karstens at garmin.com (Karstens, Nate) Date: Mon, 5 Jun 2017 14:08:15 +0000 Subject: PSK Support In-Reply-To: <20170605130916.GX55433@mdounin.ru> References: <145451D4E6785E4DA4DFA353A58CB7B2015E15B7E3@OLAWPA-EXMB04.ad.garmin.com> <20170605130916.GX55433@mdounin.ru> Message-ID: <145451D4E6785E4DA4DFA353A58CB7B2015E15C57F@OLAWPA-EXMB04.ad.garmin.com> Maxim, Thanks for the reply. I understand your concerns about PSK. We discussed it quite a bit, but ultimately decided that a PKI was not practical for our environment. We have to rely on the end user to configure security and any solution using PKI would be so difficult to work with that they just wouldn't bother with security at all. I considered some alternatives on the "ssl_nocert" option. My preference would have been to analyze the supported cipher suites (from "ssl_ciphers") and determine if any include a PSK, but it does not look like OpenSSL exposes APIs to accomplish this. Using a dummy certificate seemed more complicated than the other two suggestions you had (using "ssl_certificate" with a value of "off" or disabling the tests if there are PSK secrets), so I'd prefer one of those two. What is your preference? One advantage of the PSK path concept is that it provides a lot of flexibility. It allows, for example, multiple applications to each independently manage their own PSKs without the need to coordinate changes to a single file (note that in this scenario each application would want to use $ssl_psk_identity to check the key). stunnel uses a single file and seems to assume that keys will be ASCII strings. Its format, for example, would not allow NUL to appear in the string, as that would terminate the key early and, at best, lead to a reduced key size. I might be mistaken, but wouldn't changing a certificate also require reloading the configuration? Do you have some ideas on how this could be done without requiring a reload? I agree on the proxy_ssl_* directives; the same could probably be said for the mail and stream SSL modules. Regards, Nate -----Original Message----- From: nginx-devel [mailto:nginx-devel-bounces at nginx.org] On Behalf Of Maxim Dounin Sent: Monday, June 05, 2017 8:09 AM To: nginx-devel at nginx.org Subject: Re: PSK Support Hello! On Thu, Jun 01, 2017 at 05:20:53PM +0000, Karstens, Nate wrote: > Greetings, > > I'm about push 3 patches that add support for PSK TLS cipher suites to > nginx and thought it would be good to discuss the feature itself in a > separate thread. > > First, PSK support is useful in certain environments that are not > conducive to a full public key infrastructure. The environment I'm > personally working with is the recreational boating market; we are > developing a new industry standard that relies on HTTPS, secured by > PSK, for much of its underlying security protocol. I think this would > also be useful to the IoT market. A quick search shows that some other > users have been interested in this feature: > > https://forum.nginx.org/read.php?2,272443,272443 > https://stackoverflow.com/questions/22513641/pre-shared-keys-tls-psk-n > ginx-configuration I have mixed feelings about PSK. On the one hand, it is expected to be better for constrained devices and also may have various performance benefits for internal connections. On the other hand, using a shared key is a big step backwards compared to public-key cryptography. > After applying the patches, one can enable PSK support by adding a few > directives to their nginx.conf: > > 1) "ssl_nocert" -- This disables checks for a certificate within > nginx. By default these checks are enabled because most users will > need a certificate. This is analogous to the "-nocert" > option in the OpenSSL s_server. > 2) "ssl_psk_path" -- This is a local folder that contains all of the > valid PSKs. Each file in the folder is loaded into memory as a PSK, > and its file name is used as the PSK identity. When the client > connects it specifies the identity of the PSK it is using for the > connection. The server looks up the key using hash of the loaded PSKs > and if the keys match then the TLS handshake is successful. Note that > the identity of the PSK is made available in the variable > $ssl_psk_identity. > 3) Add some PSK ciphers to the "ssl_ciphers" directive. Some comments, in no particular order: - the "ssl_nocert" directive looks strange / unneeded, there should be a better way to do this (ssl_certificate with a special value "off"? just assume a certificate is not needed if there are PSK secrets? always require a certificate, and let users who don't need specify a dummy one?); - "ssl_psk_path" seems to be overcomplicated, and yet non-flexible as any change to keys requires configuration reloads; - the only server software with PSK support seems to be stunnel, it might be good to be compatible with the form it uses for PSK secrets (a file with "IDENTITY:KEY" lines); - probably eventually there should be a proxy_ssl_* counterpart, though it is perfectly ok to don't have it for now. -- Maxim Dounin http://nginx.org/ _______________________________________________ nginx-devel mailing list nginx-devel at nginx.org http://mailman.nginx.org/mailman/listinfo/nginx-devel ________________________________ CONFIDENTIALITY NOTICE: This email and any attachments are for the sole use of the intended recipient(s) and contain information that may be Garmin confidential and/or Garmin legally privileged. If you have received this email in error, please notify the sender by reply email and delete the message. Any disclosure, copying, distribution or use of this communication (including attachments) by someone other than the intended recipient is prohibited. Thank you. From mdounin at mdounin.ru Mon Jun 5 16:29:40 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Mon, 5 Jun 2017 19:29:40 +0300 Subject: [PATCH 1 of 3] Added support for trailers in HTTP responses In-Reply-To: <41c09a2fd90410e25ad8.1496460825@piotrsikora.sfo.corp.google.com> References: <41c09a2fd90410e25ad8.1496460825@piotrsikora.sfo.corp.google.com> Message-ID: <20170605162940.GY55433@mdounin.ru> Hello! On Fri, Jun 02, 2017 at 08:33:45PM -0700, Piotr Sikora via nginx-devel wrote: > # HG changeset patch > # User Piotr Sikora > # Date 1490351854 25200 > # Fri Mar 24 03:37:34 2017 -0700 > # Node ID 41c09a2fd90410e25ad8515793bd48028001c954 > # Parent 716852cce9136d977b81a2d1b8b6f9fbca0dce49 > Added support for trailers in HTTP responses. > > Example: > > ngx_table_elt_t *h; > > h = ngx_list_push(&r->headers_out.trailers); > if (h == NULL) { > return NGX_ERROR; > } > > ngx_str_set(&h->key, "Fun"); > ngx_str_set(&h->value, "with trailers"); > h->hash = ngx_hash_key_lc(h->key.data, h->key.len); > > The code above adds "Fun: with trailers" trailer to the response. > > Modules that want to emit trailers must set r->expect_trailers = 1 > in header filter, otherwise they might not be emitted for HTTP/1.1 > responses that aren't already chunked. > > This change also adds $sent_trailer_* variables. > > Signed-off-by: Piotr Sikora Overall looks good, see some additional comments below. > > diff -r 716852cce913 -r 41c09a2fd904 src/http/modules/ngx_http_chunked_filter_module.c > --- a/src/http/modules/ngx_http_chunked_filter_module.c > +++ b/src/http/modules/ngx_http_chunked_filter_module.c > @@ -17,6 +17,7 @@ typedef struct { > > > static ngx_int_t ngx_http_chunked_filter_init(ngx_conf_t *cf); > +static ngx_chain_t *ngx_http_chunked_create_trailers(ngx_http_request_t *r); > > > static ngx_http_module_t ngx_http_chunked_filter_module_ctx = { > @@ -69,27 +70,28 @@ ngx_http_chunked_header_filter(ngx_http_ > return ngx_http_next_header_filter(r); > } > > - if (r->headers_out.content_length_n == -1) { > - if (r->http_version < NGX_HTTP_VERSION_11) { > + if (r->headers_out.content_length_n == -1 || r->expect_trailers) { > + I actually think that using two lines as initially suggested is more readable and more in line with current style. YMMV. - if (r->headers_out.content_length_n == -1 || r->expect_trailers) { - + if (r->headers_out.content_length_n == -1 + || r->expect_trailers) + { [...] > @@ -230,6 +223,105 @@ ngx_http_chunked_body_filter(ngx_http_re > } > > > +static ngx_chain_t * > +ngx_http_chunked_create_trailers(ngx_http_request_t *r) > +{ [...] > + len += header[i].key.len + sizeof(": ") - 1 > + + header[i].value.len + sizeof(CRLF) - 1; > + } > + > + ctx = ngx_http_get_module_ctx(r, ngx_http_chunked_filter_module); Usual approach is to pass context into internal functions if needed and already available in the calling functions. static ngx_chain_t * -ngx_http_chunked_create_trailers(ngx_http_request_t *r) +ngx_http_chunked_create_trailers(ngx_http_request_t *r, + ngx_http_chunked_filter_ctx_t *ctx) { (and more, see full patch below) > + > + cl = ngx_chain_get_free_buf(r->pool, &ctx->free); > + if (cl == NULL) { > + return NULL; > + } > + > + b = cl->buf; > + > + b->tag = (ngx_buf_tag_t) &ngx_http_chunked_filter_module; > + b->temporary = 0; > + b->memory = 1; > + b->last_buf = 1; > + > + b->start = ngx_palloc(r->pool, len); > + if (b->start == NULL) { > + return NULL; > + } I would prefer to preserve the typical code path (when there are no trailers) without an extra allocation. It looks like it would be as trivail as: @@ -273,14 +273,18 @@ ngx_http_chunked_create_trailers(ngx_htt b->memory = 1; b->last_buf = 1; + if (len == sizeof(CRLF "0" CRLF CRLF) - 1) { + b->pos = (u_char *) CRLF "0" CRLF CRLF; + b->last = b->pos + sizeof(CRLF "0" CRLF CRLF) - 1; + return cl; + } + b->start = ngx_palloc(r->pool, len); if (b->start == NULL) { return NULL; } Note well that b->start is intentionally not touched in the previous code. As buffers are reused, b->start, if set, is expected to point to a chunk of memory big enough to contain "0000000000000000" CRLF, as allocated in the ngx_http_chunked_body_filter(). While this is not critical in this particular code path, as last-chunk is expected to be only created once, the resulting code is confusing: while it provides b->tag to make the buffer reusable, it doesn't maintain required invariant on b->start. Trivial solution would be to avoid setting b->start / b->end as it was done in the previous code and still done in the CRLF case. - b->start = ngx_palloc(r->pool, len); - if (b->start == NULL) { + b->pos = ngx_palloc(r->pool, len); + if (b->pos == NULL) { return NULL; } - b->end = b->last + len; - b->pos = b->start; - b->last = b->start; + b->last = b->pos; Full patch with the above comments: diff --git a/src/http/modules/ngx_http_chunked_filter_module.c b/src/http/modules/ngx_http_chunked_filter_module.c --- a/src/http/modules/ngx_http_chunked_filter_module.c +++ b/src/http/modules/ngx_http_chunked_filter_module.c @@ -17,7 +17,8 @@ typedef struct { static ngx_int_t ngx_http_chunked_filter_init(ngx_conf_t *cf); -static ngx_chain_t *ngx_http_chunked_create_trailers(ngx_http_request_t *r); +static ngx_chain_t *ngx_http_chunked_create_trailers(ngx_http_request_t *r, + ngx_http_chunked_filter_ctx_t *ctx); static ngx_http_module_t ngx_http_chunked_filter_module_ctx = { @@ -70,8 +71,9 @@ ngx_http_chunked_header_filter(ngx_http_ return ngx_http_next_header_filter(r); } - if (r->headers_out.content_length_n == -1 || r->expect_trailers) { - + if (r->headers_out.content_length_n == -1 + || r->expect_trailers) + { clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); if (r->http_version >= NGX_HTTP_VERSION_11 @@ -181,7 +183,7 @@ ngx_http_chunked_body_filter(ngx_http_re } if (cl->buf->last_buf) { - tl = ngx_http_chunked_create_trailers(r); + tl = ngx_http_chunked_create_trailers(r, ctx); if (tl == NULL) { return NGX_ERROR; } @@ -224,15 +226,15 @@ ngx_http_chunked_body_filter(ngx_http_re static ngx_chain_t * -ngx_http_chunked_create_trailers(ngx_http_request_t *r) +ngx_http_chunked_create_trailers(ngx_http_request_t *r, + ngx_http_chunked_filter_ctx_t *ctx) { - size_t len; - ngx_buf_t *b; - ngx_uint_t i; - ngx_chain_t *cl; - ngx_list_part_t *part; - ngx_table_elt_t *header; - ngx_http_chunked_filter_ctx_t *ctx; + size_t len; + ngx_buf_t *b; + ngx_uint_t i; + ngx_chain_t *cl; + ngx_list_part_t *part; + ngx_table_elt_t *header; len = sizeof(CRLF "0" CRLF CRLF) - 1; @@ -259,8 +261,6 @@ ngx_http_chunked_create_trailers(ngx_htt + header[i].value.len + sizeof(CRLF) - 1; } - ctx = ngx_http_get_module_ctx(r, ngx_http_chunked_filter_module); - cl = ngx_chain_get_free_buf(r->pool, &ctx->free); if (cl == NULL) { return NULL; @@ -273,14 +273,18 @@ ngx_http_chunked_create_trailers(ngx_htt b->memory = 1; b->last_buf = 1; - b->start = ngx_palloc(r->pool, len); - if (b->start == NULL) { + if (len == sizeof(CRLF "0" CRLF CRLF) - 1) { + b->pos = (u_char *) CRLF "0" CRLF CRLF; + b->last = b->pos + sizeof(CRLF "0" CRLF CRLF) - 1; + return cl; + } + + b->pos = ngx_palloc(r->pool, len); + if (b->pos == NULL) { return NULL; } - b->end = b->last + len; - b->pos = b->start; - b->last = b->start; + b->last = b->pos; *b->last++ = CR; *b->last++ = LF; *b->last++ = '0'; -- Maxim Dounin http://nginx.org/ From mdounin at mdounin.ru Mon Jun 5 17:52:45 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Mon, 5 Jun 2017 20:52:45 +0300 Subject: [PATCH 3 of 3] Headers filter: added "add_trailer" directive In-Reply-To: References: Message-ID: <20170605175245.GZ55433@mdounin.ru> Hello! On Fri, Jun 02, 2017 at 08:33:47PM -0700, Piotr Sikora via nginx-devel wrote: > # HG changeset patch > # User Piotr Sikora > # Date 1490351854 25200 > # Fri Mar 24 03:37:34 2017 -0700 > # Node ID acdc80c0d4ef8aa2519e2882ff1a3bd4a316ad81 > # Parent 8d74ff6c2015180f5c1f399f492214d7d0a52b3f > Headers filter: added "add_trailer" directive. > > Trailers added using this directive are evaluated after response body > is processed by output filters (but before it's written to the wire), > so it's possible to use variables calculated from the response body > as the trailer value. > > Signed-off-by: Piotr Sikora Overall looks good, see some comments below. [...] > @@ -206,11 +223,103 @@ ngx_http_headers_filter(ngx_http_request > } > } > > + if (conf->trailers) { > + h = conf->trailers->elts; > + for (i = 0; i < conf->trailers->nelts; i++) { > + > + if (!safe_status && !h[i].always) { > + continue; > + } > + > + if (h[i].value.value.len) { > + r->expect_trailers = 1; > + break; > + } It doesn't look like "if (h[i].value.value.len)" is needed here. It is either true, or the "add_trailer" directive is nop and we already know this while parsing the configuration. - if (h[i].value.value.len) { - r->expect_trailers = 1; - break; - } + r->expect_trailers = 1; + break; [...] > @@ -741,3 +858,63 @@ ngx_http_headers_add(ngx_conf_t *cf, ngx > > return NGX_CONF_OK; > } > + > + > +static char * > +ngx_http_headers_add_trailer(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) > +{ > + ngx_http_headers_conf_t *hcf = conf; > + > + ngx_str_t *value; > + ngx_http_header_val_t *hv; > + ngx_http_compile_complex_value_t ccv; > + > + value = cf->args->elts; > + > + if (hcf->trailers == NULL) { > + hcf->trailers = ngx_array_create(cf->pool, 1, > + sizeof(ngx_http_header_val_t)); > + if (hcf->trailers == NULL) { > + return NGX_CONF_ERROR; > + } > + } > + > + hv = ngx_array_push(hcf->trailers); > + if (hv == NULL) { > + return NGX_CONF_ERROR; > + } [...] It looks like the ngx_http_headers_add_trailer() function is almost exact copy of the ngx_http_headers_add(). It might worth to use single function to handle both directives. diff --git a/src/http/modules/ngx_http_headers_filter_module.c b/src/http/modules/ngx_http_headers_filter_module.c --- a/src/http/modules/ngx_http_headers_filter_module.c +++ b/src/http/modules/ngx_http_headers_filter_module.c @@ -73,8 +73,6 @@ static char *ngx_http_headers_expires(ng void *conf); static char *ngx_http_headers_add(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); -static char *ngx_http_headers_add_trailer(ngx_conf_t *cf, ngx_command_t *cmd, - void *conf); static ngx_http_set_header_t ngx_http_set_headers[] = { @@ -108,15 +106,15 @@ static ngx_command_t ngx_http_headers_f |NGX_CONF_TAKE23, ngx_http_headers_add, NGX_HTTP_LOC_CONF_OFFSET, - 0, + offsetof(ngx_http_headers_conf_t, headers), NULL }, { ngx_string("add_trailer"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF |NGX_CONF_TAKE23, - ngx_http_headers_add_trailer, + ngx_http_headers_add, NGX_HTTP_LOC_CONF_OFFSET, - 0, + offsetof(ngx_http_headers_conf_t, trailers), NULL }, ngx_null_command @@ -231,10 +229,8 @@ ngx_http_headers_filter(ngx_http_request continue; } - if (h[i].value.value.len) { - r->expect_trailers = 1; - break; - } + r->expect_trailers = 1; + break; } } @@ -791,42 +787,49 @@ ngx_http_headers_add(ngx_conf_t *cf, ngx { ngx_http_headers_conf_t *hcf = conf; - ngx_str_t *value; - ngx_uint_t i; - ngx_http_header_val_t *hv; - ngx_http_set_header_t *set; - ngx_http_compile_complex_value_t ccv; + ngx_str_t *value; + ngx_uint_t i; + ngx_array_t **headers; + ngx_http_header_val_t *hv; + ngx_http_set_header_t *set; + ngx_http_compile_complex_value_t ccv; value = cf->args->elts; - if (hcf->headers == NULL) { - hcf->headers = ngx_array_create(cf->pool, 1, - sizeof(ngx_http_header_val_t)); - if (hcf->headers == NULL) { + headers = (ngx_array_t **) ((char *) hcf + cmd->offset); + + if (*headers == NULL) { + *headers = ngx_array_create(cf->pool, 1, + sizeof(ngx_http_header_val_t)); + if (*headers == NULL) { return NGX_CONF_ERROR; } } - hv = ngx_array_push(hcf->headers); + hv = ngx_array_push(*headers); if (hv == NULL) { return NGX_CONF_ERROR; } hv->key = value[1]; - hv->handler = ngx_http_add_header; + hv->handler = NULL; hv->offset = 0; hv->always = 0; - set = ngx_http_set_headers; - for (i = 0; set[i].name.len; i++) { - if (ngx_strcasecmp(value[1].data, set[i].name.data) != 0) { - continue; + if (headers == &hcf->headers) { + hv->handler = ngx_http_add_header; + + set = ngx_http_set_headers; + for (i = 0; set[i].name.len; i++) { + if (ngx_strcasecmp(value[1].data, set[i].name.data) != 0) { + continue; + } + + hv->offset = set[i].offset; + hv->handler = set[i].handler; + + break; } - - hv->offset = set[i].offset; - hv->handler = set[i].handler; - - break; } if (value[2].len == 0) { @@ -858,63 +861,3 @@ ngx_http_headers_add(ngx_conf_t *cf, ngx return NGX_CONF_OK; } - - -static char * -ngx_http_headers_add_trailer(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) -{ - ngx_http_headers_conf_t *hcf = conf; - - ngx_str_t *value; - ngx_http_header_val_t *hv; - ngx_http_compile_complex_value_t ccv; - - value = cf->args->elts; - - if (hcf->trailers == NULL) { - hcf->trailers = ngx_array_create(cf->pool, 1, - sizeof(ngx_http_header_val_t)); - if (hcf->trailers == NULL) { - return NGX_CONF_ERROR; - } - } - - hv = ngx_array_push(hcf->trailers); - if (hv == NULL) { - return NGX_CONF_ERROR; - } - - hv->key = value[1]; - hv->handler = NULL; - hv->offset = 0; - hv->always = 0; - - if (value[2].len == 0) { - ngx_memzero(&hv->value, sizeof(ngx_http_complex_value_t)); - - } else { - ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); - - ccv.cf = cf; - ccv.value = &value[2]; - ccv.complex_value = &hv->value; - - if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { - return NGX_CONF_ERROR; - } - } - - if (cf->args->nelts == 3) { - return NGX_CONF_OK; - } - - if (ngx_strcmp(value[3].data, "always") != 0) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "invalid parameter \"%V\"", &value[3]); - return NGX_CONF_ERROR; - } - - hv->always = 1; - - return NGX_CONF_OK; -} -- Maxim Dounin http://nginx.org/ From mdounin at mdounin.ru Mon Jun 5 18:00:47 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Mon, 5 Jun 2017 21:00:47 +0300 Subject: [PATCH 2 of 3] HTTP/2: added support for trailers in HTTP responses In-Reply-To: <8d74ff6c2015180f5c1f.1496460826@piotrsikora.sfo.corp.google.com> References: <8d74ff6c2015180f5c1f.1496460826@piotrsikora.sfo.corp.google.com> Message-ID: <20170605180047.GA55433@mdounin.ru> Hello! On Fri, Jun 02, 2017 at 08:33:46PM -0700, Piotr Sikora via nginx-devel wrote: > # HG changeset patch > # User Piotr Sikora > # Date 1493191954 25200 > # Wed Apr 26 00:32:34 2017 -0700 > # Node ID 8d74ff6c2015180f5c1f399f492214d7d0a52b3f > # Parent 41c09a2fd90410e25ad8515793bd48028001c954 > HTTP/2: added support for trailers in HTTP responses. > > Signed-off-by: Piotr Sikora I've asked Valentin to look into this part. Hopefully he'll be able to do so in a couple of days. [...] -- Maxim Dounin http://nginx.org/ From mdounin at mdounin.ru Mon Jun 5 18:39:45 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Mon, 5 Jun 2017 21:39:45 +0300 Subject: PSK Support In-Reply-To: <145451D4E6785E4DA4DFA353A58CB7B2015E15C57F@OLAWPA-EXMB04.ad.garmin.com> References: <145451D4E6785E4DA4DFA353A58CB7B2015E15B7E3@OLAWPA-EXMB04.ad.garmin.com> <20170605130916.GX55433@mdounin.ru> <145451D4E6785E4DA4DFA353A58CB7B2015E15C57F@OLAWPA-EXMB04.ad.garmin.com> Message-ID: <20170605183944.GB55433@mdounin.ru> Hello! On Mon, Jun 05, 2017 at 02:08:15PM +0000, Karstens, Nate wrote: > Maxim, > > Thanks for the reply. I understand your concerns about PSK. We > discussed it quite a bit, but ultimately decided that a PKI was > not practical for our environment. We have to rely on the end > user to configure security and any solution using PKI would be > so difficult to work with that they just wouldn't bother with > security at all. Ok, understood. I think that PSK can be a reasonable alternative to using plain http in many cases. > I considered some alternatives on the "ssl_nocert" option. My > preference would have been to analyze the supported cipher > suites (from "ssl_ciphers") and determine if any include a PSK, > but it does not look like OpenSSL exposes APIs to accomplish > this. By default, nginx uses "HIGH:!aNULL:!MD5" as ciphers list, and this includes various PSK ciphers as well, so this approach doesn't look working even if there were appropriate APIs. > Using a dummy certificate seemed more complicated than the > other two suggestions you had (using "ssl_certificate" with a > value of "off" or disabling the tests if there are PSK secrets), > so I'd prefer one of those two. What is your preference? Using a dummy certificate has an obvious benefit of not requiring any changes to the code, and might actually be a good starting option. Disabling the tests with PSK secrets might not work as expected when they are defined at the http{} level. Using "ssl_certificate off" is obviously most explicit of all options, but I would rather consider a dummy certificate instead for now, as long as there are no other downsides. > One advantage of the PSK path concept is that it provides a lot > of flexibility. It allows, for example, multiple applications to > each independently manage their own PSKs without the need to > coordinate changes to a single file (note that in this scenario > each application would want to use $ssl_psk_identity to check > the key). On the one hand, it is a plus. On the other - it is a nightmare when something goes wrong. I would rather avoid such approach. > stunnel uses a single file and seems to assume that > keys will be ASCII strings. Its format, for example, would not > allow NUL to appear in the string, as that would terminate the > key early and, at best, lead to a reduced key size. Yes, and stunnel author considers this to be a feature, see https://www.stunnel.org/pipermail/stunnel-users/2015-October/005275.html. If you are targeting end-users, it might be actually easier to use sufficiently long printable keys then arbitrary binary strings. > I might be mistaken, but wouldn't changing a certificate also > require reloading the configuration? Do you have some ideas on > how this could be done without requiring a reload? Yes, changing a certificate requires a reload. But the "path" concept is generally used in SSL where appropriate filesystem lookups are done on the fly, in contrast to loading a file into memory and then working with the data from memory. Consider "openssl verify -CApath" vs. "openssl verify -CAfile". Additionally, PSK keys look much more dynamic than certificates, as adding a user requires configuration changes. With PKI, you don't need any certificate changes on the server to add a user. With PSK, you have to add a key to introduce a new user. Overall, PSK seems to be very close to basic authentication, and it might worth looking how it is implemented in the auth_basic module (in short: the password file is searched on each request). -- Maxim Dounin http://nginx.org/ From piotrsikora at google.com Tue Jun 6 04:56:03 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Mon, 5 Jun 2017 21:56:03 -0700 Subject: [PATCH 1 of 3] Added support for trailers in HTTP responses In-Reply-To: <20170605162940.GY55433@mdounin.ru> References: <41c09a2fd90410e25ad8.1496460825@piotrsikora.sfo.corp.google.com> <20170605162940.GY55433@mdounin.ru> Message-ID: Hey Maxim, > I would prefer to preserve the typical code path (when there are no > trailers) without an extra allocation. It looks like it would be > as trivail as: > > @@ -273,14 +273,18 @@ ngx_http_chunked_create_trailers(ngx_htt > b->memory = 1; > b->last_buf = 1; > > + if (len == sizeof(CRLF "0" CRLF CRLF) - 1) { > + b->pos = (u_char *) CRLF "0" CRLF CRLF; > + b->last = b->pos + sizeof(CRLF "0" CRLF CRLF) - 1; > + return cl; > + } Sounds good, but the if statement reads a bit weird. What about this instead, even though it might be a bit more expensive? @@ -236,7 +236,7 @@ ngx_http_chunked_create_trailers(ngx_http_request_t *r, ngx_list_part_t *part; ngx_table_elt_t *header; - len = sizeof(CRLF "0" CRLF CRLF) - 1; + len = 0; part = &r->headers_out.trailers.part; header = part->elts; @@ -273,12 +273,14 @@ ngx_http_chunked_create_trailers(ngx_http_request_t *r, b->memory = 1; b->last_buf = 1; - if (len == sizeof(CRLF "0" CRLF CRLF) - 1) { + if (len == 0) { b->pos = (u_char *) CRLF "0" CRLF CRLF; b->last = b->pos + sizeof(CRLF "0" CRLF CRLF) - 1; return cl; } + len += sizeof(CRLF "0" CRLF CRLF) - 1; + b->pos = ngx_palloc(r->pool, len); if (b->pos == NULL) { return NULL; Best regards, Piotr Sikora From piotrsikora at google.com Tue Jun 6 04:59:45 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Mon, 5 Jun 2017 21:59:45 -0700 Subject: [PATCH 3 of 3] Headers filter: added "add_trailer" directive In-Reply-To: <20170605175245.GZ55433@mdounin.ru> References: <20170605175245.GZ55433@mdounin.ru> Message-ID: Hey Maxim, > It doesn't look like "if (h[i].value.value.len)" is needed here. > It is either true, or the "add_trailer" directive is nop and we > already know this while parsing the configuration. > > - if (h[i].value.value.len) { > - r->expect_trailers = 1; > - break; > - } > + r->expect_trailers = 1; > + break; Well, both "add_header" and "add_trailer" allow setting something like: add_trailer Empty ""; which will get added to headers / trailers list. I've added this extra check to avoid forcing chunked encoding with such configuration. Maybe we should reject it during configuration instead, or ignore this case and let it force chunked encoding? Which one do you prefer? Best regards, Piotr Sikora From maxim at nginx.com Tue Jun 6 07:26:12 2017 From: maxim at nginx.com (Maxim Konovalov) Date: Tue, 6 Jun 2017 10:26:12 +0300 Subject: [PATCH 2 of 3] HTTP/2: added support for trailers in HTTP responses In-Reply-To: <20170605180047.GA55433@mdounin.ru> References: <8d74ff6c2015180f5c1f.1496460826@piotrsikora.sfo.corp.google.com> <20170605180047.GA55433@mdounin.ru> Message-ID: <7b105922-9797-1cf2-9fc4-882f7a0591c9@nginx.com> On 05/06/2017 21:00, Maxim Dounin wrote: > Hello! > > On Fri, Jun 02, 2017 at 08:33:46PM -0700, Piotr Sikora via nginx-devel wrote: > >> # HG changeset patch >> # User Piotr Sikora >> # Date 1493191954 25200 >> # Wed Apr 26 00:32:34 2017 -0700 >> # Node ID 8d74ff6c2015180f5c1f399f492214d7d0a52b3f >> # Parent 41c09a2fd90410e25ad8515793bd48028001c954 >> HTTP/2: added support for trailers in HTTP responses. >> >> Signed-off-by: Piotr Sikora > > I've asked Valentin to look into this part. Hopefully he'll be > able to do so in a couple of days. > > [...] > To be precise and to avoid confusion: Valentin is on the conf today so expect his feedback this week. -- Maxim Konovalov From orgads at gmail.com Tue Jun 6 10:57:39 2017 From: orgads at gmail.com (Orgad Shaneh) Date: Tue, 6 Jun 2017 13:57:39 +0300 Subject: [PATCH] Fix compilation on MinGW64 Message-ID: I already proposed a similar patch (without MSYS) on November, but it was unnoticed since then. --- auto/configure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/auto/configure b/auto/configure index ceff15e4..107c2b5f 100755 --- a/auto/configure +++ b/auto/configure @@ -36,7 +36,7 @@ if test -z "$NGX_PLATFORM"; then NGX_PLATFORM="$NGX_SYSTEM:$NGX_RELEASE:$NGX_MACHINE"; case "$NGX_SYSTEM" in - MINGW32_*) + MINGW32_*|MINGW64_*|MSYS_*) NGX_PLATFORM=win32 ;; esac -- 2.13.0.windows.1.7.g80a6209eb5 From orgads at gmail.com Tue Jun 6 10:58:17 2017 From: orgads at gmail.com (Orgad Shaneh) Date: Tue, 6 Jun 2017 13:58:17 +0300 Subject: [PATCH] Use .exe for binaries for all win32 compilers Message-ID: --- auto/cc/conf | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/auto/cc/conf b/auto/cc/conf index afbca62b..19a231aa 100644 --- a/auto/cc/conf +++ b/auto/cc/conf @@ -144,7 +144,9 @@ fi CFLAGS="$CFLAGS $NGX_CC_OPT" NGX_TEST_LD_OPT="$NGX_LD_OPT" -if [ "$NGX_PLATFORM" != win32 ]; then +if [ "$NGX_PLATFORM" = win32 ]; then + ngx_binext=".exe" +else if test -n "$NGX_LD_OPT"; then ngx_feature=--with-ld-opt=\"$NGX_LD_OPT\" -- 2.13.0.windows.1.7.g80a6209eb5 From mdounin at mdounin.ru Tue Jun 6 12:25:54 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Tue, 6 Jun 2017 15:25:54 +0300 Subject: [PATCH 1 of 3] Added support for trailers in HTTP responses In-Reply-To: References: <41c09a2fd90410e25ad8.1496460825@piotrsikora.sfo.corp.google.com> <20170605162940.GY55433@mdounin.ru> Message-ID: <20170606122554.GD55433@mdounin.ru> Hello! On Mon, Jun 05, 2017 at 09:56:03PM -0700, Piotr Sikora via nginx-devel wrote: > Hey Maxim, > > > I would prefer to preserve the typical code path (when there are no > > trailers) without an extra allocation. It looks like it would be > > as trivail as: > > > > @@ -273,14 +273,18 @@ ngx_http_chunked_create_trailers(ngx_htt > > b->memory = 1; > > b->last_buf = 1; > > > > + if (len == sizeof(CRLF "0" CRLF CRLF) - 1) { > > + b->pos = (u_char *) CRLF "0" CRLF CRLF; > > + b->last = b->pos + sizeof(CRLF "0" CRLF CRLF) - 1; > > + return cl; > > + } > > Sounds good, but the if statement reads a bit weird. > > What about this instead, even though it might be a bit more expensive? > > @@ -236,7 +236,7 @@ ngx_http_chunked_create_trailers(ngx_http_request_t *r, > ngx_list_part_t *part; > ngx_table_elt_t *header; > > - len = sizeof(CRLF "0" CRLF CRLF) - 1; > + len = 0; > > part = &r->headers_out.trailers.part; > header = part->elts; > @@ -273,12 +273,14 @@ ngx_http_chunked_create_trailers(ngx_http_request_t *r, > b->memory = 1; > b->last_buf = 1; > > - if (len == sizeof(CRLF "0" CRLF CRLF) - 1) { > + if (len == 0) { > b->pos = (u_char *) CRLF "0" CRLF CRLF; > b->last = b->pos + sizeof(CRLF "0" CRLF CRLF) - 1; > return cl; > } > > + len += sizeof(CRLF "0" CRLF CRLF) - 1; > + > b->pos = ngx_palloc(r->pool, len); > if (b->pos == NULL) { > return NULL; I've tried this as well, and decided that "if (len == sizeof(...))" is slightly more readable, and also produces smaller patch to your code. No strict preference though, feel free to use any variant you think is better. -- Maxim Dounin http://nginx.org/ From mdounin at mdounin.ru Tue Jun 6 12:35:30 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Tue, 6 Jun 2017 15:35:30 +0300 Subject: [PATCH 3 of 3] Headers filter: added "add_trailer" directive In-Reply-To: References: <20170605175245.GZ55433@mdounin.ru> Message-ID: <20170606123530.GE55433@mdounin.ru> Hello! On Mon, Jun 05, 2017 at 09:59:45PM -0700, Piotr Sikora via nginx-devel wrote: > Hey Maxim, > > > It doesn't look like "if (h[i].value.value.len)" is needed here. > > It is either true, or the "add_trailer" directive is nop and we > > already know this while parsing the configuration. > > > > - if (h[i].value.value.len) { > > - r->expect_trailers = 1; > > - break; > > - } > > + r->expect_trailers = 1; > > + break; > > Well, both "add_header" and "add_trailer" allow setting something like: > > add_trailer Empty ""; > > which will get added to headers / trailers list. > > I've added this extra check to avoid forcing chunked encoding with > such configuration. > > Maybe we should reject it during configuration instead, or ignore this > case and let it force chunked encoding? Which one do you prefer? I think it is perfectly ok to ignore this and let if force chunked encoding (and this is what the suggested change does). -- Maxim Dounin http://nginx.org/ From mdounin at mdounin.ru Tue Jun 6 14:11:39 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Tue, 6 Jun 2017 17:11:39 +0300 Subject: [PATCH] Fix compilation on MinGW64 In-Reply-To: References: Message-ID: <20170606141139.GI55433@mdounin.ru> Hello! On Tue, Jun 06, 2017 at 01:57:39PM +0300, Orgad Shaneh wrote: > I already proposed a similar patch (without MSYS) on November, but it > was unnoticed since then. > --- > auto/configure | 2 +- > 1 file changed, 1 insertion(+), 1 deletion(-) > > diff --git a/auto/configure b/auto/configure > index ceff15e4..107c2b5f 100755 > --- a/auto/configure > +++ b/auto/configure > @@ -36,7 +36,7 @@ if test -z "$NGX_PLATFORM"; then > NGX_PLATFORM="$NGX_SYSTEM:$NGX_RELEASE:$NGX_MACHINE"; > > case "$NGX_SYSTEM" in > - MINGW32_*) > + MINGW32_*|MINGW64_*|MSYS_*) > NGX_PLATFORM=win32 > ;; > esac > -- > 2.13.0.windows.1.7.g80a6209eb5 A review of your previous patch can be found here: http://mailman.nginx.org/pipermail/nginx-devel/2016-December/009233.html It still applies. -- Maxim Dounin http://nginx.org/ From mdounin at mdounin.ru Tue Jun 6 14:12:29 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Tue, 6 Jun 2017 17:12:29 +0300 Subject: [PATCH] Use .exe for binaries for all win32 compilers In-Reply-To: References: Message-ID: <20170606141229.GJ55433@mdounin.ru> Hello! On Tue, Jun 06, 2017 at 01:58:17PM +0300, Orgad Shaneh wrote: > --- > auto/cc/conf | 4 +++- > 1 file changed, 3 insertions(+), 1 deletion(-) > > diff --git a/auto/cc/conf b/auto/cc/conf > index afbca62b..19a231aa 100644 > --- a/auto/cc/conf > +++ b/auto/cc/conf > @@ -144,7 +144,9 @@ fi > CFLAGS="$CFLAGS $NGX_CC_OPT" > NGX_TEST_LD_OPT="$NGX_LD_OPT" > > -if [ "$NGX_PLATFORM" != win32 ]; then > +if [ "$NGX_PLATFORM" = win32 ]; then > + ngx_binext=".exe" > +else > > if test -n "$NGX_LD_OPT"; then > ngx_feature=--with-ld-opt=\"$NGX_LD_OPT\" > -- > 2.13.0.windows.1.7.g80a6209eb5 http://mailman.nginx.org/pipermail/nginx-devel/2016-December/009234.html -- Maxim Dounin http://nginx.org/ From orgads at gmail.com Tue Jun 6 14:47:54 2017 From: orgads at gmail.com (Orgad Shaneh) Date: Tue, 6 Jun 2017 17:47:54 +0300 Subject: [PATCH] Configure: Fix compilation on MSYS2 / MinGW64 Message-ID: --- auto/configure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/auto/configure b/auto/configure index ceff15e4..107c2b5f 100755 --- a/auto/configure +++ b/auto/configure @@ -36,7 +36,7 @@ if test -z "$NGX_PLATFORM"; then NGX_PLATFORM="$NGX_SYSTEM:$NGX_RELEASE:$NGX_MACHINE"; case "$NGX_SYSTEM" in - MINGW32_*) + MINGW32_*|MINGW64_*|MSYS_*) NGX_PLATFORM=win32 ;; esac -- 2.13.0.windows.1.7.g80a6209eb5 From orgads at gmail.com Tue Jun 6 14:48:39 2017 From: orgads at gmail.com (Orgad Shaneh) Date: Tue, 6 Jun 2017 17:48:39 +0300 Subject: [PATCH] Fix compilation on MinGW64 In-Reply-To: <20170606141139.GI55433@mdounin.ru> References: <20170606141139.GI55433@mdounin.ru> Message-ID: On Tue, Jun 6, 2017 at 5:11 PM, Maxim Dounin wrote: > Hello! > > On Tue, Jun 06, 2017 at 01:57:39PM +0300, Orgad Shaneh wrote: > >> I already proposed a similar patch (without MSYS) on November, but it >> was unnoticed since then. >> --- >> auto/configure | 2 +- >> 1 file changed, 1 insertion(+), 1 deletion(-) >> >> diff --git a/auto/configure b/auto/configure >> index ceff15e4..107c2b5f 100755 >> --- a/auto/configure >> +++ b/auto/configure >> @@ -36,7 +36,7 @@ if test -z "$NGX_PLATFORM"; then >> NGX_PLATFORM="$NGX_SYSTEM:$NGX_RELEASE:$NGX_MACHINE"; >> >> case "$NGX_SYSTEM" in >> - MINGW32_*) >> + MINGW32_*|MINGW64_*|MSYS_*) >> NGX_PLATFORM=win32 >> ;; >> esac >> -- >> 2.13.0.windows.1.7.g80a6209eb5 > > A review of your previous patch can be found here: > > http://mailman.nginx.org/pipermail/nginx-devel/2016-December/009233.html > > It still applies. > > -- > Maxim Dounin > http://nginx.org/ > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel Thanks. I wasn't aware that I should stay subscribed to receive replies. Posted a fixed patch (using Git, I hope you don't mind). - Orgad From orgads at gmail.com Tue Jun 6 14:54:01 2017 From: orgads at gmail.com (Orgad Shaneh) Date: Tue, 6 Jun 2017 17:54:01 +0300 Subject: [PATCH] Use .exe for binaries for all win32 compilers In-Reply-To: <20170606141229.GJ55433@mdounin.ru> References: <20170606141229.GJ55433@mdounin.ru> Message-ID: > http://mailman.nginx.org/pipermail/nginx-devel/2016-December/009234.html Thanks. It is needed with MSYS2 / gcc. Proposing a new patch: --- auto/cc/bcc | 1 - auto/cc/conf | 7 ++++++- auto/cc/msvc | 1 - auto/cc/owc | 1 - 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/auto/cc/bcc b/auto/cc/bcc index ec82e60f..e990a9f7 100644 --- a/auto/cc/bcc +++ b/auto/cc/bcc @@ -62,7 +62,6 @@ ngx_include_opt="-I" ngx_objout="-o" ngx_binout="-e" ngx_objext="obj" -ngx_binext=".exe" ngx_long_start='@&&| ' diff --git a/auto/cc/conf b/auto/cc/conf index afbca62b..7e1186b5 100644 --- a/auto/cc/conf +++ b/auto/cc/conf @@ -14,9 +14,14 @@ ngx_pic_opt="-fPIC" ngx_objout="-o " ngx_binout="-o " ngx_objext="o" -ngx_binext= ngx_modext=".so" +if [ "$NGX_PLATFORM" = win32 ]; then + ngx_binext=".exe" +else + ngx_binext= +fi + ngx_long_start= ngx_long_end= diff --git a/auto/cc/msvc b/auto/cc/msvc index 4eef1010..82572529 100644 --- a/auto/cc/msvc +++ b/auto/cc/msvc @@ -142,7 +142,6 @@ ngx_pic_opt= ngx_objout="-Fo" ngx_binout="-Fe" ngx_objext="obj" -ngx_binext=".exe" ngx_long_start='@<< ' diff --git a/auto/cc/owc b/auto/cc/owc index a063aa34..f7fd88c9 100644 --- a/auto/cc/owc +++ b/auto/cc/owc @@ -84,7 +84,6 @@ ngx_include_opt="-i=" ngx_objout="-fo" ngx_binout="-fe=" ngx_objext="obj" -ngx_binext=".exe" ngx_regex_dirsep='\\' ngx_dirsep="\\" -- 2.13.0.windows.1.7.g80a6209eb5 From mdounin at mdounin.ru Tue Jun 6 16:51:57 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Tue, 6 Jun 2017 19:51:57 +0300 Subject: [PATCH] Fix compilation on MinGW64 In-Reply-To: References: <20170606141139.GI55433@mdounin.ru> Message-ID: <20170606165157.GK55433@mdounin.ru> Hello! On Tue, Jun 06, 2017 at 05:48:39PM +0300, Orgad Shaneh wrote: > On Tue, Jun 6, 2017 at 5:11 PM, Maxim Dounin wrote: > > Hello! > > > > On Tue, Jun 06, 2017 at 01:57:39PM +0300, Orgad Shaneh wrote: > > > >> I already proposed a similar patch (without MSYS) on November, but it > >> was unnoticed since then. > >> --- > >> auto/configure | 2 +- > >> 1 file changed, 1 insertion(+), 1 deletion(-) > >> > >> diff --git a/auto/configure b/auto/configure > >> index ceff15e4..107c2b5f 100755 > >> --- a/auto/configure > >> +++ b/auto/configure > >> @@ -36,7 +36,7 @@ if test -z "$NGX_PLATFORM"; then > >> NGX_PLATFORM="$NGX_SYSTEM:$NGX_RELEASE:$NGX_MACHINE"; > >> > >> case "$NGX_SYSTEM" in > >> - MINGW32_*) > >> + MINGW32_*|MINGW64_*|MSYS_*) > >> NGX_PLATFORM=win32 > >> ;; > >> esac > >> -- > >> 2.13.0.windows.1.7.g80a6209eb5 > > > > A review of your previous patch can be found here: > > > > http://mailman.nginx.org/pipermail/nginx-devel/2016-December/009233.html > > > > It still applies. > > > > -- > > Maxim Dounin > > http://nginx.org/ > > _______________________________________________ > > nginx-devel mailing list > > nginx-devel at nginx.org > > http://mailman.nginx.org/mailman/listinfo/nginx-devel > > Thanks. I wasn't aware that I should stay subscribed to receive replies. You can unsubscribe and/or switch to write-only mode, but unless you've used Mail-Followup-To in your messages there will be no direct replies. > Posted a fixed patch (using Git, I hope you don't mind). Mercurial is really preferred, though I was able to import this particular patch. Queued with a couple of style fixes you've missed, thanks. -- Maxim Dounin http://nginx.org/ From mdounin at mdounin.ru Tue Jun 6 17:09:52 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Tue, 6 Jun 2017 20:09:52 +0300 Subject: [PATCH] Use .exe for binaries for all win32 compilers In-Reply-To: References: <20170606141229.GJ55433@mdounin.ru> Message-ID: <20170606170952.GL55433@mdounin.ru> Hello! On Tue, Jun 06, 2017 at 05:54:01PM +0300, Orgad Shaneh wrote: > > http://mailman.nginx.org/pipermail/nginx-devel/2016-December/009234.html > > Thanks. It is needed with MSYS2 / gcc. Proposing a new patch: > > --- > auto/cc/bcc | 1 - > auto/cc/conf | 7 ++++++- > auto/cc/msvc | 1 - > auto/cc/owc | 1 - > 4 files changed, 6 insertions(+), 4 deletions(-) > > diff --git a/auto/cc/bcc b/auto/cc/bcc > index ec82e60f..e990a9f7 100644 > --- a/auto/cc/bcc > +++ b/auto/cc/bcc > @@ -62,7 +62,6 @@ ngx_include_opt="-I" > ngx_objout="-o" > ngx_binout="-e" > ngx_objext="obj" > -ngx_binext=".exe" > > ngx_long_start='@&&| > ' > diff --git a/auto/cc/conf b/auto/cc/conf > index afbca62b..7e1186b5 100644 > --- a/auto/cc/conf > +++ b/auto/cc/conf > @@ -14,9 +14,14 @@ ngx_pic_opt="-fPIC" > ngx_objout="-o " > ngx_binout="-o " > ngx_objext="o" > -ngx_binext= > ngx_modext=".so" > > +if [ "$NGX_PLATFORM" = win32 ]; then > + ngx_binext=".exe" > +else > + ngx_binext= > +fi > + > ngx_long_start= > ngx_long_end= > Looking more at this I tend to think that a better place would be to redefine it in auto/os/win32, like this: diff --git a/auto/os/win32 b/auto/os/win32 --- a/auto/os/win32 +++ b/auto/os/win32 @@ -13,6 +13,7 @@ NGX_ICONS="$NGX_WIN32_ICONS" SELECT_SRCS=$WIN32_SELECT_SRCS ngx_pic_opt= +ngx_binext=".exe" case "$NGX_CC_NAME" in Full patch modified accordingly provided below. Please test if it works for you. # HG changeset patch # User Orgad Shaneh # Date 1496767054 -10800 # Tue Jun 06 19:37:34 2017 +0300 # Node ID 6c9b1238cf5c99ffc5a8a449ce738606e312350e # Parent 23bea7aaebe287722ec5b5252e145da55d7906a9 Configure: use .exe for binaries for all win32 compilers. diff --git a/auto/cc/bcc b/auto/cc/bcc --- a/auto/cc/bcc +++ b/auto/cc/bcc @@ -62,7 +62,6 @@ ngx_include_opt="-I" ngx_objout="-o" ngx_binout="-e" ngx_objext="obj" -ngx_binext=".exe" ngx_long_start='@&&| ' diff --git a/auto/cc/msvc b/auto/cc/msvc --- a/auto/cc/msvc +++ b/auto/cc/msvc @@ -142,7 +142,6 @@ ngx_pic_opt= ngx_objout="-Fo" ngx_binout="-Fe" ngx_objext="obj" -ngx_binext=".exe" ngx_long_start='@<< ' diff --git a/auto/cc/owc b/auto/cc/owc --- a/auto/cc/owc +++ b/auto/cc/owc @@ -84,7 +84,6 @@ ngx_include_opt="-i=" ngx_objout="-fo" ngx_binout="-fe=" ngx_objext="obj" -ngx_binext=".exe" ngx_regex_dirsep='\\' ngx_dirsep="\\" diff --git a/auto/os/win32 b/auto/os/win32 --- a/auto/os/win32 +++ b/auto/os/win32 @@ -13,6 +13,7 @@ NGX_ICONS="$NGX_WIN32_ICONS" SELECT_SRCS=$WIN32_SELECT_SRCS ngx_pic_opt= +ngx_binext=".exe" case "$NGX_CC_NAME" in -- Maxim Dounin http://nginx.org/ From Nate.Karstens at garmin.com Wed Jun 7 05:02:52 2017 From: Nate.Karstens at garmin.com (Karstens, Nate) Date: Wed, 7 Jun 2017 05:02:52 +0000 Subject: PSK Support In-Reply-To: <20170605183944.GB55433@mdounin.ru> References: <145451D4E6785E4DA4DFA353A58CB7B2015E15B7E3@OLAWPA-EXMB04.ad.garmin.com> <20170605130916.GX55433@mdounin.ru> <145451D4E6785E4DA4DFA353A58CB7B2015E15C57F@OLAWPA-EXMB04.ad.garmin.com> <20170605183944.GB55433@mdounin.ru> Message-ID: <145451D4E6785E4DA4DFA353A58CB7B2015E15CE05@OLAWPA-EXMB04.ad.garmin.com> Maxim, The biggest downside that I can see to the dummy certificate approach is documentation. Using a dummy certificate wasn't immediately obvious to me, though perhaps my familiarity with OpenSSL's "nocert" option may have affected that. Which do you think would be easier for the user to find in the documentation: 1) a description of the dummy certificate approach under the "ssl_certificate" directive, 2) a separate directive ("ssl_nocert"), or 3) an explicit option to the "ssl_certificate" directive (e.g., " Syntax: ssl_certificate file | off;")? I'm OK with changing it to read from a password file (formatted in a manner similar to stunnel) that is searched as needed (an "ssl_psk_file" directive). Would it be OK to support multiple files and stipulate that files are searched in the order that they are included in nginx.conf? Can we support both ASCII and binary PSKs? RFC 4279 section 5.4 seems to require both types, and I need binary keys for my application :). Maybe a parameter to the "ssl_psk_file" directive could indicate how the PSKs are stored in the file? Thanks, Nate -----Original Message----- From: nginx-devel [mailto:nginx-devel-bounces at nginx.org] On Behalf Of Maxim Dounin Sent: Monday, June 05, 2017 1:40 PM To: nginx-devel at nginx.org Subject: Re: PSK Support Hello! On Mon, Jun 05, 2017 at 02:08:15PM +0000, Karstens, Nate wrote: > Maxim, > > Thanks for the reply. I understand your concerns about PSK. We > discussed it quite a bit, but ultimately decided that a PKI was not > practical for our environment. We have to rely on the end user to > configure security and any solution using PKI would be so difficult to > work with that they just wouldn't bother with security at all. Ok, understood. I think that PSK can be a reasonable alternative to using plain http in many cases. > I considered some alternatives on the "ssl_nocert" option. My > preference would have been to analyze the supported cipher suites > (from "ssl_ciphers") and determine if any include a PSK, but it does > not look like OpenSSL exposes APIs to accomplish this. By default, nginx uses "HIGH:!aNULL:!MD5" as ciphers list, and this includes various PSK ciphers as well, so this approach doesn't look working even if there were appropriate APIs. > Using a dummy certificate seemed more complicated than the other two > suggestions you had (using "ssl_certificate" with a value of "off" or > disabling the tests if there are PSK secrets), so I'd prefer one of > those two. What is your preference? Using a dummy certificate has an obvious benefit of not requiring any changes to the code, and might actually be a good starting option. Disabling the tests with PSK secrets might not work as expected when they are defined at the http{} level. Using "ssl_certificate off" is obviously most explicit of all options, but I would rather consider a dummy certificate instead for now, as long as there are no other downsides. > One advantage of the PSK path concept is that it provides a lot of > flexibility. It allows, for example, multiple applications to each > independently manage their own PSKs without the need to coordinate > changes to a single file (note that in this scenario each application > would want to use $ssl_psk_identity to check the key). On the one hand, it is a plus. On the other - it is a nightmare when something goes wrong. I would rather avoid such approach. > stunnel uses a single file and seems to assume that keys will be ASCII > strings. Its format, for example, would not allow NUL to appear in the > string, as that would terminate the key early and, at best, lead to a > reduced key size. Yes, and stunnel author considers this to be a feature, see https://www.stunnel.org/pipermail/stunnel-users/2015-October/005275.html. If you are targeting end-users, it might be actually easier to use sufficiently long printable keys then arbitrary binary strings. > I might be mistaken, but wouldn't changing a certificate also require > reloading the configuration? Do you have some ideas on how this could > be done without requiring a reload? Yes, changing a certificate requires a reload. But the "path" concept is generally used in SSL where appropriate filesystem lookups are done on the fly, in contrast to loading a file into memory and then working with the data from memory. Consider "openssl verify -CApath" vs. "openssl verify -CAfile". Additionally, PSK keys look much more dynamic than certificates, as adding a user requires configuration changes. With PKI, you don't need any certificate changes on the server to add a user. With PSK, you have to add a key to introduce a new user. Overall, PSK seems to be very close to basic authentication, and it might worth looking how it is implemented in the auth_basic module (in short: the password file is searched on each request). -- Maxim Dounin http://nginx.org/ _______________________________________________ nginx-devel mailing list nginx-devel at nginx.org http://mailman.nginx.org/mailman/listinfo/nginx-devel ________________________________ CONFIDENTIALITY NOTICE: This email and any attachments are for the sole use of the intended recipient(s) and contain information that may be Garmin confidential and/or Garmin legally privileged. If you have received this email in error, please notify the sender by reply email and delete the message. Any disclosure, copying, distribution or use of this communication (including attachments) by someone other than the intended recipient is prohibited. Thank you. From tommy.lindgren at gmail.com Wed Jun 7 10:38:59 2017 From: tommy.lindgren at gmail.com (Tommy Lindgren) Date: Wed, 07 Jun 2017 18:38:59 +0800 Subject: Implement support for serving growing files Message-ID: <149683193947.29398.17315132378386264450@argon> Hi, I want nginx to serve growing files, i.e. if mtime was changed recently the response transfer-encoding should be chunked and when nginx hits EOF it waits a bit to see if more data is written to the file. Any new data is transparently written to the client. When file hasn't been updated for X seconds, the request is closed. As far as I can tell there's no such support. Any hints how to implement this? Can I solve this using a header/body filter module? Or I need some kind of streaming-ish module? Any existing module that is suitable to use as a starting point? Thanks, Tommy From xeioex at nginx.com Wed Jun 7 11:16:53 2017 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Wed, 07 Jun 2017 11:16:53 +0000 Subject: [njs] Object.keys() method. Message-ID: details: http://hg.nginx.org/njs/rev/fb703c8f4292 branches: changeset: 352:fb703c8f4292 user: Dmitry Volyntsev date: Wed Jun 07 14:12:23 2017 +0300 description: Object.keys() method. diffstat: njs/njs_object.c | 101 +++++++++++++++++++++++++++++++++++++++++++++++ njs/njs_vm.h | 13 ++++++ njs/test/njs_unit_test.c | 19 ++++++++ 3 files changed, 133 insertions(+), 0 deletions(-) diffs (178 lines): diff -r 02a59fe82c7a -r fb703c8f4292 njs/njs_object.c --- a/njs/njs_object.c Mon Jun 05 14:59:28 2017 +0300 +++ b/njs/njs_object.c Wed Jun 07 14:12:23 2017 +0300 @@ -19,7 +19,9 @@ #include #include #include +#include #include +#include #include @@ -318,6 +320,97 @@ njs_object_create(njs_vm_t *vm, njs_valu } +static njs_ret_t +njs_object_keys(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + size_t size; + uint32_t i, n, keys_length, array_length; + njs_value_t *value; + njs_array_t *keys, *array; + nxt_lvlhsh_t *hash; + njs_object_prop_t *prop; + nxt_lvlhsh_each_t lhe; + + if (nargs < 2 || !njs_is_object(&args[1])) { + vm->exception = &njs_exception_type_error; + return NXT_ERROR; + } + + keys_length = 0; + array_length = 0; + + if (njs_is_array(&args[1])) { + array = args[1].data.u.array; + array_length = array->length; + + for (i = 0; i < array_length; i++) { + if (njs_is_valid(&array->start[i])) { + keys_length++; + } + } + } + + memset(&lhe, 0, sizeof(nxt_lvlhsh_each_t)); + lhe.proto = &njs_object_hash_proto; + + hash = &args[1].data.u.object->hash; + + for ( ;; ) { + prop = nxt_lvlhsh_each(hash, &lhe); + + if (prop == NULL) { + break; + } + + if (prop->enumerable) { + keys_length++; + } + } + + keys = njs_array_alloc(vm, keys_length, NJS_ARRAY_SPARE); + if (nxt_slow_path(keys == NULL)) { + return NXT_ERROR; + } + + n = 0; + + for (i = 0; i < array_length; i++) { + if (njs_is_valid(&array->start[i])) { + value = &keys->start[n++]; + /* + * The maximum array index is 4294967294, so + * it can be stored as a short string inside value. + */ + size = snprintf((char *) njs_string_short_start(value), + NJS_STRING_SHORT, "%u", i); + njs_string_short_set(value, size, size); + } + } + + memset(&lhe, 0, sizeof(nxt_lvlhsh_each_t)); + lhe.proto = &njs_object_hash_proto; + + for ( ;; ) { + prop = nxt_lvlhsh_each(hash, &lhe); + + if (prop == NULL) { + break; + } + + if (prop->enumerable) { + njs_string_copy(&keys->start[n++], &prop->name); + } + } + + vm->retval.data.u.array = keys; + vm->retval.type = NJS_ARRAY; + vm->retval.data.truth = 1; + + return NXT_OK; +} + + /* * The __proto__ property of booleans, numbers and strings primitives, * of objects created by Boolean(), Number(), and String() constructors, @@ -456,6 +549,14 @@ static const njs_object_prop_t njs_obje .name = njs_string("create"), .value = njs_native_function(njs_object_create, 0, 0), }, + + /* Object.keys(). */ + { + .type = NJS_METHOD, + .name = njs_string("keys"), + .value = njs_native_function(njs_object_keys, 0, + NJS_SKIP_ARG, NJS_OBJECT_ARG), + }, }; diff -r 02a59fe82c7a -r fb703c8f4292 njs/njs_vm.h --- a/njs/njs_vm.h Mon Jun 05 14:59:28 2017 +0300 +++ b/njs/njs_vm.h Wed Jun 07 14:12:23 2017 +0300 @@ -408,6 +408,19 @@ typedef njs_ret_t (*njs_vmcode_operation #define njs_string_truth(value, size) +#define njs_string_short_start(value) \ + (value)->short_string.start + + +#define njs_string_short_set(value, _size, _length) \ + do { \ + (value)->type = NJS_STRING; \ + njs_string_truth(value, _size); \ + (value)->short_string.size = _size; \ + (value)->short_string.length = _length; \ + } while (0) + + #define njs_is_primitive(value) \ ((value)->type <= NJS_STRING) diff -r 02a59fe82c7a -r fb703c8f4292 njs/test/njs_unit_test.c --- a/njs/test/njs_unit_test.c Mon Jun 05 14:59:28 2017 +0300 +++ b/njs/test/njs_unit_test.c Wed Jun 07 14:12:23 2017 +0300 @@ -5843,6 +5843,25 @@ static njs_unit_test_t njs_test[] = { nxt_string("var o = Object.create(null); '__proto__' in o"), nxt_string("false") }, + { nxt_string("var o = {a:1, b:2, c:3};" + "Object.keys(o)"), + nxt_string("a,b,c") }, + + { nxt_string("var a = []; a.one = 7; Object.keys(a)"), + nxt_string("one") }, + + { nxt_string("var a = [,,]; a.one = 7; Object.keys(a)"), + nxt_string("one") }, + + { nxt_string("var a = [,6,,3]; a.one = 7; Object.keys(a)"), + nxt_string("1,3,one") }, + + { nxt_string("Object.keys('a')"), + nxt_string("TypeError") }, + + { nxt_string("Object.keys(1)"), + nxt_string("TypeError") }, + { nxt_string("var d = new Date(''); d +' '+ d.getTime()"), nxt_string("Invalid Date NaN") }, From xeioex at nginx.com Wed Jun 7 12:45:10 2017 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Wed, 07 Jun 2017 12:45:10 +0000 Subject: [njs] Fixed building by GCC 4.2. Message-ID: details: http://hg.nginx.org/njs/rev/7196ac334d64 branches: changeset: 353:7196ac334d64 user: Dmitry Volyntsev date: Wed Jun 07 15:45:01 2017 +0300 description: Fixed building by GCC 4.2. diffstat: njs/njs_object.c | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diffs (11 lines): diff -r fb703c8f4292 -r 7196ac334d64 njs/njs_object.c --- a/njs/njs_object.c Wed Jun 07 14:12:23 2017 +0300 +++ b/njs/njs_object.c Wed Jun 07 15:45:01 2017 +0300 @@ -337,6 +337,7 @@ njs_object_keys(njs_vm_t *vm, njs_value_ return NXT_ERROR; } + array = NULL; keys_length = 0; array_length = 0; From hucong.c at foxmail.com Wed Jun 7 14:00:26 2017 From: hucong.c at foxmail.com (=?utf-8?B?6IOh6IGqIChodWNjKQ==?=) Date: Wed, 7 Jun 2017 22:00:26 +0800 Subject: nginx development guide In-Reply-To: <93394254-72e9-35aa-4644-ff0d5d12f282@nginx.com> References: <20170213093825.GA27852@vlpc.nginx.com> <93394254-72e9-35aa-4644-ff0d5d12f282@nginx.com> Message-ID: Hello, There are two possible errors in http://nginx.org/en/docs/dev/development_guide.html#http_load_balancing >init(r, us) ? initializes per-request ngx_http_upstream_peer_t.peer (not to be confused with the >ngx_http_upstream_srv_conf_t.peer described above which is per-upstream) structure that is used >for load balancing. It will be passed as data argument to all callbacks that deal with server selection. Maybe is "initializes some fields (data, get, free, etc.) of per-request ngx_http_upstream_t.peer (...) " >When nginx has to pass a request to another host for processing, it uses a configured load balancing method >to obtain an address to connect to. The method is taken from the ngx_http_upstream_peer_t.peer object of >type ngx_peer_connection_t: It should be "The method is taken from the ngx_http_upstream_t.peer object of type ngx_peer_connection_t:" ------------------ Original ------------------ From: "Maxim Konovalov";; Date: Apr 20, 2017 To: "nginx-devel"; Subject: Re: nginx development guide Hello, On 13/02/2017 12:38, Vladimir Homutov wrote: > Hello all! > > We are glad to share with first results of our ongoing efforts to create > documentation for nginx developers: the development guide document [1]. > > The guide is not yet 100% complete and more parts to follow. > > Of course, your feedback is welcome. > > [1] http://nginx.org/en/docs/dev/development_guide.html We added a bunch of new content recently. Feedbacks/comments are welcome. -- Maxim Konovalov _______________________________________________ nginx-devel mailing list nginx-devel at nginx.org http://mailman.nginx.org/mailman/listinfo/nginx-devel From zelenkov at nginx.com Wed Jun 7 14:50:02 2017 From: zelenkov at nginx.com (Andrey Zelenkov) Date: Wed, 07 Jun 2017 14:50:02 +0000 Subject: [njs] Fixed zero basis handling for scientific notation. Message-ID: details: http://hg.nginx.org/njs/rev/2cba0bd90189 branches: changeset: 354:2cba0bd90189 user: Andrey Zelenkov date: Wed Jun 07 16:36:13 2017 +0300 description: Fixed zero basis handling for scientific notation. diffstat: njs/njs_number.c | 6 ++++-- njs/test/njs_unit_test.c | 6 ++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diffs (32 lines): diff -r 7196ac334d64 -r 2cba0bd90189 njs/njs_number.c --- a/njs/njs_number.c Wed Jun 07 15:45:01 2017 +0300 +++ b/njs/njs_number.c Wed Jun 07 16:36:13 2017 +0300 @@ -154,8 +154,10 @@ njs_number_dec_parse(u_char **start, u_c p++; } - exponent = minus ? -exponent : exponent; - num = num * pow(10.0, exponent); + if (num != 0) { + exponent = minus ? -exponent : exponent; + num = num * pow(10.0, exponent); + } } } diff -r 7196ac334d64 -r 2cba0bd90189 njs/test/njs_unit_test.c --- a/njs/test/njs_unit_test.c Wed Jun 07 15:45:01 2017 +0300 +++ b/njs/test/njs_unit_test.c Wed Jun 07 16:36:13 2017 +0300 @@ -191,6 +191,12 @@ static njs_unit_test_t njs_test[] = { nxt_string("1.0e308"), nxt_string("1e+308") }, + { nxt_string("0e309"), + nxt_string("0") }, + + { nxt_string("0e-309"), + nxt_string("0") }, + { nxt_string("1e"), nxt_string("SyntaxError: Unexpected token \"e\" in 1") }, From zelenkov at nginx.com Wed Jun 7 14:50:04 2017 From: zelenkov at nginx.com (Andrey Zelenkov) Date: Wed, 07 Jun 2017 14:50:04 +0000 Subject: [njs] More scientific notation tests. Message-ID: details: http://hg.nginx.org/njs/rev/edf64bf9677c branches: changeset: 355:edf64bf9677c user: Andrey Zelenkov date: Wed Jun 07 16:36:17 2017 +0300 description: More scientific notation tests. diffstat: njs/test/njs_unit_test.c | 6 ++++++ 1 files changed, 6 insertions(+), 0 deletions(-) diffs (16 lines): diff -r 2cba0bd90189 -r edf64bf9677c njs/test/njs_unit_test.c --- a/njs/test/njs_unit_test.c Wed Jun 07 16:36:13 2017 +0300 +++ b/njs/test/njs_unit_test.c Wed Jun 07 16:36:17 2017 +0300 @@ -197,6 +197,12 @@ static njs_unit_test_t njs_test[] = { nxt_string("0e-309"), nxt_string("0") }, + { nxt_string("1e309"), + nxt_string("Infinity") }, + + { nxt_string("-1e309"), + nxt_string("-Infinity") }, + { nxt_string("1e"), nxt_string("SyntaxError: Unexpected token \"e\" in 1") }, From xeioex at nginx.com Wed Jun 7 14:59:38 2017 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Wed, 07 Jun 2017 14:59:38 +0000 Subject: [njs] Object.defineProperty() method. Message-ID: details: http://hg.nginx.org/njs/rev/a0bc58cc65d5 branches: changeset: 356:a0bc58cc65d5 user: Dmitry Volyntsev date: Wed Jun 07 17:57:40 2017 +0300 description: Object.defineProperty() method. diffstat: njs/njs_object.c | 112 +++++++++++++++++++++++++++++++++++++++++++++++ njs/njs_object_hash.h | 51 +++++++++++++++++++++ njs/njs_vm.c | 4 +- njs/test/njs_unit_test.c | 58 ++++++++++++++++++++++++ 4 files changed, 224 insertions(+), 1 deletions(-) diffs (291 lines): diff -r edf64bf9677c -r a0bc58cc65d5 njs/njs_object.c --- a/njs/njs_object.c Wed Jun 07 16:36:17 2017 +0300 +++ b/njs/njs_object.c Wed Jun 07 17:57:40 2017 +0300 @@ -412,6 +412,109 @@ njs_object_keys(njs_vm_t *vm, njs_value_ } +static njs_ret_t +njs_object_define_property(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + nxt_int_t ret; + nxt_str_t key; + njs_value_t *name; + njs_object_t *object, *descriptor; + njs_object_prop_t *prop, *pr; + nxt_lvlhsh_query_t lhq, pq; + + if (nargs < 4 || !njs_is_object(&args[1]) || !njs_is_object(&args[3])) { + vm->exception = &njs_exception_type_error; + return NXT_ERROR; + } + + object = args[1].data.u.object; + name = &args[2]; + descriptor = args[3].data.u.object; + + if (name->short_string.size != NJS_STRING_LONG) { + key.start = name->short_string.start; + key.length = name->short_string.length; + + } else { + key.start = name->data.u.string->start; + key.length = name->data.string_size; + } + + lhq.key = key; + lhq.key_hash = nxt_djb_hash(key.start, key.length); + lhq.proto = &njs_object_hash_proto; + + ret = nxt_lvlhsh_find(&object->hash, &lhq); + + if (ret != NXT_OK) { + prop = njs_object_prop_alloc(vm, name); + + if (nxt_slow_path(prop == NULL)) { + return NXT_ERROR; + } + + prop->configurable = 0; + prop->enumerable = 0; + prop->writable = 0; + + lhq.value = prop; + + } else { + prop = lhq.value; + } + + pq.key = nxt_string_value("value"); + pq.key_hash = NJS_VALUE_HASH; + pq.proto = &njs_object_hash_proto; + + pr = njs_object_property(vm, descriptor, &pq); + + if (pr != NULL) { + prop->value = pr->value; + } + + pq.key = nxt_string_value("configurable"); + pq.key_hash = NJS_CONFIGURABLE_HASH; + + pr = njs_object_property(vm, descriptor, &pq); + + if (pr != NULL) { + prop->configurable = pr->value.data.truth; + } + + pq.key = nxt_string_value("enumerable"); + pq.key_hash = NJS_ENUMERABLE_HASH; + + pr = njs_object_property(vm, descriptor, &pq); + + if (pr != NULL) { + prop->enumerable = pr->value.data.truth; + } + + pq.key = nxt_string_value("writable"); + pq.key_hash = NJS_WRITABABLE_HASH; + + pr = njs_object_property(vm, descriptor, &pq); + + if (pr != NULL) { + prop->writable = pr->value.data.truth; + } + + lhq.replace = 0; + lhq.pool = vm->mem_cache_pool; + + ret = nxt_lvlhsh_insert(&object->hash, &lhq); + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } + + vm->retval = args[1]; + + return NXT_OK; +} + + /* * The __proto__ property of booleans, numbers and strings primitives, * of objects created by Boolean(), Number(), and String() constructors, @@ -558,6 +661,15 @@ static const njs_object_prop_t njs_obje .value = njs_native_function(njs_object_keys, 0, NJS_SKIP_ARG, NJS_OBJECT_ARG), }, + + /* Object.defineProperty(). */ + { + .type = NJS_METHOD, + .name = njs_string("defineProperty"), + .value = njs_native_function(njs_object_define_property, 0, + NJS_SKIP_ARG, NJS_OBJECT_ARG, + NJS_STRING_ARG, NJS_OBJECT_ARG), + }, }; diff -r edf64bf9677c -r a0bc58cc65d5 njs/njs_object_hash.h --- a/njs/njs_object_hash.h Wed Jun 07 16:36:17 2017 +0300 +++ b/njs/njs_object_hash.h Wed Jun 07 17:57:40 2017 +0300 @@ -8,6 +8,22 @@ #define _NJS_OBJECT_HASH_H_INCLUDED_ +#define NJS_CONFIGURABLE_HASH \ + nxt_djb_hash_add( \ + nxt_djb_hash_add( \ + nxt_djb_hash_add( \ + nxt_djb_hash_add( \ + nxt_djb_hash_add( \ + nxt_djb_hash_add( \ + nxt_djb_hash_add( \ + nxt_djb_hash_add( \ + nxt_djb_hash_add( \ + nxt_djb_hash_add( \ + nxt_djb_hash_add( \ + nxt_djb_hash_add(NXT_DJB_HASH_INIT, \ + 'c'), 'o'), 'n'), 'f'), 'i'), 'g'), 'u'), 'r'), 'a'), 'b'), 'l'), 'e') + + #define NJS_CONSTRUCTOR_HASH \ nxt_djb_hash_add( \ nxt_djb_hash_add( \ @@ -23,6 +39,20 @@ 'c'), 'o'), 'n'), 's'), 't'), 'r'), 'u'), 'c'), 't'), 'o'), 'r') +#define NJS_ENUMERABLE_HASH \ + nxt_djb_hash_add( \ + nxt_djb_hash_add( \ + nxt_djb_hash_add( \ + nxt_djb_hash_add( \ + nxt_djb_hash_add( \ + nxt_djb_hash_add( \ + nxt_djb_hash_add( \ + nxt_djb_hash_add( \ + nxt_djb_hash_add( \ + nxt_djb_hash_add(NXT_DJB_HASH_INIT, \ + 'e'), 'n'), 'u'), 'm'), 'e'), 'r'), 'a'), 'b'), 'l'), 'e') + + #define NJS_INDEX_HASH \ nxt_djb_hash_add( \ nxt_djb_hash_add( \ @@ -74,6 +104,15 @@ 't'), 'o'), 'S'), 't'), 'r'), 'i'), 'n'), 'g') +#define NJS_VALUE_HASH \ + nxt_djb_hash_add( \ + nxt_djb_hash_add( \ + nxt_djb_hash_add( \ + nxt_djb_hash_add( \ + nxt_djb_hash_add(NXT_DJB_HASH_INIT, \ + 'v'), 'a'), 'l'), 'u'), 'e') + + #define NJS_VALUE_OF_HASH \ nxt_djb_hash_add( \ nxt_djb_hash_add( \ @@ -100,4 +139,16 @@ 't'), 'o'), 'I'), 'S'), 'O'), 'S'), 't'), 'r'), 'i'), 'n'), 'g') +#define NJS_WRITABABLE_HASH \ + nxt_djb_hash_add( \ + nxt_djb_hash_add( \ + nxt_djb_hash_add( \ + nxt_djb_hash_add( \ + nxt_djb_hash_add( \ + nxt_djb_hash_add( \ + nxt_djb_hash_add( \ + nxt_djb_hash_add(NXT_DJB_HASH_INIT, \ + 'w'), 'r'), 'i'), 't'), 'a'), 'b'), 'l'), 'e') + + #endif /* _NJS_OBJECT_HASH_H_INCLUDED_ */ diff -r edf64bf9677c -r a0bc58cc65d5 njs/njs_vm.c --- a/njs/njs_vm.c Wed Jun 07 16:36:17 2017 +0300 +++ b/njs/njs_vm.c Wed Jun 07 17:57:40 2017 +0300 @@ -746,7 +746,9 @@ njs_vmcode_property_set(njs_vm_t *vm, nj return ret; } - prop->value = *value; + if (prop->writable) { + prop->value = *value; + } return sizeof(njs_vmcode_prop_set_t); } diff -r edf64bf9677c -r a0bc58cc65d5 njs/test/njs_unit_test.c --- a/njs/test/njs_unit_test.c Wed Jun 07 16:36:17 2017 +0300 +++ b/njs/test/njs_unit_test.c Wed Jun 07 17:57:40 2017 +0300 @@ -5874,6 +5874,64 @@ static njs_unit_test_t njs_test[] = { nxt_string("Object.keys(1)"), nxt_string("TypeError") }, + { nxt_string("var o = {}; Object.defineProperty(o, 'a', {}); o.a"), + nxt_string("undefined") }, + + { nxt_string("Object.defineProperty({}, 'a', {value:1})"), + nxt_string("[object Object]") }, + + { nxt_string("var o = {}; Object.defineProperty(o, 'a', {value:1}); o.a"), + nxt_string("1") }, + + { nxt_string("var o = {a:1, c:2}; Object.defineProperty(o, 'b', {});" + "Object.keys(o)"), + nxt_string("a,c") }, + + { nxt_string("var o = {a:1, c:2};" + "Object.defineProperty(o, 'b', {enumerable:false});" + "Object.keys(o)"), + nxt_string("a,c") }, + + { nxt_string("var o = {a:1, c:3};" + "Object.defineProperty(o, 'b', {enumerable:true});" + "Object.keys(o)"), + nxt_string("a,c,b") }, + + { nxt_string("var o = {}; Object.defineProperty(o, 'a', {}); o.a = 1; o.a"), + nxt_string("undefined") }, + + { nxt_string("var o = {}; Object.defineProperty(o, 'a', {writable:false});" + "o.a = 1; o.a"), + nxt_string("undefined") }, + + { nxt_string("var o = {}; Object.defineProperty(o, 'a', {writable:true});" + "o.a = 1; o.a"), + nxt_string("1") }, + + { nxt_string("var o = {};" + "Object.defineProperty(o, 'a', {value:1}); delete o.a; o.a"), + nxt_string("1") }, + + { nxt_string("var o = {};" + "Object.defineProperty(o, 'a', {value:1, configurable:true});" + "delete o.a; o.a"), + nxt_string("undefined") }, + + { nxt_string("var o = {};" + "Object.defineProperty(o, 'a', {value:1, configurable:false});" + "delete o.a; o.a"), + nxt_string("1") }, + + { nxt_string("var o = {};" + "Object.defineProperty(o, 'a', Object.create({value:2})); o.a"), + nxt_string("2") }, + + { nxt_string("var o = {}; Object.defineProperty()"), + nxt_string("TypeError") }, + + { nxt_string("var o = {}; Object.defineProperty(o)"), + nxt_string("TypeError") }, + { nxt_string("var d = new Date(''); d +' '+ d.getTime()"), nxt_string("Invalid Date NaN") }, From pluknet at nginx.com Wed Jun 7 15:51:51 2017 From: pluknet at nginx.com (Sergey Kandaurov) Date: Wed, 07 Jun 2017 15:51:51 +0000 Subject: [nginx] SSI: return NGX_ERROR when timefmt memory allocation failed. Message-ID: details: http://hg.nginx.org/nginx/rev/e699e6b6d76c branches: changeset: 7026:e699e6b6d76c user: Sergey Kandaurov date: Wed Jun 07 15:21:42 2017 +0300 description: SSI: return NGX_ERROR when timefmt memory allocation failed. Previously, when using NGX_HTTP_SSI_ERROR, error was ignored in ssi processing, thus timefmt could be accessed later in ngx_http_ssi_date_gmt_local_variable() as part of "set" handler, or NULL format pointer could be passed to strftime(). diffstat: src/http/modules/ngx_http_ssi_filter_module.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diffs (12 lines): diff -r 7206c3630310 -r e699e6b6d76c src/http/modules/ngx_http_ssi_filter_module.c --- a/src/http/modules/ngx_http_ssi_filter_module.c Fri Jun 02 15:05:32 2017 +0300 +++ b/src/http/modules/ngx_http_ssi_filter_module.c Wed Jun 07 15:21:42 2017 +0300 @@ -2388,7 +2388,7 @@ ngx_http_ssi_config(ngx_http_request_t * ctx->timefmt.len = value->len; ctx->timefmt.data = ngx_pnalloc(r->pool, value->len + 1); if (ctx->timefmt.data == NULL) { - return NGX_HTTP_SSI_ERROR; + return NGX_ERROR; } ngx_cpystrn(ctx->timefmt.data, value->data, value->len + 1); From pluknet at nginx.com Wed Jun 7 15:51:54 2017 From: pluknet at nginx.com (Sergey Kandaurov) Date: Wed, 07 Jun 2017 15:51:54 +0000 Subject: [nginx] Fixed segfault in try_files with nested location. Message-ID: details: http://hg.nginx.org/nginx/rev/6d0e0d982ec0 branches: changeset: 7027:6d0e0d982ec0 user: Sergey Kandaurov date: Wed Jun 07 18:46:35 2017 +0300 description: Fixed segfault in try_files with nested location. If memory allocation of a new r->uri.data storage failed, reset its length as well. Request URI is used in ngx_http_finalize_request() for debug logging. diffstat: src/http/ngx_http_core_module.c | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diffs (11 lines): diff -r e699e6b6d76c -r 6d0e0d982ec0 src/http/ngx_http_core_module.c --- a/src/http/ngx_http_core_module.c Wed Jun 07 15:21:42 2017 +0300 +++ b/src/http/ngx_http_core_module.c Wed Jun 07 18:46:35 2017 +0300 @@ -1353,6 +1353,7 @@ ngx_http_core_try_files_phase(ngx_http_r r->uri.len = alias + path.len; r->uri.data = ngx_pnalloc(r->pool, r->uri.len); if (r->uri.data == NULL) { + r->uri.len = 0; ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return NGX_OK; } From pluknet at nginx.com Wed Jun 7 15:51:56 2017 From: pluknet at nginx.com (Sergey Kandaurov) Date: Wed, 07 Jun 2017 15:51:56 +0000 Subject: [nginx] Userid: ngx_http_get_indexed_variable() error handling. Message-ID: details: http://hg.nginx.org/nginx/rev/e6f399a176e7 branches: changeset: 7028:e6f399a176e7 user: Sergey Kandaurov date: Wed Jun 07 18:46:36 2017 +0300 description: Userid: ngx_http_get_indexed_variable() error handling. When evaluating a mapped $reset_uid variable in the userid filter, if get_handler set to ngx_http_map_variable() returned an error, this previously resulted in a NULL pointer dereference. diffstat: src/http/modules/ngx_http_userid_filter_module.c | 4 ++++ 1 files changed, 4 insertions(+), 0 deletions(-) diffs (14 lines): diff -r 6d0e0d982ec0 -r e6f399a176e7 src/http/modules/ngx_http_userid_filter_module.c --- a/src/http/modules/ngx_http_userid_filter_module.c Wed Jun 07 18:46:35 2017 +0300 +++ b/src/http/modules/ngx_http_userid_filter_module.c Wed Jun 07 18:46:36 2017 +0300 @@ -472,6 +472,10 @@ ngx_http_userid_create_uid(ngx_http_requ vv = ngx_http_get_indexed_variable(r, ngx_http_userid_reset_index); + if (vv == NULL || vv->not_found) { + return NGX_ERROR; + } + if (vv->len == 0 || (vv->len == 1 && vv->data[0] == '0')) { if (conf->mark == '\0' From vl at nginx.com Wed Jun 7 16:42:06 2017 From: vl at nginx.com (Vladimir Homutov) Date: Wed, 7 Jun 2017 19:42:06 +0300 Subject: nginx development guide In-Reply-To: References: <20170213093825.GA27852@vlpc.nginx.com> <93394254-72e9-35aa-4644-ff0d5d12f282@nginx.com> Message-ID: <51e8e0e1-3862-6e51-55e4-730d71f4ab3a@nginx.com> 07.06.2017 17:00, ?? (hucc) ?????: > Hello, > > There are two possible errors in > http://nginx.org/en/docs/dev/development_guide.html#http_load_balancing > >> init(r, us) ? initializes per-request ngx_http_upstream_peer_t.peer (not to be confused with the >> ngx_http_upstream_srv_conf_t.peer described above which is per-upstream) structure that is used >> for load balancing. It will be passed as data argument to all callbacks that deal with server selection. > > Maybe is "initializes some fields (data, get, free, etc.) of per-request ngx_http_upstream_t.peer (...) " this details are described below in the text > >> When nginx has to pass a request to another host for processing, it uses a configured load balancing method >> to obtain an address to connect to. The method is taken from the ngx_http_upstream_peer_t.peer object of >> type ngx_peer_connection_t: > > It should be "The method is taken from the ngx_http_upstream_t.peer object of type ngx_peer_connection_t:" thanks, fixed From mdounin at mdounin.ru Wed Jun 7 18:13:27 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Wed, 7 Jun 2017 21:13:27 +0300 Subject: PSK Support In-Reply-To: <145451D4E6785E4DA4DFA353A58CB7B2015E15CE05@OLAWPA-EXMB04.ad.garmin.com> References: <145451D4E6785E4DA4DFA353A58CB7B2015E15B7E3@OLAWPA-EXMB04.ad.garmin.com> <20170605130916.GX55433@mdounin.ru> <145451D4E6785E4DA4DFA353A58CB7B2015E15C57F@OLAWPA-EXMB04.ad.garmin.com> <20170605183944.GB55433@mdounin.ru> <145451D4E6785E4DA4DFA353A58CB7B2015E15CE05@OLAWPA-EXMB04.ad.garmin.com> Message-ID: <20170607181327.GS55433@mdounin.ru> Hello! On Wed, Jun 07, 2017 at 05:02:52AM +0000, Karstens, Nate wrote: > Maxim, > > The biggest downside that I can see to the dummy certificate > approach is documentation. Using a dummy certificate wasn't > immediately obvious to me, though perhaps my familiarity with > OpenSSL's "nocert" option may have affected that. Which do you > think would be easier for the user to find in the documentation: > 1) a description of the dummy certificate approach under the > "ssl_certificate" directive, 2) a separate directive > ("ssl_nocert"), or 3) an explicit option to the > "ssl_certificate" directive (e.g., " Syntax: ssl_certificate > file | off;")? In most cases, normal users won't need PSK at all, or will use it combined with certificates anyway. Describing in the documentation that "if you need to use PSK only, you still have to configure a certificate" doesn't looks like a big problem to me. Note well that there is a side problem with "listen ... ssl" used in server block but no ssl_certificate configured for the server. As of now, it is not reported during configuration parsing and may result in non-obvious behaviour in some cases, see ticket #178 (https://trac.nginx.org/nginx/ticket/178). This is a separate problem though, and it can't be fixed with the changes suggested. On the other hand, properly fixing it will greatly improve user experience in many cases, including PSK. > I'm OK with changing it to read from a password file (formatted > in a manner similar to stunnel) that is searched as needed (an > "ssl_psk_file" directive). Would it be OK to support multiple > files and stipulate that files are searched in the order that > they are included in nginx.conf? I don't think that supporting multiple files is a good idea, a single one should be enough. Possible alternative names: - "ssl_psk_secrets", similar to the one used by stunnel; - "ssl_pre_shared_keys". Not sure if these are better though. > Can we support both ASCII and binary PSKs? RFC 4279 section 5.4 > seems to require both types, and I need binary keys for my > application :). Maybe a parameter to the "ssl_psk_file" > directive could indicate how the PSKs are stored in the file? We can consider providing an alternative form to allow arbitrary keys, e.g., by using hex-encoded keys. This can be done using some explicit prefix, something like this: identity:key identity:0x6b6579 identity:{HEX}6b6579 (The last variant is in line with "{scheme}data" syntax as used in auth_basic_user_file. Not sure if we need it here, just "0x" might be easier.) -- Maxim Dounin http://nginx.org/ From vbart at nginx.com Wed Jun 7 19:16:17 2017 From: vbart at nginx.com (Valentin V. Bartenev) Date: Wed, 07 Jun 2017 22:16:17 +0300 Subject: [PATCH 2 of 3] HTTP/2: added support for trailers in HTTP responses In-Reply-To: <8d74ff6c2015180f5c1f.1496460826@piotrsikora.sfo.corp.google.com> References: <8d74ff6c2015180f5c1f.1496460826@piotrsikora.sfo.corp.google.com> Message-ID: <8319246.PdA5WZVuiQ@vbart-workstation> On Friday 02 June 2017 20:33:46 Piotr Sikora via nginx-devel wrote: > # HG changeset patch > # User Piotr Sikora > # Date 1493191954 25200 > # Wed Apr 26 00:32:34 2017 -0700 > # Node ID 8d74ff6c2015180f5c1f399f492214d7d0a52b3f > # Parent 41c09a2fd90410e25ad8515793bd48028001c954 > HTTP/2: added support for trailers in HTTP responses. > > Signed-off-by: Piotr Sikora > > diff -r 41c09a2fd904 -r 8d74ff6c2015 src/http/v2/ngx_http_v2_filter_module.c > --- a/src/http/v2/ngx_http_v2_filter_module.c > +++ b/src/http/v2/ngx_http_v2_filter_module.c > @@ -50,13 +50,17 @@ > #define NGX_HTTP_V2_SERVER_INDEX 54 > #define NGX_HTTP_V2_VARY_INDEX 59 > > +#define NGX_HTTP_V2_FRAME_ERROR (ngx_http_v2_out_frame_t *) -1 > + > > static u_char *ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len, > u_char *tmp, ngx_uint_t lower); > static u_char *ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix, > ngx_uint_t value); > static ngx_http_v2_out_frame_t *ngx_http_v2_create_headers_frame( > - ngx_http_request_t *r, u_char *pos, u_char *end); > + ngx_http_request_t *r, u_char *pos, u_char *end, ngx_uint_t fin); > +static ngx_http_v2_out_frame_t *ngx_http_v2_create_trailers_frame( > + ngx_http_request_t *r); > > static ngx_chain_t *ngx_http_v2_send_chain(ngx_connection_t *fc, > ngx_chain_t *in, off_t limit); > @@ -612,7 +616,7 @@ ngx_http_v2_header_filter(ngx_http_reque > header[i].value.len, tmp); > } > > - frame = ngx_http_v2_create_headers_frame(r, start, pos); > + frame = ngx_http_v2_create_headers_frame(r, start, pos, r->header_only); > if (frame == NULL) { > return NGX_ERROR; > } > @@ -636,6 +640,126 @@ ngx_http_v2_header_filter(ngx_http_reque > } > > > +static ngx_http_v2_out_frame_t * > +ngx_http_v2_create_trailers_frame(ngx_http_request_t *r) > +{ [..] > + > + frame = ngx_http_v2_create_headers_frame(r, start, pos, 1); > + if (frame == NULL) { > + return NGX_HTTP_V2_FRAME_ERROR; > + } > + > + return frame; > +} It's better to keep return values consistent with ngx_http_v2_create_headers_frame() and introduce NGX_HTTP_V2_NO_TRAILERS instead of NGX_HTTP_V2_FRAME_ERROR. [..] > @@ -934,17 +1060,36 @@ ngx_http_v2_send_chain(ngx_connection_t > size -= rest; > } > > - frame = ngx_http_v2_filter_get_data_frame(stream, frame_size, out, cl); > - if (frame == NULL) { > - return NGX_CHAIN_ERROR; > + if (cl->buf->last_buf) { > + trailers = ngx_http_v2_create_trailers_frame(r); > + if (trailers == NGX_HTTP_V2_FRAME_ERROR) { > + return NGX_CHAIN_ERROR; > + } > + > + if (trailers) { > + cl->buf->last_buf = 0; > + } > } > > - ngx_http_v2_queue_frame(h2c, frame); > + if (frame_size || cl->buf->last_buf) { > + frame = ngx_http_v2_filter_get_data_frame(stream, frame_size, out, > + cl); > + if (frame == NULL) { > + return NGX_CHAIN_ERROR; > + } > > - h2c->send_window -= frame_size; > + ngx_http_v2_queue_frame(h2c, frame); > > - stream->send_window -= frame_size; > - stream->queued++; > + h2c->send_window -= frame_size; > + > + stream->send_window -= frame_size; > + stream->queued++; > + } > + > + if (trailers) { > + ngx_http_v2_queue_frame(h2c, trailers); > + stream->queued++; > + } > > if (in == NULL) { > break; There's no reason to check "trailers" on each iteration. I think you can put it inside the "if (in == NULL)" condition. Please consider the changes below. wbr, Valentin V. Bartenev diff -r 0e2f2f8b5c9b src/http/v2/ngx_http_v2_filter_module.c --- a/src/http/v2/ngx_http_v2_filter_module.c Wed Apr 26 00:32:34 2017 -0700 +++ b/src/http/v2/ngx_http_v2_filter_module.c Wed Jun 07 22:10:04 2017 +0300 @@ -50,7 +50,7 @@ #define NGX_HTTP_V2_SERVER_INDEX 54 #define NGX_HTTP_V2_VARY_INDEX 59 -#define NGX_HTTP_V2_FRAME_ERROR (ngx_http_v2_out_frame_t *) -1 +#define NGX_HTTP_V2_NO_TRAILERS (ngx_http_v2_out_frame_t *) -1 static u_char *ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len, @@ -643,12 +643,11 @@ ngx_http_v2_header_filter(ngx_http_reque static ngx_http_v2_out_frame_t * ngx_http_v2_create_trailers_frame(ngx_http_request_t *r) { - u_char *pos, *start, *tmp; - size_t len, tmp_len; - ngx_uint_t i; - ngx_list_part_t *part; - ngx_table_elt_t *header; - ngx_http_v2_out_frame_t *frame; + u_char *pos, *start, *tmp; + size_t len, tmp_len; + ngx_uint_t i; + ngx_list_part_t *part; + ngx_table_elt_t *header; len = 0; tmp_len = 0; @@ -677,7 +676,7 @@ ngx_http_v2_create_trailers_frame(ngx_ht "too long response trailer name: \"%V\"", &header[i].key); - return NGX_HTTP_V2_FRAME_ERROR; + return NULL; } if (header[i].value.len > NGX_HTTP_V2_MAX_FIELD) { @@ -685,7 +684,7 @@ ngx_http_v2_create_trailers_frame(ngx_ht "too long response trailer value: \"%V: %V\"", &header[i].key, &header[i].value); - return NGX_HTTP_V2_FRAME_ERROR; + return NULL; } len += 1 + NGX_HTTP_V2_INT_OCTETS + header[i].key.len @@ -701,14 +700,14 @@ ngx_http_v2_create_trailers_frame(ngx_ht } if (len == 0) { - return NULL; + return NGX_HTTP_V2_NO_TRAILERS; } tmp = ngx_palloc(r->pool, tmp_len); pos = ngx_pnalloc(r->pool, len); if (pos == NULL || tmp == NULL) { - return NGX_HTTP_V2_FRAME_ERROR; + return NULL; } start = pos; @@ -751,12 +750,7 @@ ngx_http_v2_create_trailers_frame(ngx_ht header[i].value.len, tmp); } - frame = ngx_http_v2_create_headers_frame(r, start, pos, 1); - if (frame == NULL) { - return NGX_HTTP_V2_FRAME_ERROR; - } - - return frame; + return ngx_http_v2_create_headers_frame(r, start, pos, 1); } @@ -996,7 +990,7 @@ ngx_http_v2_send_chain(ngx_connection_t frame_size = (h2lcf->chunk_size < h2c->frame_size) ? h2lcf->chunk_size : h2c->frame_size; - trailers = NULL; + trailers = NGX_HTTP_V2_NO_TRAILERS; #if (NGX_SUPPRESS_WARN) cl = NULL; @@ -1062,18 +1056,18 @@ ngx_http_v2_send_chain(ngx_connection_t if (cl->buf->last_buf) { trailers = ngx_http_v2_create_trailers_frame(r); - if (trailers == NGX_HTTP_V2_FRAME_ERROR) { + if (trailers == NULL) { return NGX_CHAIN_ERROR; } - if (trailers) { + if (trailers != NGX_HTTP_V2_NO_TRAILERS) { cl->buf->last_buf = 0; } } if (frame_size || cl->buf->last_buf) { - frame = ngx_http_v2_filter_get_data_frame(stream, frame_size, out, - cl); + frame = ngx_http_v2_filter_get_data_frame(stream, frame_size, + out, cl); if (frame == NULL) { return NGX_CHAIN_ERROR; } @@ -1086,12 +1080,13 @@ ngx_http_v2_send_chain(ngx_connection_t stream->queued++; } - if (trailers) { - ngx_http_v2_queue_frame(h2c, trailers); - stream->queued++; - } + if (in == NULL) { - if (in == NULL) { + if (trailers != NGX_HTTP_V2_NO_TRAILERS) { + ngx_http_v2_queue_frame(h2c, trailers); + stream->queued++; + } + break; } From hongzhidao at gmail.com Thu Jun 8 04:07:29 2017 From: hongzhidao at gmail.com (=?UTF-8?B?5rSq5b+X6YGT?=) Date: Thu, 8 Jun 2017 12:07:29 +0800 Subject: [nginx] support http2 per server Message-ID: Hi! Now, http2 is enabled globally for 'listen' directive with ip:port. It seems it's possible to enable by server with sni, alpn, npn. Take a look, please. diff -r 5e05118678af src/http/modules/ngx_http_ssl_module.c --- a/src/http/modules/ngx_http_ssl_module.c Mon May 29 23:33:38 2017 +0300 +++ b/src/http/modules/ngx_http_ssl_module.c Wed Jun 07 12:17:34 2017 -0400 @@ -234,6 +234,13 @@ offsetof(ngx_http_ssl_srv_conf_t, stapling_verify), NULL }, + { ngx_string("ssl_h2"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, + ngx_http_ssl_enable, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_ssl_srv_conf_t, h2), + NULL }, + ngx_null_command }; @@ -343,16 +350,17 @@ unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg) { - unsigned int srvlen; - unsigned char *srv; + unsigned int srvlen; + unsigned char *srv; #if (NGX_DEBUG) - unsigned int i; + unsigned int i; #endif #if (NGX_HTTP_V2) - ngx_http_connection_t *hc; + ngx_http_connection_t *hc; + ngx_http_ssl_srv_conf_t *sscf; #endif #if (NGX_HTTP_V2 || NGX_DEBUG) - ngx_connection_t *c; + ngx_connection_t *c; c = ngx_ssl_get_connection(ssl_conn); #endif @@ -367,8 +375,9 @@ #if (NGX_HTTP_V2) hc = c->data; + sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module); - if (hc->addr_conf->http2) { + if (hc->addr_conf->http2 && sscf->h2) { srv = (unsigned char *) NGX_HTTP_V2_ALPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE; srvlen = sizeof(NGX_HTTP_V2_ALPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE) - 1; @@ -411,11 +420,13 @@ #if (NGX_HTTP_V2) { - ngx_http_connection_t *hc; + ngx_http_connection_t *hc; + ngx_http_ssl_srv_conf_t *sscf; hc = c->data; + sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module); - if (hc->addr_conf->http2) { + if (hc->addr_conf->http2 && sscf->h2) { *out = (unsigned char *) NGX_HTTP_V2_NPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE; *outlen = sizeof(NGX_HTTP_V2_NPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE) - 1; @@ -555,6 +566,7 @@ sscf->session_ticket_keys = NGX_CONF_UNSET_PTR; sscf->stapling = NGX_CONF_UNSET; sscf->stapling_verify = NGX_CONF_UNSET; + sscf->h2 = NGX_CONF_UNSET; return sscf; } @@ -620,6 +632,8 @@ ngx_conf_merge_str_value(conf->stapling_responder, prev->stapling_responder, ""); + ngx_conf_merge_value(conf->h2, prev->h2, 0); + conf->ssl.log = cf->log; if (conf->enable) { diff -r 5e05118678af src/http/modules/ngx_http_ssl_module.h --- a/src/http/modules/ngx_http_ssl_module.h Mon May 29 23:33:38 2017 +0300 +++ b/src/http/modules/ngx_http_ssl_module.h Wed Jun 07 12:17:34 2017 -0400 @@ -57,6 +57,8 @@ u_char *file; ngx_uint_t line; + + ngx_flag_t h2; } ngx_http_ssl_srv_conf_t; Thanks. B.R. -------------- next part -------------- An HTML attachment was scrubbed... URL: From hongzhidao at gmail.com Thu Jun 8 06:59:19 2017 From: hongzhidao at gmail.com (=?UTF-8?B?5rSq5b+X6YGT?=) Date: Thu, 8 Jun 2017 14:59:19 +0800 Subject: [nginx] support http2 per server In-Reply-To: References: Message-ID: Sorry for the typo. diff -r 5e05118678af src/http/modules/ngx_http_ssl_module.c --- a/src/http/modules/ngx_http_ssl_module.c Mon May 29 23:33:38 2017 +0300 +++ b/src/http/modules/ngx_http_ssl_module.c Wed Jun 07 12:17:34 2017 -0400 @@ -234,6 +234,13 @@ offsetof(ngx_http_ssl_srv_conf_t, stapling_verify), NULL }, + { ngx_string("ssl_h2"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_ssl_srv_conf_t, h2), + NULL }, + ngx_null_command }; @@ -343,16 +350,17 @@ unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg) { - unsigned int srvlen; - unsigned char *srv; + unsigned int srvlen; + unsigned char *srv; #if (NGX_DEBUG) - unsigned int i; + unsigned int i; #endif #if (NGX_HTTP_V2) - ngx_http_connection_t *hc; + ngx_http_connection_t *hc; + ngx_http_ssl_srv_conf_t *sscf; #endif #if (NGX_HTTP_V2 || NGX_DEBUG) - ngx_connection_t *c; + ngx_connection_t *c; c = ngx_ssl_get_connection(ssl_conn); #endif @@ -367,8 +375,9 @@ #if (NGX_HTTP_V2) hc = c->data; + sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module); - if (hc->addr_conf->http2) { + if (hc->addr_conf->http2 && sscf->h2) { srv = (unsigned char *) NGX_HTTP_V2_ALPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE; srvlen = sizeof(NGX_HTTP_V2_ALPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE) - 1; @@ -411,11 +420,13 @@ #if (NGX_HTTP_V2) { - ngx_http_connection_t *hc; + ngx_http_connection_t *hc; + ngx_http_ssl_srv_conf_t *sscf; hc = c->data; + sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module); - if (hc->addr_conf->http2) { + if (hc->addr_conf->http2 && sscf->h2) { *out = (unsigned char *) NGX_HTTP_V2_NPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE; *outlen = sizeof(NGX_HTTP_V2_NPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE) - 1; @@ -555,6 +566,7 @@ sscf->session_ticket_keys = NGX_CONF_UNSET_PTR; sscf->stapling = NGX_CONF_UNSET; sscf->stapling_verify = NGX_CONF_UNSET; + sscf->h2 = NGX_CONF_UNSET; return sscf; } @@ -620,6 +632,8 @@ ngx_conf_merge_str_value(conf->stapling_responder, prev->stapling_responder, ""); + ngx_conf_merge_value(conf->h2, prev->h2, 0); + conf->ssl.log = cf->log; if (conf->enable) { diff -r 5e05118678af src/http/modules/ngx_http_ssl_module.h --- a/src/http/modules/ngx_http_ssl_module.h Mon May 29 23:33:38 2017 +0300 +++ b/src/http/modules/ngx_http_ssl_module.h Wed Jun 07 12:17:34 2017 -0400 @@ -57,6 +57,8 @@ u_char *file; ngx_uint_t line; + + ngx_flag_t h2; } ngx_http_ssl_srv_conf_t; On Thu, Jun 8, 2017 at 12:07 PM, ??? wrote: > Hi! > Now, http2 is enabled globally for 'listen' directive with ip:port. > It seems it's possible to enable by server with sni, alpn, npn. > Take a look, please. > > diff -r 5e05118678af src/http/modules/ngx_http_ssl_module.c > --- a/src/http/modules/ngx_http_ssl_module.c Mon May 29 23:33:38 2017 > +0300 > +++ b/src/http/modules/ngx_http_ssl_module.c Wed Jun 07 12:17:34 2017 > -0400 > @@ -234,6 +234,13 @@ > offsetof(ngx_http_ssl_srv_conf_t, stapling_verify), > NULL }, > > + { ngx_string("ssl_h2"), > + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, > + ngx_http_ssl_enable, > + NGX_HTTP_SRV_CONF_OFFSET, > + offsetof(ngx_http_ssl_srv_conf_t, h2), > + NULL }, > + > ngx_null_command > }; > > @@ -343,16 +350,17 @@ > unsigned char *outlen, const unsigned char *in, unsigned int inlen, > void *arg) > { > - unsigned int srvlen; > - unsigned char *srv; > + unsigned int srvlen; > + unsigned char *srv; > #if (NGX_DEBUG) > - unsigned int i; > + unsigned int i; > #endif > #if (NGX_HTTP_V2) > - ngx_http_connection_t *hc; > + ngx_http_connection_t *hc; > + ngx_http_ssl_srv_conf_t *sscf; > #endif > #if (NGX_HTTP_V2 || NGX_DEBUG) > - ngx_connection_t *c; > + ngx_connection_t *c; > > c = ngx_ssl_get_connection(ssl_conn); > #endif > @@ -367,8 +375,9 @@ > > #if (NGX_HTTP_V2) > hc = c->data; > + sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, > ngx_http_ssl_module); > > - if (hc->addr_conf->http2) { > + if (hc->addr_conf->http2 && sscf->h2) { > srv = > (unsigned char *) NGX_HTTP_V2_ALPN_ADVERTISE > NGX_HTTP_NPN_ADVERTISE; > srvlen = sizeof(NGX_HTTP_V2_ALPN_ADVERTISE > NGX_HTTP_NPN_ADVERTISE) - 1; > @@ -411,11 +420,13 @@ > > #if (NGX_HTTP_V2) > { > - ngx_http_connection_t *hc; > + ngx_http_connection_t *hc; > + ngx_http_ssl_srv_conf_t *sscf; > > hc = c->data; > + sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, > ngx_http_ssl_module); > > - if (hc->addr_conf->http2) { > + if (hc->addr_conf->http2 && sscf->h2) { > *out = > (unsigned char *) NGX_HTTP_V2_NPN_ADVERTISE > NGX_HTTP_NPN_ADVERTISE; > *outlen = sizeof(NGX_HTTP_V2_NPN_ADVERTISE > NGX_HTTP_NPN_ADVERTISE) - 1; > @@ -555,6 +566,7 @@ > sscf->session_ticket_keys = NGX_CONF_UNSET_PTR; > sscf->stapling = NGX_CONF_UNSET; > sscf->stapling_verify = NGX_CONF_UNSET; > + sscf->h2 = NGX_CONF_UNSET; > > return sscf; > } > @@ -620,6 +632,8 @@ > ngx_conf_merge_str_value(conf->stapling_responder, > prev->stapling_responder, ""); > > + ngx_conf_merge_value(conf->h2, prev->h2, 0); > + > conf->ssl.log = cf->log; > > if (conf->enable) { > diff -r 5e05118678af src/http/modules/ngx_http_ssl_module.h > --- a/src/http/modules/ngx_http_ssl_module.h Mon May 29 23:33:38 2017 > +0300 > +++ b/src/http/modules/ngx_http_ssl_module.h Wed Jun 07 12:17:34 2017 > -0400 > @@ -57,6 +57,8 @@ > > u_char *file; > ngx_uint_t line; > + > + ngx_flag_t h2; > } ngx_http_ssl_srv_conf_t; > > Thanks. > B.R. > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From xeioex at nginx.com Thu Jun 8 11:19:07 2017 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Thu, 08 Jun 2017 11:19:07 +0000 Subject: [njs] Object.prototype.hasOwnProperty() method. Message-ID: details: http://hg.nginx.org/njs/rev/692ad3557d58 branches: changeset: 357:692ad3557d58 user: Dmitry Volyntsev date: Thu Jun 08 14:18:37 2017 +0300 description: Object.prototype.hasOwnProperty() method. diffstat: njs/njs_object.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++ njs/njs_vm.h | 13 ++++++++++++ njs/test/njs_unit_test.c | 42 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 105 insertions(+), 0 deletions(-) diffs (142 lines): diff -r a0bc58cc65d5 -r 692ad3557d58 njs/njs_object.c --- a/njs/njs_object.c Wed Jun 07 17:57:40 2017 +0300 +++ b/njs/njs_object.c Thu Jun 08 14:18:37 2017 +0300 @@ -882,6 +882,49 @@ found: } +static njs_ret_t +njs_object_prototype_has_own_property(njs_vm_t *vm, njs_value_t *args, + nxt_uint_t nargs, njs_index_t unused) +{ + uint32_t index; + nxt_int_t ret; + njs_array_t *array; + const njs_value_t *retval; + nxt_lvlhsh_query_t lhq; + + retval = &njs_string_false; + + if (njs_is_object(&args[0])) { + + if (njs_is_array(&args[0])) { + array = args[0].data.u.array; + index = njs_string_to_index(&args[1]); + + if (index < array->length && njs_is_valid(&array->start[index])) { + retval = &njs_string_true; + goto done; + } + } + + njs_string_get(&args[1], &lhq.key); + lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length); + lhq.proto = &njs_object_hash_proto; + + ret = nxt_lvlhsh_find(&args[0].data.u.object->hash, &lhq); + + if (ret == NXT_OK) { + retval = &njs_string_true; + } + } + +done: + + vm->retval = *retval; + + return NXT_OK; +} + + static const njs_object_prop_t njs_object_prototype_properties[] = { { @@ -907,6 +950,13 @@ static const njs_object_prop_t njs_obje .name = njs_string("toString"), .value = njs_native_function(njs_object_prototype_to_string, 0, 0), }, + + { + .type = NJS_METHOD, + .name = njs_string("hasOwnProperty"), + .value = njs_native_function(njs_object_prototype_has_own_property, 0, + NJS_OBJECT_ARG, NJS_STRING_ARG), + }, }; diff -r a0bc58cc65d5 -r 692ad3557d58 njs/njs_vm.h --- a/njs/njs_vm.h Wed Jun 07 17:57:40 2017 +0300 +++ b/njs/njs_vm.h Thu Jun 08 14:18:37 2017 +0300 @@ -408,6 +408,19 @@ typedef njs_ret_t (*njs_vmcode_operation #define njs_string_truth(value, size) +#define njs_string_get(value, str) \ + do { \ + if ((value)->short_string.size != NJS_STRING_LONG) { \ + (str)->length = (value)->short_string.size; \ + (str)->start = (value)->short_string.start; \ + \ + } else { \ + (str)->length = (value)->data.string_size; \ + (str)->start = (value)->data.u.string->start; \ + } \ + } while (0) + + #define njs_string_short_start(value) \ (value)->short_string.start diff -r a0bc58cc65d5 -r 692ad3557d58 njs/test/njs_unit_test.c --- a/njs/test/njs_unit_test.c Wed Jun 07 17:57:40 2017 +0300 +++ b/njs/test/njs_unit_test.c Thu Jun 08 14:18:37 2017 +0300 @@ -5932,6 +5932,48 @@ static njs_unit_test_t njs_test[] = { nxt_string("var o = {}; Object.defineProperty(o)"), nxt_string("TypeError") }, + { nxt_string("var o = {a:1}; o.hasOwnProperty('a')"), + nxt_string("true") }, + + { nxt_string("var o = Object.create({a:2}); o.hasOwnProperty('a')"), + nxt_string("false") }, + + { nxt_string("var o = {a:1}; o.hasOwnProperty('b')"), + nxt_string("false") }, + + { nxt_string("var a = []; a.hasOwnProperty('0')"), + nxt_string("false") }, + + { nxt_string("var a = [,,]; a.hasOwnProperty('0')"), + nxt_string("false") }, + + { nxt_string("var a = [3,,]; a.hasOwnProperty('0')"), + nxt_string("true") }, + + { nxt_string("var a = [,4]; a.hasOwnProperty('1')"), + nxt_string("true") }, + + { nxt_string("var a = [3,4]; a.hasOwnProperty('2')"), + nxt_string("false") }, + + { nxt_string("var a = [3,4]; a.one = 1; a.hasOwnProperty('one')"), + nxt_string("true") }, + + { nxt_string("var o = {a:1}; o.hasOwnProperty(o)"), + nxt_string("false") }, + + { nxt_string("var o = {a:1}; o.hasOwnProperty(1)"), + nxt_string("false") }, + + { nxt_string("var o = {a:1}; o.hasOwnProperty()"), + nxt_string("false") }, + + { nxt_string("1..hasOwnProperty('b')"), + nxt_string("false") }, + + { nxt_string("'s'.hasOwnProperty('b')"), + nxt_string("false") }, + { nxt_string("var d = new Date(''); d +' '+ d.getTime()"), nxt_string("Invalid Date NaN") }, From vbart at nginx.com Thu Jun 8 14:17:20 2017 From: vbart at nginx.com (Valentin V. Bartenev) Date: Thu, 08 Jun 2017 17:17:20 +0300 Subject: [nginx] support http2 per server In-Reply-To: References: Message-ID: <2402328.DdJ9xUFFzf@vbart-workstation> On Thursday 08 June 2017 12:07:29 ??? wrote: > Hi! > Now, http2 is enabled globally for 'listen' directive with ip:port. > It seems it's possible to enable by server with sni, alpn, npn. > Take a look, please. > [..] How will "sni, alpn, npn" prevent browser from asking other virtual servers using already opened HTTP/2 connection? wbr, Valentin V. Bartenev From vbart at nginx.com Thu Jun 8 15:17:38 2017 From: vbart at nginx.com (Valentin V. Bartenev) Date: Thu, 08 Jun 2017 18:17:38 +0300 Subject: [PATCH] HTTP/2: reject HTTP/2 requests with "Connection" header In-Reply-To: References: Message-ID: <42772973.0zRm91nVQn@vbart-workstation> On Sunday 26 March 2017 01:41:18 Piotr Sikora via nginx-devel wrote: > # HG changeset patch > # User Piotr Sikora > # Date 1490516709 25200 > # Sun Mar 26 01:25:09 2017 -0700 > # Node ID b8daccea5fde213d4b7a10fa9f57070ab3b6a1ec > # Parent 22be63bf21edaa1b8ea916c7d8cd4e5fe4892061 > HTTP/2: reject HTTP/2 requests with "Connection" header. > > While there, populate r->headers_in.connection. > > Signed-off-by: Piotr Sikora > > diff -r 22be63bf21ed -r b8daccea5fde src/http/ngx_http_request.c > --- a/src/http/ngx_http_request.c > +++ b/src/http/ngx_http_request.c > @@ -1659,6 +1659,22 @@ static ngx_int_t > ngx_http_process_connection(ngx_http_request_t *r, ngx_table_elt_t *h, > ngx_uint_t offset) > { > + if (r->headers_in.connection == NULL) { > + r->headers_in.connection = h; > + } > + > +#if (NGX_HTTP_V2) > + > + if (r->http_version >= NGX_HTTP_VERSION_20) { > + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, > + "client sent HTTP/2 request with \"Connection\" header"); > + > + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); > + return NGX_ERROR; > + } > + > +#endif > + > if (ngx_strcasestrn(h->value.data, "close", 5 - 1)) { > r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE; > Since HTTP/2 is a separate protocol and not just GET / HTTP/2.0, so the r->stream pointer should be tested instead (like in many other places). wbr, Valentin V. Bartenev From hongzhidao at gmail.com Thu Jun 8 15:19:23 2017 From: hongzhidao at gmail.com (=?UTF-8?B?5rSq5b+X6YGT?=) Date: Thu, 8 Jun 2017 23:19:23 +0800 Subject: [nginx] support http2 per server In-Reply-To: <2402328.DdJ9xUFFzf@vbart-workstation> References: <2402328.DdJ9xUFFzf@vbart-workstation> Message-ID: It sounds right. According to the same situation, how does http2 protocol force other virtual servers to process certificate (ssl handshake). Example: server { listen 443 http2; a.com; ssl_certi....; } server { listen 443 http2; b.com; ssl_certi....; } We assume sni is 'a.com', then the connection contains different requests with different domains. That b.com will escape from ssl handshake. In fact, we need it to do the ssl handshake. Thanks. On Thu, Jun 8, 2017 at 10:17 PM, Valentin V. Bartenev wrote: > On Thursday 08 June 2017 12:07:29 ??? wrote: > > Hi! > > Now, http2 is enabled globally for 'listen' directive with ip:port. > > It seems it's possible to enable by server with sni, alpn, npn. > > Take a look, please. > > > [..] > > How will "sni, alpn, npn" prevent browser from asking other virtual > servers using already opened HTTP/2 connection? > > wbr, Valentin V. Bartenev > > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel -------------- next part -------------- An HTML attachment was scrubbed... URL: From vbart at nginx.com Thu Jun 8 15:25:10 2017 From: vbart at nginx.com (Valentin V. Bartenev) Date: Thu, 08 Jun 2017 18:25:10 +0300 Subject: [nginx] support http2 per server In-Reply-To: References: <2402328.DdJ9xUFFzf@vbart-workstation> Message-ID: <2643497.seJtDEk1sC@vbart-workstation> On Thursday 08 June 2017 23:19:23 ??? wrote: > It sounds right. > > According to the same situation, how does http2 protocol force other > virtual servers to process certificate (ssl handshake). > > Example: > > server { > listen 443 http2; > a.com; > ssl_certi....; > } > > server { > listen 443 http2; > b.com; > ssl_certi....; > } > > We assume sni is 'a.com', then the connection contains different requests > with different domains. > That b.com will escape from ssl handshake. In fact, we need it to do the > ssl handshake. > > Thanks. > It is called "Connection Reuse" in HTTP/2: https://tools.ietf.org/html/rfc7540#section-9.1.1 wbr, Valentin V. Bartenev From hongzhidao at gmail.com Thu Jun 8 16:08:06 2017 From: hongzhidao at gmail.com (=?UTF-8?B?5rSq5b+X6YGT?=) Date: Fri, 9 Jun 2017 00:08:06 +0800 Subject: [nginx] support http2 per server In-Reply-To: <2643497.seJtDEk1sC@vbart-workstation> References: <2402328.DdJ9xUFFzf@vbart-workstation> <2643497.seJtDEk1sC@vbart-workstation> Message-ID: " For "https" resources, connection reuse additionally depends on having a certificate that is valid for the host in the URI. The certificate presented by the server MUST satisfy any checks that the client would perform when forming a new TLS connection for the host in the URI. " It seems the brower can prevent the unreasonable behavior. In reallity, It still exist some clients that dosen't perfom well in http2. So it's kind of valuable to enable http2 by server. It's not a good idea the put the patch in nginx, Can you help to check the patch whether contains serious problem? Maybe it's helpful for other guys. Thanks again. On Thu, Jun 8, 2017 at 11:25 PM, Valentin V. Bartenev wrote: > On Thursday 08 June 2017 23:19:23 ??? wrote: > > It sounds right. > > > > According to the same situation, how does http2 protocol force other > > virtual servers to process certificate (ssl handshake). > > > > Example: > > > > server { > > listen 443 http2; > > a.com; > > ssl_certi....; > > } > > > > server { > > listen 443 http2; > > b.com; > > ssl_certi....; > > } > > > > We assume sni is 'a.com', then the connection contains different > requests > > with different domains. > > That b.com will escape from ssl handshake. In fact, we need it to do the > > ssl handshake. > > > > Thanks. > > > > It is called "Connection Reuse" in HTTP/2: > https://tools.ietf.org/html/rfc7540#section-9.1.1 > > wbr, Valentin V. Bartenev > > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel > -------------- next part -------------- An HTML attachment was scrubbed... URL: From mdounin at mdounin.ru Thu Jun 8 16:08:20 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Thu, 8 Jun 2017 19:08:20 +0300 Subject: [PATCH] Proxy: always emit "Host" header first In-Reply-To: References: Message-ID: <20170608160820.GW55433@mdounin.ru> Hello! On Sat, Jun 03, 2017 at 08:03:57PM -0700, Piotr Sikora via nginx-devel wrote: > # HG changeset patch > # User Piotr Sikora > # Date 1489618489 25200 > # Wed Mar 15 15:54:49 2017 -0700 > # Node ID e472b23fdc387943ea90fb2f0ae415d9d104edc7 > # Parent 716852cce9136d977b81a2d1b8b6f9fbca0dce49 > Proxy: always emit "Host" header first. > > Signed-off-by: Piotr Sikora > > diff -r 716852cce913 -r e472b23fdc38 src/http/modules/ngx_http_proxy_module.c > --- a/src/http/modules/ngx_http_proxy_module.c > +++ b/src/http/modules/ngx_http_proxy_module.c > @@ -3412,7 +3412,7 @@ ngx_http_proxy_init_headers(ngx_conf_t * > uintptr_t *code; > ngx_uint_t i; > ngx_array_t headers_names, headers_merged; > - ngx_keyval_t *src, *s, *h; > + ngx_keyval_t *host, *src, *s, *h; > ngx_hash_key_t *hk; > ngx_hash_init_t hash; > ngx_http_script_compile_t sc; > @@ -3444,11 +3444,33 @@ ngx_http_proxy_init_headers(ngx_conf_t * > return NGX_ERROR; > } > > + h = default_headers; > + > + if (h->key.len != sizeof("Host") - 1 > + || ngx_strcasecmp(h->key.data, (u_char *) "Host") != 0) > + { > + return NGX_ERROR; > + } > + > + host = ngx_array_push(&headers_merged); > + if (host == NULL) { > + return NGX_ERROR; > + } > + > + *host = *h++; > + > if (conf->headers_source) { > > src = conf->headers_source->elts; > for (i = 0; i < conf->headers_source->nelts; i++) { > > + if (src[i].key.len == sizeof("Host") - 1 > + && ngx_strcasecmp(src[i].key.data, (u_char *) "Host") == 0) > + { > + *host = src[i]; > + continue; > + } > + > s = ngx_array_push(&headers_merged); > if (s == NULL) { > return NGX_ERROR; > @@ -3458,8 +3480,6 @@ ngx_http_proxy_init_headers(ngx_conf_t * > } > } > > - h = default_headers; > - > while (h->key.len) { > > src = headers_merged.elts; I don't think I like this. Trying to prioritize headers based on some mostly external knowledge in a function which is intended to merge headers user-supplied and default headers doesn't look wise. Not to mention that it depends on the "Host" header being first in the default headers lists. Instead, consider the following variants: - emitting default headers first, than user-supplied ones; - emitting the Host header separately from other headers, via a dedicated code in ngx_http_proxy_create_request(). Note well that I'm not really sure that any of the above variants is in fact better than the current behaviour. The current behaviour is to follow header order specified in the configuration, and it makes it possible to redefine order of standard headers if needed. I suspect this and other patches you've send at the same time are in fact parts of your work to introduce HTTP/2 support to upstream servers. Please consider submitting a patch series with the whole feature instead. Individual patches which doesn't make much sense by its own are hard to review, and are likely to be rejected. -- Maxim Dounin http://nginx.org/ From mdounin at mdounin.ru Thu Jun 8 16:19:50 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Thu, 8 Jun 2017 19:19:50 +0300 Subject: [PATCH] Upstream: ignore read-readiness if request wasn't sent In-Reply-To: References: Message-ID: <20170608161950.GX55433@mdounin.ru> Hello! On Sat, Jun 03, 2017 at 08:04:05PM -0700, Piotr Sikora via nginx-devel wrote: > # HG changeset patch > # User Piotr Sikora > # Date 1491296505 25200 > # Tue Apr 04 02:01:45 2017 -0700 > # Node ID bff5ac3da350d8d9225d4204d8aded90fb670f3f > # Parent 716852cce9136d977b81a2d1b8b6f9fbca0dce49 > Upstream: ignore read-readiness if request wasn't sent. > > Signed-off-by: Piotr Sikora > > diff -r 716852cce913 -r bff5ac3da350 src/http/ngx_http_upstream.c > --- a/src/http/ngx_http_upstream.c > +++ b/src/http/ngx_http_upstream.c > @@ -2179,8 +2179,12 @@ ngx_http_upstream_process_header(ngx_htt > return; > } > > - if (!u->request_sent && ngx_http_upstream_test_connect(c) != NGX_OK) { > - ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR); > + if (!u->request_sent) { > + if (ngx_http_upstream_test_connect(c) != NGX_OK) { > + ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR); > + return; > + } > + > return; > } This change looks wrong, as 1) a response may happen to be received even before we've sent a request - for example, the other side may simply close the connection for its own reasons; 2) in the resulting code we return from a read event handler without calling ngx_handle_read_event(), and this will result in CPU hog when used with level-triggered events. -- Maxim Dounin http://nginx.org/ From mdounin at mdounin.ru Thu Jun 8 16:29:56 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Thu, 8 Jun 2017 19:29:56 +0300 Subject: [PATCH] Output chain: propagate flush and last_buf flags to send_chain() In-Reply-To: <2a48b9b6e67d91594c17.1496545447@piotrsikora.sfo.corp.google.com> References: <2a48b9b6e67d91594c17.1496545447@piotrsikora.sfo.corp.google.com> Message-ID: <20170608162956.GY55433@mdounin.ru> Hello! On Sat, Jun 03, 2017 at 08:04:07PM -0700, Piotr Sikora via nginx-devel wrote: > # HG changeset patch > # User Piotr Sikora > # Date 1491708381 25200 > # Sat Apr 08 20:26:21 2017 -0700 > # Node ID 2a48b9b6e67d91594c1787ebf721daebf5f88c91 > # Parent 716852cce9136d977b81a2d1b8b6f9fbca0dce49 > Output chain: propagate flush and last_buf flags to send_chain(). > > Signed-off-by: Piotr Sikora > > diff -r 716852cce913 -r 2a48b9b6e67d src/core/ngx_output_chain.c > --- a/src/core/ngx_output_chain.c > +++ b/src/core/ngx_output_chain.c > @@ -658,6 +658,7 @@ ngx_chain_writer(void *data, ngx_chain_t > ngx_chain_writer_ctx_t *ctx = data; > > off_t size; > + ngx_uint_t flush; > ngx_chain_t *cl, *ln, *chain; > ngx_connection_t *c; > > @@ -689,9 +690,10 @@ ngx_chain_writer(void *data, ngx_chain_t > > size += ngx_buf_size(in->buf); > > - ngx_log_debug2(NGX_LOG_DEBUG_CORE, c->log, 0, > - "chain writer buf fl:%d s:%uO", > - in->buf->flush, ngx_buf_size(in->buf)); > + ngx_log_debug3(NGX_LOG_DEBUG_CORE, c->log, 0, > + "chain writer buf fl:%d l:%d s:%uO", > + in->buf->flush, in->buf->last_buf, > + ngx_buf_size(in->buf)); > > cl = ngx_alloc_chain_link(ctx->pool); > if (cl == NULL) { > @@ -707,6 +709,8 @@ ngx_chain_writer(void *data, ngx_chain_t > ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0, > "chain writer in: %p", ctx->out); > > + flush = 0; > + > for (cl = ctx->out; cl; cl = cl->next) { > > #if 1 > @@ -732,9 +736,13 @@ ngx_chain_writer(void *data, ngx_chain_t > #endif > > size += ngx_buf_size(cl->buf); > + > + if (cl->buf->flush || cl->buf->last_buf) { > + flush = 1; > + } > } > > - if (size == 0 && !c->buffered) { > + if (size == 0 && !flush && !c->buffered) { > return NGX_OK; > } This is not normally needed, especially in case of flush. If you think it is - please clarify how do you expect to use it. Note well that in HTTP/2-related code the special flag c->need_last_buf is used to indicate that a (fake) connection needs an information about last_buf, thus allowing HTTP/2 c->send_chain() wrapper to add its own framing. If the goal is the same, please consider using the same approach. -- Maxim Dounin http://nginx.org/ From mdounin at mdounin.ru Thu Jun 8 16:32:08 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Thu, 8 Jun 2017 19:32:08 +0300 Subject: [PATCH] Proxy: add "proxy_ssl_alpn" directive In-Reply-To: <7733d946e2651a2486a5.1496545442@piotrsikora.sfo.corp.google.com> References: <7733d946e2651a2486a5.1496545442@piotrsikora.sfo.corp.google.com> Message-ID: <20170608163207.GZ55433@mdounin.ru> Hello! On Sat, Jun 03, 2017 at 08:04:02PM -0700, Piotr Sikora via nginx-devel wrote: > # HG changeset patch > # User Piotr Sikora > # Date 1489621682 25200 > # Wed Mar 15 16:48:02 2017 -0700 > # Node ID 7733d946e2651a2486a53d912703e2dfaea30421 > # Parent 716852cce9136d977b81a2d1b8b6f9fbca0dce49 > Proxy: add "proxy_ssl_alpn" directive. > > ALPN is used here only to indicate which version of the HTTP protocol > is going to be used and we doesn't verify that upstream agreed to it. > > Please note that upstream is allowed to reject SSL connection with a > fatal "no_application_protocol" alert if it doesn't support it. > > Signed-off-by: Piotr Sikora It doesn't look like this patch make sense by its own. If this patch is a part of a larger work, please consider submitting a patch series with the whole feature instead. Individual patches which doesn't make much sense by its own are hard to review, and are likely to be rejected. -- Maxim Dounin http://nginx.org/ From vbart at nginx.com Thu Jun 8 17:09:14 2017 From: vbart at nginx.com (Valentin V. Bartenev) Date: Thu, 08 Jun 2017 20:09:14 +0300 Subject: [nginx] support http2 per server In-Reply-To: References: <2643497.seJtDEk1sC@vbart-workstation> Message-ID: <2417822.uuLYJAc3k0@vbart-workstation> On Friday 09 June 2017 00:08:06 ??? wrote: > " > > For "https" resources, connection reuse additionally depends on > having a certificate that is valid for the host in the URI. The > certificate presented by the server MUST satisfy any checks that the > client would perform when forming a new TLS connection for the host > in the URI. > > " > > > It seems the brower can prevent the unreasonable behavior. > > > In reallity, It still exist some clients that dosen't perfom well in http2. > > So it's kind of valuable to enable http2 by server. > > > It's not a good idea the put the patch in nginx, > > Can you help to check the patch whether contains serious problem? > > > Maybe it's helpful for other guys. > > > Thanks again. > [..] The most serious problem with the patch, that it gives an illusion that HTTP/2 can be enabled per virtual server basis, but in fact it doesn't prevent requests to any server on particular listen socket using already existing HTTP/2 connection. Also please note that with your patch clients are still able to negotiate HTTP/2 even if nginx doesn't announce it. I don't see any other serious problems. wbr, Valentin V. Bartenev From hongzhidao at gmail.com Thu Jun 8 17:17:14 2017 From: hongzhidao at gmail.com (=?UTF-8?B?5rSq5b+X6YGT?=) Date: Fri, 9 Jun 2017 01:17:14 +0800 Subject: [nginx] support http2 per server In-Reply-To: <2417822.uuLYJAc3k0@vbart-workstation> References: <2643497.seJtDEk1sC@vbart-workstation> <2417822.uuLYJAc3k0@vbart-workstation> Message-ID: Thanks. On Fri, Jun 9, 2017 at 1:09 AM, Valentin V. Bartenev wrote: > On Friday 09 June 2017 00:08:06 ??? wrote: > > " > > > > For "https" resources, connection reuse additionally depends on > > having a certificate that is valid for the host in the URI. The > > certificate presented by the server MUST satisfy any checks that the > > client would perform when forming a new TLS connection for the host > > in the URI. > > > > " > > > > > > It seems the brower can prevent the unreasonable behavior. > > > > > > In reallity, It still exist some clients that dosen't perfom well in > http2. > > > > So it's kind of valuable to enable http2 by server. > > > > > > It's not a good idea the put the patch in nginx, > > > > Can you help to check the patch whether contains serious problem? > > > > > > Maybe it's helpful for other guys. > > > > > > Thanks again. > > > [..] > > The most serious problem with the patch, that it gives an illusion > that HTTP/2 can be enabled per virtual server basis, but in fact it > doesn't prevent requests to any server on particular listen socket > using already existing HTTP/2 connection. > > Also please note that with your patch clients are still able to > negotiate HTTP/2 even if nginx doesn't announce it. > > I don't see any other serious problems. > > wbr, Valentin V. Bartenev > > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel > -------------- next part -------------- An HTML attachment was scrubbed... URL: From Neil.Craig at bbc.co.uk Thu Jun 8 17:20:42 2017 From: Neil.Craig at bbc.co.uk (Neil Craig) Date: Thu, 8 Jun 2017 17:20:42 +0000 Subject: [nginx] support http2 per server In-Reply-To: References: <2643497.seJtDEk1sC@vbart-workstation> <2417822.uuLYJAc3k0@vbart-workstation>, Message-ID: <856E1199-1263-491E-9AF9-36FF241E95DB@bbc.co.uk> WRT the below, he H2 RFC includes a new status code to deal with thus, 421: https://tools.ietf.org/html/rfc7540#section-9.1.2 Client support is poor right no so it'd be good if sending 421 was optional perhaps. Cheers Sent from my iPhone On 8 Jun 2017, at 18:17, ??? > wrote: Thanks. On Fri, Jun 9, 2017 at 1:09 AM, Valentin V. Bartenev > wrote: On Friday 09 June 2017 00:08:06 ??? wrote: > " > > For "https" resources, connection reuse additionally depends on > having a certificate that is valid for the host in the URI. The > certificate presented by the server MUST satisfy any checks that the > client would perform when forming a new TLS connection for the host > in the URI. > > " > > > It seems the brower can prevent the unreasonable behavior. > > > In reallity, It still exist some clients that dosen't perfom well in http2. > > So it's kind of valuable to enable http2 by server. > > > It's not a good idea the put the patch in nginx, > > Can you help to check the patch whether contains serious problem? > > > Maybe it's helpful for other guys. > > > Thanks again. > [..] The most serious problem with the patch, that it gives an illusion that HTTP/2 can be enabled per virtual server basis, but in fact it doesn't prevent requests to any server on particular listen socket using already existing HTTP/2 connection. Also please note that with your patch clients are still able to negotiate HTTP/2 even if nginx doesn't announce it. I don't see any other serious problems. wbr, Valentin V. Bartenev _______________________________________________ nginx-devel mailing list nginx-devel at nginx.org http://mailman.nginx.org/mailman/listinfo/nginx-devel _______________________________________________ nginx-devel mailing list nginx-devel at nginx.org http://mailman.nginx.org/mailman/listinfo/nginx-devel -------------- next part -------------- An HTML attachment was scrubbed... URL: From vbart at nginx.com Thu Jun 8 17:45:18 2017 From: vbart at nginx.com (Valentin V. Bartenev) Date: Thu, 08 Jun 2017 20:45:18 +0300 Subject: [PATCH] HTTP/2: reject HTTP/2 requests without ":scheme" pseudo-header In-Reply-To: References: <6bb029b1df11662ba11e.1490517677@piotrsikora.sfo.corp.google.com> <17543215.oua5ZIKEOg@vbart-workstation> Message-ID: <2695745.s2C5zqsLaN@vbart-workstation> On Wednesday 31 May 2017 15:48:34 Piotr Sikora via nginx-devel wrote: > Hey Valentin, > > > As the 1.11 branch is going to be stable soon, it's a good idea to postpone > > any changes that explicitly affect interoperability (at least till 1.13). > > Any thoughts on this now that 1.12 branched? > [..] If there will be no other objections against the change, I'll commit this patch next week. wbr, Valentin V. Bartenev From vbart at nginx.com Thu Jun 8 18:31:19 2017 From: vbart at nginx.com (Valentin V. Bartenev) Date: Thu, 08 Jun 2017 21:31:19 +0300 Subject: [PATCH] HTTP/2: add debug logging of control frames In-Reply-To: References: <06d6418afe6e73604aea.1491275620@piotrsikora.sfo.corp.google.com> Message-ID: <6088549.Je3sRdAShv@vbart-workstation> On Tuesday 30 May 2017 14:21:05 Piotr Sikora via nginx-devel wrote: > Hey Valentin, > > > What do you suggest instead? All 3 params in the same line? > > > > http2 send SETTINGS frame MAX_CONCURRENT_STREAMS:%ui > > INITIAL_WINDOW_SIZE:%uz MAX_FRAME_SIZE:%ud > > > > What about receiving part, then? Do you want to put all 6 params in > > the same line? > > > > http2 recv SETTINGS frame HEADER_TABLE_SIZE:%ui (ignored) > > ENABLE_PUSH:%ui (ignored) MAX_CONCURRENT_STREAMS:%ui (ignored) > > INITIAL_WINDOW_SIZE:%ui MAX_FRAME_SIZE:%ui MAX_HEADER_LIST_SIZE:%ui > > (ignored) > > > > It makes this way less readable, IMHO. > > Ping. > Ok, I've already resigned myself to multiline output, but don't let it look like an another SETTINGS frame. IMHO, something like that will be good enough: http2 send SETTINGS frame http2 SETTINGS param MAX_CONCURRENT_STREAMS: 100 http2 SETTINGS param INITIAL_WINDOW_SIZE: 65536 http2 SETTINGS param MAX_FRAME_SIZE: 16777215 wbr, Valentin V. Bartenev From Nate.Karstens at garmin.com Fri Jun 9 03:40:15 2017 From: Nate.Karstens at garmin.com (Karstens, Nate) Date: Fri, 9 Jun 2017 03:40:15 +0000 Subject: PSK Support In-Reply-To: <20170607181327.GS55433@mdounin.ru> References: <145451D4E6785E4DA4DFA353A58CB7B2015E15B7E3@OLAWPA-EXMB04.ad.garmin.com> <20170605130916.GX55433@mdounin.ru> <145451D4E6785E4DA4DFA353A58CB7B2015E15C57F@OLAWPA-EXMB04.ad.garmin.com> <20170605183944.GB55433@mdounin.ru> <145451D4E6785E4DA4DFA353A58CB7B2015E15CE05@OLAWPA-EXMB04.ad.garmin.com> <20170607181327.GS55433@mdounin.ru> Message-ID: <145451D4E6785E4DA4DFA353A58CB7B2015E15DA0A@OLAWPA-EXMB04.ad.garmin.com> Maxim, OK, we can skip the patch for turning off the certificate warnings (and just use a dummy certificate) and just support a single PSK file. The {HEX} prefix seems OK. I think it would also be good to support an {ASC}. It is unlikely that anyone would have an ASCII-based PSK that starts with {HEX}, but using {ASC} would provide a way to make prevent that case. Also, instead of referring to text-based PSKs as ASCII, maybe they should be UTF8-encoded and referred to as {TXT}? Nate -----Original Message----- From: nginx-devel [mailto:nginx-devel-bounces at nginx.org] On Behalf Of Maxim Dounin Sent: Wednesday, June 07, 2017 1:13 PM To: nginx-devel at nginx.org Subject: Re: PSK Support Hello! On Wed, Jun 07, 2017 at 05:02:52AM +0000, Karstens, Nate wrote: > Maxim, > > The biggest downside that I can see to the dummy certificate approach > is documentation. Using a dummy certificate wasn't immediately obvious > to me, though perhaps my familiarity with OpenSSL's "nocert" option > may have affected that. Which do you think would be easier for the > user to find in the documentation: > 1) a description of the dummy certificate approach under the > "ssl_certificate" directive, 2) a separate directive ("ssl_nocert"), > or 3) an explicit option to the "ssl_certificate" directive (e.g., " > Syntax: ssl_certificate file | off;")? In most cases, normal users won't need PSK at all, or will use it combined with certificates anyway. Describing in the documentation that "if you need to use PSK only, you still have to configure a certificate" doesn't looks like a big problem to me. Note well that there is a side problem with "listen ... ssl" used in server block but no ssl_certificate configured for the server. As of now, it is not reported during configuration parsing and may result in non-obvious behaviour in some cases, see ticket #178 (https://trac.nginx.org/nginx/ticket/178). This is a separate problem though, and it can't be fixed with the changes suggested. On the other hand, properly fixing it will greatly improve user experience in many cases, including PSK. > I'm OK with changing it to read from a password file (formatted in a > manner similar to stunnel) that is searched as needed (an > "ssl_psk_file" directive). Would it be OK to support multiple files > and stipulate that files are searched in the order that they are > included in nginx.conf? I don't think that supporting multiple files is a good idea, a single one should be enough. Possible alternative names: - "ssl_psk_secrets", similar to the one used by stunnel; - "ssl_pre_shared_keys". Not sure if these are better though. > Can we support both ASCII and binary PSKs? RFC 4279 section 5.4 seems > to require both types, and I need binary keys for my application :). > Maybe a parameter to the "ssl_psk_file" > directive could indicate how the PSKs are stored in the file? We can consider providing an alternative form to allow arbitrary keys, e.g., by using hex-encoded keys. This can be done using some explicit prefix, something like this: identity:key identity:0x6b6579 identity:{HEX}6b6579 (The last variant is in line with "{scheme}data" syntax as used in auth_basic_user_file. Not sure if we need it here, just "0x" might be easier.) -- Maxim Dounin http://nginx.org/ _______________________________________________ nginx-devel mailing list nginx-devel at nginx.org http://mailman.nginx.org/mailman/listinfo/nginx-devel ________________________________ CONFIDENTIALITY NOTICE: This email and any attachments are for the sole use of the intended recipient(s) and contain information that may be Garmin confidential and/or Garmin legally privileged. If you have received this email in error, please notify the sender by reply email and delete the message. Any disclosure, copying, distribution or use of this communication (including attachments) by someone other than the intended recipient is prohibited. Thank you. From hongzhidao at gmail.com Fri Jun 9 05:40:23 2017 From: hongzhidao at gmail.com (=?UTF-8?B?5rSq5b+X6YGT?=) Date: Fri, 9 Jun 2017 13:40:23 +0800 Subject: [nginx] support http2 per server In-Reply-To: <2417822.uuLYJAc3k0@vbart-workstation> References: <2643497.seJtDEk1sC@vbart-workstation> <2417822.uuLYJAc3k0@vbart-workstation> Message-ID: Hi, Valentin. " Also please note that with your patch clients are still able to negotiate HTTP/2 even if nginx doesn't announce it. " Two points: 1. The patch forbids the clients explicitly not support HTTP/2 doing v2 ( ngx_http_v2_init). How to follow you mean of "with the patch, clients are still able to negotiate HTTP/2" 2. "even if nginx doesn't announce it" Is it related to nginx? On Fri, Jun 9, 2017 at 1:09 AM, Valentin V. Bartenev wrote: > On Friday 09 June 2017 00:08:06 ??? wrote: > > " > > > > For "https" resources, connection reuse additionally depends on > > having a certificate that is valid for the host in the URI. The > > certificate presented by the server MUST satisfy any checks that the > > client would perform when forming a new TLS connection for the host > > in the URI. > > > > " > > > > > > It seems the brower can prevent the unreasonable behavior. > > > > > > In reallity, It still exist some clients that dosen't perfom well in > http2. > > > > So it's kind of valuable to enable http2 by server. > > > > > > It's not a good idea the put the patch in nginx, > > > > Can you help to check the patch whether contains serious problem? > > > > > > Maybe it's helpful for other guys. > > > > > > Thanks again. > > > [..] > > The most serious problem with the patch, that it gives an illusion > that HTTP/2 can be enabled per virtual server basis, but in fact it > doesn't prevent requests to any server on particular listen socket > using already existing HTTP/2 connection. > > Also please note that with your patch clients are still able to > negotiate HTTP/2 even if nginx doesn't announce it. > > I don't see any other serious problems. > > wbr, Valentin V. Bartenev > > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel > -------------- next part -------------- An HTML attachment was scrubbed... URL: From vbart at nginx.com Fri Jun 9 11:45:28 2017 From: vbart at nginx.com (Valentin V. Bartenev) Date: Fri, 09 Jun 2017 14:45:28 +0300 Subject: [nginx] support http2 per server In-Reply-To: References: <2417822.uuLYJAc3k0@vbart-workstation> Message-ID: <2058364.S2bnstnO8o@vbart-laptop> On Friday 09 June 2017 13:40:23 ??? wrote: > Hi, Valentin. > > " > Also please note that with your patch clients are still able to > negotiate HTTP/2 even if nginx doesn't announce it. > " > > Two points: > 1. The patch forbids the clients explicitly not support HTTP/2 doing v2 ( > ngx_http_v2_init). > How to follow you mean of "with the patch, clients are still able to > negotiate HTTP/2" > 2. "even if nginx doesn't announce it" > Is it related to nginx? > [..] Your patch prevents advertising the protocol using ALPN and NPN, but selecting protocol happens here (this part of code isn't touched): http://hg.nginx.org/nginx/file/tip/src/http/ngx_http_request.c#l808 In case of NPN, client can select protocol even if it hasn't been advertised by the server. wbr, Valentin V. Bartenev From hongzhidao at gmail.com Fri Jun 9 12:26:03 2017 From: hongzhidao at gmail.com (=?UTF-8?B?5rSq5b+X6YGT?=) Date: Fri, 9 Jun 2017 20:26:03 +0800 Subject: [nginx] support http2 per server In-Reply-To: <2058364.S2bnstnO8o@vbart-laptop> References: <2417822.uuLYJAc3k0@vbart-workstation> <2058364.S2bnstnO8o@vbart-laptop> Message-ID: Get it, thanks. On Fri, Jun 9, 2017 at 7:45 PM, Valentin V. Bartenev wrote: > On Friday 09 June 2017 13:40:23 ??? wrote: > > Hi, Valentin. > > > > " > > Also please note that with your patch clients are still able to > > negotiate HTTP/2 even if nginx doesn't announce it. > > " > > > > Two points: > > 1. The patch forbids the clients explicitly not support HTTP/2 doing v2 ( > > ngx_http_v2_init). > > How to follow you mean of "with the patch, clients are still able to > > negotiate HTTP/2" > > 2. "even if nginx doesn't announce it" > > Is it related to nginx? > > > [..] > > Your patch prevents advertising the protocol using ALPN and NPN, but > selecting protocol happens here (this part of code isn't touched): > http://hg.nginx.org/nginx/file/tip/src/http/ngx_http_request.c#l808 > > In case of NPN, client can select protocol even if it hasn't been > advertised by the server. > > wbr, Valentin V. Bartenev > > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel > -------------- next part -------------- An HTML attachment was scrubbed... URL: From hongzhidao at gmail.com Fri Jun 9 12:56:38 2017 From: hongzhidao at gmail.com (=?UTF-8?B?5rSq5b+X6YGT?=) Date: Fri, 9 Jun 2017 20:56:38 +0800 Subject: [nginx] support http2 per server In-Reply-To: References: <2417822.uuLYJAc3k0@vbart-workstation> <2058364.S2bnstnO8o@vbart-laptop> Message-ID: Hi, Valentin. Please confirm again, thanks. diff -r 5e05118678af src/http/modules/ngx_http_ssl_module.c --- a/src/http/modules/ngx_http_ssl_module.c Mon May 29 23:33:38 2017 +0300 +++ b/src/http/modules/ngx_http_ssl_module.c Fri Jun 09 07:15:50 2017 -0400 @@ -234,6 +234,13 @@ offsetof(ngx_http_ssl_srv_conf_t, stapling_verify), NULL }, + { ngx_string("ssl_h2"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, + ngx_http_ssl_enable, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_ssl_srv_conf_t, h2), + NULL }, + ngx_null_command }; @@ -343,16 +350,17 @@ unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg) { - unsigned int srvlen; - unsigned char *srv; + unsigned int srvlen; + unsigned char *srv; #if (NGX_DEBUG) - unsigned int i; + unsigned int i; #endif #if (NGX_HTTP_V2) - ngx_http_connection_t *hc; + ngx_http_connection_t *hc; + ngx_http_ssl_srv_conf_t *sscf; #endif #if (NGX_HTTP_V2 || NGX_DEBUG) - ngx_connection_t *c; + ngx_connection_t *c; c = ngx_ssl_get_connection(ssl_conn); #endif @@ -367,8 +375,9 @@ #if (NGX_HTTP_V2) hc = c->data; + sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module); - if (hc->addr_conf->http2) { + if (hc->addr_conf->http2 && sscf->h2) { srv = (unsigned char *) NGX_HTTP_V2_ALPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE; srvlen = sizeof(NGX_HTTP_V2_ALPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE) - 1; @@ -411,11 +420,13 @@ #if (NGX_HTTP_V2) { - ngx_http_connection_t *hc; + ngx_http_connection_t *hc; + ngx_http_ssl_srv_conf_t *sscf; hc = c->data; + sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module); - if (hc->addr_conf->http2) { + if (hc->addr_conf->http2 && sscf->h2) { *out = (unsigned char *) NGX_HTTP_V2_NPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE; *outlen = sizeof(NGX_HTTP_V2_NPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE) - 1; @@ -555,6 +566,7 @@ sscf->session_ticket_keys = NGX_CONF_UNSET_PTR; sscf->stapling = NGX_CONF_UNSET; sscf->stapling_verify = NGX_CONF_UNSET; + sscf->h2 = NGX_CONF_UNSET; return sscf; } @@ -620,6 +632,8 @@ ngx_conf_merge_str_value(conf->stapling_responder, prev->stapling_responder, ""); + ngx_conf_merge_value(conf->h2, prev->h2, 0); + conf->ssl.log = cf->log; if (conf->enable) { diff -r 5e05118678af src/http/modules/ngx_http_ssl_module.h --- a/src/http/modules/ngx_http_ssl_module.h Mon May 29 23:33:38 2017 +0300 +++ b/src/http/modules/ngx_http_ssl_module.h Fri Jun 09 07:15:50 2017 -0400 @@ -57,6 +57,8 @@ u_char *file; ngx_uint_t line; + + ngx_flag_t h2; } ngx_http_ssl_srv_conf_t; diff -r 5e05118678af src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c Mon May 29 23:33:38 2017 +0300 +++ b/src/http/ngx_http_request.c Fri Jun 09 07:15:50 2017 -0400 @@ -784,9 +784,10 @@ && (defined TLSEXT_TYPE_application_layer_protocol_negotiation \ || defined TLSEXT_TYPE_next_proto_neg)) { - unsigned int len; - const unsigned char *data; - ngx_http_connection_t *hc; + unsigned int len; + const unsigned char *data; + ngx_http_connection_t *hc; + ngx_http_ssl_srv_conf_t *sscf; hc = c->data; @@ -805,9 +806,14 @@ SSL_get0_next_proto_negotiated(c->ssl->connection, &data, &len); #endif - if (len == 2 && data[0] == 'h' && data[1] == '2') { - ngx_http_v2_init(c->read); - return; + sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module); + + if (sscf->h2) { + + if (len == 2 && data[0] == 'h' && data[1] == '2') { + ngx_http_v2_init(c->read); + return; + } } } } -------------- next part -------------- An HTML attachment was scrubbed... URL: From xeioex at nginx.com Fri Jun 9 14:23:02 2017 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Fri, 09 Jun 2017 14:23:02 +0000 Subject: [njs] Use njs_string_get() where appropriate. Message-ID: details: http://hg.nginx.org/njs/rev/065bf91227a1 branches: changeset: 358:065bf91227a1 user: Dmitry Volyntsev date: Fri Jun 09 17:22:27 2017 +0300 description: Use njs_string_get() where appropriate. diffstat: njs/njs_date.c | 8 ++++---- njs/njs_number.c | 6 +++--- njs/njs_object.c | 18 +++--------------- njs/njs_regexp.c | 14 ++++++++------ njs/njs_string.c | 44 ++++++++++++++++++++++---------------------- 5 files changed, 40 insertions(+), 50 deletions(-) diffs (288 lines): diff -r 692ad3557d58 -r 065bf91227a1 njs/njs_date.c --- a/njs/njs_date.c Thu Jun 08 14:18:37 2017 +0300 +++ b/njs/njs_date.c Fri Jun 09 17:22:27 2017 +0300 @@ -306,15 +306,15 @@ njs_date_string_parse(njs_value_t *date) { int ext, ms, ms_length, skipped; double time; + nxt_str_t string; struct tm tm; nxt_bool_t sign, week, utc; const u_char *p, *next, *end; - njs_string_prop_t string; - - (void) njs_string_prop(&string, date); + + njs_string_get(date, &string); p = string.start; - end = p + string.size; + end = p + string.length; if (nxt_slow_path(p >= end)) { return NAN; diff -r 692ad3557d58 -r 065bf91227a1 njs/njs_number.c --- a/njs/njs_number.c Thu Jun 08 14:18:37 2017 +0300 +++ b/njs/njs_number.c Fri Jun 09 17:22:27 2017 +0300 @@ -741,15 +741,15 @@ njs_number_parse_int(njs_vm_t *vm, njs_v u_char *p, *end; int64_t n; uint8_t radix; + nxt_str_t string; nxt_bool_t minus, test_prefix; - njs_string_prop_t string; num = NAN; if (nargs > 1) { - (void) njs_string_prop(&string, &args[1]); + njs_string_get(&args[1], &string); - end = string.start + string.size; + end = string.start + string.length; for (p = string.start; p < end; p++) { if (*p != ' ') { diff -r 692ad3557d58 -r 065bf91227a1 njs/njs_object.c --- a/njs/njs_object.c Thu Jun 08 14:18:37 2017 +0300 +++ b/njs/njs_object.c Fri Jun 09 17:22:27 2017 +0300 @@ -417,8 +417,6 @@ njs_object_define_property(njs_vm_t *vm, njs_index_t unused) { nxt_int_t ret; - nxt_str_t key; - njs_value_t *name; njs_object_t *object, *descriptor; njs_object_prop_t *prop, *pr; nxt_lvlhsh_query_t lhq, pq; @@ -429,26 +427,16 @@ njs_object_define_property(njs_vm_t *vm, } object = args[1].data.u.object; - name = &args[2]; descriptor = args[3].data.u.object; - if (name->short_string.size != NJS_STRING_LONG) { - key.start = name->short_string.start; - key.length = name->short_string.length; - - } else { - key.start = name->data.u.string->start; - key.length = name->data.string_size; - } - - lhq.key = key; - lhq.key_hash = nxt_djb_hash(key.start, key.length); + njs_string_get(&args[2], &lhq.key); + lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length); lhq.proto = &njs_object_hash_proto; ret = nxt_lvlhsh_find(&object->hash, &lhq); if (ret != NXT_OK) { - prop = njs_object_prop_alloc(vm, name); + prop = njs_object_prop_alloc(vm, &args[2]); if (nxt_slow_path(prop == NULL)) { return NXT_ERROR; diff -r 692ad3557d58 -r 065bf91227a1 njs/njs_regexp.c --- a/njs/njs_regexp.c Thu Jun 08 14:18:37 2017 +0300 +++ b/njs/njs_regexp.c Fri Jun 09 17:22:27 2017 +0300 @@ -87,7 +87,7 @@ njs_ret_t njs_regexp_constructor(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, njs_index_t unused) { - njs_string_prop_t string; + nxt_str_t string; njs_regexp_flags_t flags; flags = 0; @@ -96,13 +96,14 @@ njs_regexp_constructor(njs_vm_t *vm, njs case 1: string.start = NULL; - string.size = 0; + string.length = 0; break; default: - (void) njs_string_prop(&string, &args[2]); + njs_string_get(&args[2], &string); - flags = njs_regexp_flags(&string.start, string.start + string.size, 1); + flags = njs_regexp_flags(&string.start, string.start + string.length, + 1); if (nxt_slow_path(flags < 0)) { return NXT_ERROR; } @@ -110,11 +111,12 @@ njs_regexp_constructor(njs_vm_t *vm, njs /* Fall through. */ case 2: - (void) njs_string_prop(&string, &args[1]); + njs_string_get(&args[1], &string); break; } - return njs_regexp_create(vm, &vm->retval, string.start, string.size, flags); + return njs_regexp_create(vm, &vm->retval, string.start, string.length, + flags); } diff -r 692ad3557d58 -r 065bf91227a1 njs/njs_string.c --- a/njs/njs_string.c Thu Jun 08 14:18:37 2017 +0300 +++ b/njs/njs_string.c Fri Jun 09 17:22:27 2017 +0300 @@ -1940,15 +1940,15 @@ static njs_ret_t njs_string_prototype_match(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, njs_index_t unused) { + nxt_str_t string; njs_ret_t ret; njs_value_t arguments[2]; - njs_string_prop_t string; njs_regexp_pattern_t *pattern; arguments[1] = args[0]; string.start = NULL; - string.size = 0; + string.length = 0; if (nargs > 1) { @@ -1970,13 +1970,13 @@ njs_string_prototype_match(njs_vm_t *vm, if (njs_is_string(&args[1])) { /* string1.match(string2) is the same as /string2/.exec(string1). */ - (void) njs_string_prop(&string, &args[1]); + njs_string_get(&args[1], &string); } /* A void value. */ } - ret = njs_regexp_create(vm, &arguments[0], string.start, string.size, 0); + ret = njs_regexp_create(vm, &arguments[0], string.start, string.length, 0); if (nxt_slow_path(ret != NXT_OK)) { return ret; } @@ -2559,19 +2559,19 @@ njs_string_replace_search(njs_vm_t *vm, u_char *p, *end; size_t size; njs_ret_t ret; - njs_string_prop_t search; - - (void) njs_string_prop(&search, &args[1]); + nxt_str_t search; + + njs_string_get(&args[1], &search); p = r->part[0].start; - end = (p + r->part[0].size) - (search.size - 1); + end = (p + r->part[0].size) - (search.length - 1); do { - if (memcmp(p, search.start, search.size) == 0) { + if (memcmp(p, search.start, search.length) == 0) { if (r->substitutions != NULL) { captures[0] = p - r->part[0].start; - captures[1] = captures[0] + search.size; + captures[1] = captures[0] + search.length; ret = njs_string_replace_substitute(vm, r, captures); if (nxt_slow_path(ret != NXT_OK)) { @@ -2579,9 +2579,9 @@ njs_string_replace_search(njs_vm_t *vm, } } else { - r->part[2].start = p + search.size; + r->part[2].start = p + search.length; size = p - r->part[0].start; - r->part[2].size = r->part[0].size - size - search.size; + r->part[2].size = r->part[0].size - size - search.length; r->part[0].size = size; njs_set_invalid(&r->part[2].value); @@ -3344,17 +3344,17 @@ njs_string_encode(njs_vm_t *vm, njs_valu { u_char byte, *src, *dst; size_t n, size; - njs_string_prop_t string; + nxt_str_t string; static const u_char hex[16] = "0123456789ABCDEF"; nxt_prefetch(escape); - (void) njs_string_prop(&string, value); + njs_string_get(value, &string); src = string.start; n = 0; - for (size = string.size; size != 0; size--) { + for (size = string.length; size != 0; size--) { byte = *src++; if ((escape[byte >> 5] & ((uint32_t) 1 << (byte & 0x1f))) != 0) { @@ -3368,14 +3368,14 @@ njs_string_encode(njs_vm_t *vm, njs_valu return NXT_OK; } - size = string.size + n; + size = string.length + n; dst = njs_string_alloc(vm, &vm->retval, size, size); if (nxt_slow_path(dst == NULL)) { return NXT_ERROR; } - size = string.size; + size = string.length; src = string.start; do { @@ -3477,8 +3477,8 @@ njs_string_decode(njs_vm_t *vm, njs_valu u_char byte, *start, *src, *dst; size_t n; ssize_t size, length; + nxt_str_t string; nxt_bool_t utf8; - njs_string_prop_t string; static const int8_t hex[256] nxt_aligned(32) = @@ -3504,12 +3504,12 @@ njs_string_decode(njs_vm_t *vm, njs_valu nxt_prefetch(&hex['0']); nxt_prefetch(reserve); - (void) njs_string_prop(&string, value); + njs_string_get(value, &string); src = string.start; n = 0; - for (size = string.size; size != 0; size--) { + for (size = string.length; size != 0; size--) { byte = *src++; if (byte == '%') { @@ -3543,7 +3543,7 @@ njs_string_decode(njs_vm_t *vm, njs_valu return NXT_OK; } - n = string.size - n; + n = string.length - n; start = njs_string_alloc(vm, &vm->retval, n, n); if (nxt_slow_path(start == NULL)) { @@ -3552,7 +3552,7 @@ njs_string_decode(njs_vm_t *vm, njs_valu utf8 = 0; dst = start; - size = string.size; + size = string.length; src = string.start; do { From xeioex at nginx.com Fri Jun 9 14:55:39 2017 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Fri, 09 Jun 2017 14:55:39 +0000 Subject: [njs] Object.getPrototypeOf() method. Message-ID: details: http://hg.nginx.org/njs/rev/e49777448f84 branches: changeset: 359:e49777448f84 user: Dmitry Volyntsev date: Fri Jun 09 17:55:08 2017 +0300 description: Object.getPrototypeOf() method. diffstat: njs/njs_object.c | 22 ++++++++++++++++++++++ njs/test/njs_unit_test.c | 18 ++++++++++++++++++ 2 files changed, 40 insertions(+), 0 deletions(-) diffs (67 lines): diff -r 065bf91227a1 -r e49777448f84 njs/njs_object.c --- a/njs/njs_object.c Fri Jun 09 17:22:27 2017 +0300 +++ b/njs/njs_object.c Fri Jun 09 17:55:08 2017 +0300 @@ -503,6 +503,20 @@ njs_object_define_property(njs_vm_t *vm, } +static njs_ret_t +njs_object_get_prototype_of(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + if (nargs > 1 && njs_is_object(&args[1])) { + njs_object_prototype_get_proto(vm, &args[1]); + return NXT_OK; + } + + vm->exception = &njs_exception_type_error; + return NXT_ERROR; +} + + /* * The __proto__ property of booleans, numbers and strings primitives, * of objects created by Boolean(), Number(), and String() constructors, @@ -658,6 +672,14 @@ static const njs_object_prop_t njs_obje NJS_SKIP_ARG, NJS_OBJECT_ARG, NJS_STRING_ARG, NJS_OBJECT_ARG), }, + + /* Object.getPrototypeOf(). */ + { + .type = NJS_METHOD, + .name = njs_string("getPrototypeOf"), + .value = njs_native_function(njs_object_get_prototype_of, 0, + NJS_SKIP_ARG, NJS_OBJECT_ARG), + }, }; diff -r 065bf91227a1 -r e49777448f84 njs/test/njs_unit_test.c --- a/njs/test/njs_unit_test.c Fri Jun 09 17:22:27 2017 +0300 +++ b/njs/test/njs_unit_test.c Fri Jun 09 17:55:08 2017 +0300 @@ -5974,6 +5974,24 @@ static njs_unit_test_t njs_test[] = { nxt_string("'s'.hasOwnProperty('b')"), nxt_string("false") }, + { nxt_string("var p = { a:5 }; var o = Object.create(p);" + "Object.getPrototypeOf(o) === p"), + nxt_string("true") }, + + { nxt_string("var p = { a:5 }; var o = Object.create(p);" + "Object.getPrototypeOf(o) === o.__proto__"), + nxt_string("true") }, + + { nxt_string("var o = Object.create(Object.prototype);" + "Object.getPrototypeOf(o) === Object.prototype"), + nxt_string("true") }, + + { nxt_string("Object.getPrototypeOf(1)"), + nxt_string("TypeError") }, + + { nxt_string("Object.getPrototypeOf('a')"), + nxt_string("TypeError") }, + { nxt_string("var d = new Date(''); d +' '+ d.getTime()"), nxt_string("Invalid Date NaN") }, From xeioex at nginx.com Fri Jun 9 14:55:42 2017 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Fri, 09 Jun 2017 14:55:42 +0000 Subject: [njs] Object.prototype.isPrototypeOf() method. Message-ID: details: http://hg.nginx.org/njs/rev/740823b9f444 branches: changeset: 360:740823b9f444 user: Dmitry Volyntsev date: Fri Jun 09 17:55:21 2017 +0300 description: Object.prototype.isPrototypeOf() method. diffstat: njs/njs_object.c | 37 +++++++++++++++++++++++++++++++++++++ njs/test/njs_unit_test.c | 25 +++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 0 deletions(-) diffs (89 lines): diff -r e49777448f84 -r 740823b9f444 njs/njs_object.c --- a/njs/njs_object.c Fri Jun 09 17:55:08 2017 +0300 +++ b/njs/njs_object.c Fri Jun 09 17:55:21 2017 +0300 @@ -935,6 +935,36 @@ done: } +static njs_ret_t +njs_object_prototype_is_prototype_of(njs_vm_t *vm, njs_value_t *args, + nxt_uint_t nargs, njs_index_t unused) +{ + njs_object_t *object, *proto; + const njs_value_t *retval; + + retval = &njs_string_false; + + if (njs_is_object(&args[0]) && njs_is_object(&args[1])) { + proto = args[0].data.u.object; + object = args[1].data.u.object; + + do { + object = object->__proto__; + + if (object == proto) { + retval = &njs_string_true; + break; + } + + } while (object != NULL); + } + + vm->retval = *retval; + + return NXT_OK; +} + + static const njs_object_prop_t njs_object_prototype_properties[] = { { @@ -967,6 +997,13 @@ static const njs_object_prop_t njs_obje .value = njs_native_function(njs_object_prototype_has_own_property, 0, NJS_OBJECT_ARG, NJS_STRING_ARG), }, + + { + .type = NJS_METHOD, + .name = njs_string("isPrototypeOf"), + .value = njs_native_function(njs_object_prototype_is_prototype_of, 0, + NJS_OBJECT_ARG, NJS_OBJECT_ARG), + }, }; diff -r e49777448f84 -r 740823b9f444 njs/test/njs_unit_test.c --- a/njs/test/njs_unit_test.c Fri Jun 09 17:55:08 2017 +0300 +++ b/njs/test/njs_unit_test.c Fri Jun 09 17:55:21 2017 +0300 @@ -5992,6 +5992,31 @@ static njs_unit_test_t njs_test[] = { nxt_string("Object.getPrototypeOf('a')"), nxt_string("TypeError") }, + { nxt_string("var p = {}; var o = Object.create(p);" + "p.isPrototypeOf(o)"), + nxt_string("true") }, + + { nxt_string("var pp = {}; var p = Object.create(pp);" + "var o = Object.create(p);" + "pp.isPrototypeOf(o)"), + nxt_string("true") }, + + { nxt_string("var p = {}; var o = Object.create(p);" + "o.isPrototypeOf(p)"), + nxt_string("false") }, + + { nxt_string("var p = {}; var o = Object.create(p);" + "o.isPrototypeOf()"), + nxt_string("false") }, + + { nxt_string("var p = {}; var o = Object.create(p);" + "o.isPrototypeOf(1)"), + nxt_string("false") }, + + { nxt_string("var p = {}; var o = Object.create(p);" + "1..isPrototypeOf(p)"), + nxt_string("false") }, + { nxt_string("var d = new Date(''); d +' '+ d.getTime()"), nxt_string("Invalid Date NaN") }, From xeioex at nginx.com Fri Jun 9 17:28:44 2017 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Fri, 09 Jun 2017 17:28:44 +0000 Subject: [njs] Object.defineProperties() method. Message-ID: details: http://hg.nginx.org/njs/rev/499ed5aa4f98 branches: changeset: 361:499ed5aa4f98 user: Dmitry Volyntsev date: Fri Jun 09 20:28:15 2017 +0300 description: Object.defineProperties() method. diffstat: njs/njs_object.c | 85 ++++++++++++++++++++++++++++++++++++++++++----- njs/test/njs_unit_test.c | 21 +++++++++++ nxt/nxt_lvlhsh.h | 6 +++ 3 files changed, 103 insertions(+), 9 deletions(-) diffs (177 lines): diff -r 740823b9f444 -r 499ed5aa4f98 njs/njs_object.c --- a/njs/njs_object.c Fri Jun 09 17:55:21 2017 +0300 +++ b/njs/njs_object.c Fri Jun 09 20:28:15 2017 +0300 @@ -26,6 +26,8 @@ static nxt_int_t njs_object_hash_test(nxt_lvlhsh_query_t *lhq, void *data); +static njs_ret_t njs_define_property(njs_vm_t *vm, njs_object_t *object, + njs_value_t *name, njs_object_t *descriptor); nxt_noinline njs_object_t * @@ -416,27 +418,85 @@ static njs_ret_t njs_object_define_property(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, njs_index_t unused) { - nxt_int_t ret; - njs_object_t *object, *descriptor; - njs_object_prop_t *prop, *pr; - nxt_lvlhsh_query_t lhq, pq; + nxt_int_t ret; if (nargs < 4 || !njs_is_object(&args[1]) || !njs_is_object(&args[3])) { vm->exception = &njs_exception_type_error; return NXT_ERROR; } + ret = njs_define_property(vm, args[1].data.u.object, &args[2], + args[3].data.u.object); + + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } + + vm->retval = args[1]; + + return NXT_OK; +} + + +static njs_ret_t +njs_object_define_properties(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + nxt_int_t ret; + nxt_lvlhsh_t *hash; + njs_object_t *object; + nxt_lvlhsh_each_t lhe; + njs_object_prop_t *prop; + + if (nargs < 3 || !njs_is_object(&args[1]) || !njs_is_object(&args[2])) { + vm->exception = &njs_exception_type_error; + return NXT_ERROR; + } + + nxt_lvlhsh_each_init(&lhe, &njs_object_hash_proto); + object = args[1].data.u.object; - descriptor = args[3].data.u.object; + hash = &args[2].data.u.object->hash; - njs_string_get(&args[2], &lhq.key); + for ( ;; ) { + prop = nxt_lvlhsh_each(hash, &lhe); + + if (prop == NULL) { + break; + } + + if (prop->enumerable && njs_is_object(&prop->value)) { + ret = njs_define_property(vm, object, &prop->name, + prop->value.data.u.object); + + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } + } + } + + vm->retval = args[1]; + + return NXT_OK; +} + + +static njs_ret_t +njs_define_property(njs_vm_t *vm, njs_object_t *object, njs_value_t *name, + njs_object_t *descriptor) +{ + nxt_int_t ret; + njs_object_prop_t *prop, *pr; + nxt_lvlhsh_query_t lhq, pq; + + njs_string_get(name, &lhq.key); lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length); lhq.proto = &njs_object_hash_proto; ret = nxt_lvlhsh_find(&object->hash, &lhq); if (ret != NXT_OK) { - prop = njs_object_prop_alloc(vm, &args[2]); + prop = njs_object_prop_alloc(vm, name); if (nxt_slow_path(prop == NULL)) { return NXT_ERROR; @@ -497,8 +557,6 @@ njs_object_define_property(njs_vm_t *vm, return NXT_ERROR; } - vm->retval = args[1]; - return NXT_OK; } @@ -673,6 +731,15 @@ static const njs_object_prop_t njs_obje NJS_STRING_ARG, NJS_OBJECT_ARG), }, + /* Object.defineProperties(). */ + { + .type = NJS_METHOD, + .name = njs_long_string("defineProperties"), + .value = njs_native_function(njs_object_define_properties, 0, + NJS_SKIP_ARG, NJS_OBJECT_ARG, + NJS_OBJECT_ARG), + }, + /* Object.getPrototypeOf(). */ { .type = NJS_METHOD, diff -r 740823b9f444 -r 499ed5aa4f98 njs/test/njs_unit_test.c --- a/njs/test/njs_unit_test.c Fri Jun 09 17:55:21 2017 +0300 +++ b/njs/test/njs_unit_test.c Fri Jun 09 20:28:15 2017 +0300 @@ -5932,6 +5932,27 @@ static njs_unit_test_t njs_test[] = { nxt_string("var o = {}; Object.defineProperty(o)"), nxt_string("TypeError") }, + { nxt_string("var o = Object.defineProperties({}, {a:{value:1}}); o.a"), + nxt_string("1") }, + + { nxt_string("var o = Object.defineProperties({}, {a:{enumerable:true}, b:{enumerable:true}});" + "Object.keys(o)"), + nxt_string("a,b") }, + + { nxt_string("var desc = Object.defineProperty({b:{value:1, enumerable:true}}, 'a', {});" + "var o = Object.defineProperties({}, desc);" + "Object.keys(o)"), + nxt_string("b") }, + + { nxt_string("var o = Object.defineProperties({a:1}, {}); o.a"), + nxt_string("1") }, + + { nxt_string("Object.defineProperties(1, {})"), + nxt_string("TypeError") }, + + { nxt_string("Object.defineProperties({}, 1)"), + nxt_string("TypeError") }, + { nxt_string("var o = {a:1}; o.hasOwnProperty('a')"), nxt_string("true") }, diff -r 740823b9f444 -r 499ed5aa4f98 nxt/nxt_lvlhsh.h --- a/nxt/nxt_lvlhsh.h Fri Jun 09 17:55:21 2017 +0300 +++ b/nxt/nxt_lvlhsh.h Fri Jun 09 20:28:15 2017 +0300 @@ -173,6 +173,12 @@ typedef struct { } nxt_lvlhsh_each_t; +#define nxt_lvlhsh_each_init(lhe, _proto) \ + do { \ + memset(lhe, 0, sizeof(nxt_lvlhsh_each_t)); \ + (lhe)->proto = _proto; \ + } while (0) + NXT_EXPORT void *nxt_lvlhsh_each(nxt_lvlhsh_t *lh, nxt_lvlhsh_each_t *le); From paulyang.inf at gmail.com Fri Jun 9 18:05:39 2017 From: paulyang.inf at gmail.com (Paul Yang) Date: Sat, 10 Jun 2017 02:05:39 +0800 Subject: [nginx] support http2 per server In-Reply-To: References: <2417822.uuLYJAc3k0@vbart-workstation> <2058364.S2bnstnO8o@vbart-laptop> Message-ID: > On 9 Jun 2017, at 20:26, ??? wrote: > > Get it, thanks. > > On Fri, Jun 9, 2017 at 7:45 PM, Valentin V. Bartenev wrote: > On Friday 09 June 2017 13:40:23 ??? wrote: > > Hi, Valentin. > > > > " > > Also please note that with your patch clients are still able to > > negotiate HTTP/2 even if nginx doesn't announce it. > > " > > > > Two points: > > 1. The patch forbids the clients explicitly not support HTTP/2 doing v2 ( > > ngx_http_v2_init). > > How to follow you mean of "with the patch, clients are still able to > > negotiate HTTP/2" > > 2. "even if nginx doesn't announce it" > > Is it related to nginx? > > > [..] > > Your patch prevents advertising the protocol using ALPN and NPN, but > selecting protocol happens here (this part of code isn't touched): > http://hg.nginx.org/nginx/file/tip/src/http/ngx_http_request.c#l808 > > In case of NPN, client can select protocol even if it hasn't been > advertised by the server. Hmmm, interesting feature of NPN. > > wbr, Valentin V. Bartenev > > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel > > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel From george at ucdn.com Mon Jun 12 07:02:16 2017 From: george at ucdn.com (George .) Date: Mon, 12 Jun 2017 10:02:16 +0300 Subject: slice module issue if redirected origin and have have fist slice Message-ID: ??Hi, I've discovered following strange issue with http_slice_module If I have a named location for internal 302 redirect and caching one slice makes further request for whole object to brake upstream redirected request (missing Rage header, see frame 254 in the attached capture ? slice_redirect_problem.pcapng ? ). If there is no cached slice everything is okey (2nd capture? slice_redirect_no_problem.pcapng ?) Problem appears in main branch and also nginx/1.12 ... and may be in all versions nginx version: nginx/1.13.2 built by gcc 4.9.2 (Debian 4.9.2-10) configure arguments: --prefix=/home/george/run/nginx_hg --with-http_slice_module nginx.conf user cdnuser cdnuser; worker_processes 1; error_log logs/error.log debug; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; sendfile on; tcp_nopush on; proxy_cache_path /home/george/run/nginx_hg/cache/ keys_zone=zone_uid_default:4m levels=2:1 inactive=360d max_size=18329m; # our redirecting origin server { listen 8081; return 302 $scheme://127.0.0.1:8082$request_uri; } # our final origin server { listen 8082; add_header Cache-Control "max-age=3600"; root /home/george/run/nginx_hg/root; } server { listen 8080; server_name localhost; recursive_error_pages on; proxy_intercept_errors on; location / { slice 4m; proxy_cache zone_uid_default; proxy_cache_key $uri$is_args$args$slice_range; proxy_set_header Range $slice_range; proxy_pass http://localhost:8081; error_page 301 302 307 = @fetch_from_redirected_origin; } location @fetch_from_redirected_origin { slice 4m; internal; set $my_upstream_http_location $upstream_http_location; proxy_cache zone_uid_default; proxy_cache_key $uri$is_args$args$slice_range; proxy_set_header Range $slice_range; proxy_pass $my_upstream_http_location; } } } How to reproduce: 1. Create some empty object in our emulated origin mkdir /home/george/run/nginx_hg/root dd if=/dev/zero of=/home/george/run/nginx_hg/root/some_object bs=64M count=1 2. Ask our caching proxy for one 4m slice, so it will be cached curl -v -r 0-4194303 "http://127.0.0.1:8080/some_object" --header "Host: localhost" -o /dev/null 3. See it really there george at george ~/run/nginx_hg $ head /home/george/run/nginx_hg/cache/81/c/00214df7041ea53dd335ed5b055bfc81 ?:Y?:Y??:YV??r ? "593aa9cb-4000000" KEY: /some_objectbytes=0-4194303 HTTP/1.1 206 Partial Content Server: nginx/1.13.2 Date: Fri, 09 Jun 2017 14:16:20 GMT Content-Type: application/octet-stream Content-Length: 4194304 Last-Modified: Fri, 09 Jun 2017 13:59:39 GMT Connection: close ETag: "593aa9cb-4000000" 4. This time request the whole object curl -v "http://127.0.0.1:8080/some_object" --header "Host: localhost" -o /dev/null ?? -------------- next part -------------- An HTML attachment was scrubbed... URL: From xeioex at nginx.com Tue Jun 13 11:34:55 2017 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Tue, 13 Jun 2017 11:34:55 +0000 Subject: [njs] Using nxt_lvlhsh_each_init() where appropriate. Message-ID: details: http://hg.nginx.org/njs/rev/096d526744d5 branches: changeset: 362:096d526744d5 user: Dmitry Volyntsev date: Tue Jun 13 14:33:51 2017 +0300 description: Using nxt_lvlhsh_each_init() where appropriate. diffstat: njs/njs_generator.c | 3 +-- njs/njs_object.c | 6 ++---- njs/njs_variable.c | 9 +++------ njs/njs_vm.c | 3 +-- nxt/test/lvlhsh_unit_test.c | 3 +-- 5 files changed, 8 insertions(+), 16 deletions(-) diffs (95 lines): diff -r 499ed5aa4f98 -r 096d526744d5 njs/njs_generator.c --- a/njs/njs_generator.c Fri Jun 09 20:28:15 2017 +0300 +++ b/njs/njs_generator.c Tue Jun 13 14:33:51 2017 +0300 @@ -2112,8 +2112,7 @@ njs_generate_argument_closures(njs_parse return; } - memset(&lhe, 0, sizeof(nxt_lvlhsh_each_t)); - lhe.proto = &njs_variables_hash_proto; + nxt_lvlhsh_each_init(&lhe, &njs_variables_hash_proto); do { var = nxt_lvlhsh_each(&node->scope->variables, &lhe); diff -r 499ed5aa4f98 -r 096d526744d5 njs/njs_object.c --- a/njs/njs_object.c Fri Jun 09 20:28:15 2017 +0300 +++ b/njs/njs_object.c Tue Jun 13 14:33:51 2017 +0300 @@ -354,8 +354,7 @@ njs_object_keys(njs_vm_t *vm, njs_value_ } } - memset(&lhe, 0, sizeof(nxt_lvlhsh_each_t)); - lhe.proto = &njs_object_hash_proto; + nxt_lvlhsh_each_init(&lhe, &njs_object_hash_proto); hash = &args[1].data.u.object->hash; @@ -391,8 +390,7 @@ njs_object_keys(njs_vm_t *vm, njs_value_ } } - memset(&lhe, 0, sizeof(nxt_lvlhsh_each_t)); - lhe.proto = &njs_object_hash_proto; + nxt_lvlhsh_each_init(&lhe, &njs_object_hash_proto); for ( ;; ) { prop = nxt_lvlhsh_each(hash, &lhe); diff -r 499ed5aa4f98 -r 096d526744d5 njs/njs_variable.c --- a/njs/njs_variable.c Fri Jun 09 20:28:15 2017 +0300 +++ b/njs/njs_variable.c Tue Jun 13 14:33:51 2017 +0300 @@ -245,8 +245,7 @@ njs_variables_scope_reference(njs_vm_t * return NXT_ERROR; } - memset(&lhe, 0, sizeof(nxt_lvlhsh_each_t)); - lhe.proto = &njs_variables_hash_proto; + nxt_lvlhsh_each_init(&lhe, &njs_variables_hash_proto); for ( ;; ) { node = nxt_lvlhsh_each(&scope->references, &lhe); @@ -501,8 +500,7 @@ njs_vm_export_functions(njs_vm_t *vm) n = 1; - memset(&lhe, 0, sizeof(nxt_lvlhsh_each_t)); - lhe.proto = &njs_variables_hash_proto; + nxt_lvlhsh_each_init(&lhe, &njs_variables_hash_proto); for ( ;; ) { var = nxt_lvlhsh_each(&vm->variables_hash, &lhe); @@ -522,8 +520,7 @@ njs_vm_export_functions(njs_vm_t *vm) return NULL; } - memset(&lhe, 0, sizeof(nxt_lvlhsh_each_t)); - lhe.proto = &njs_variables_hash_proto; + nxt_lvlhsh_each_init(&lhe, &njs_variables_hash_proto); ex = export; diff -r 499ed5aa4f98 -r 096d526744d5 njs/njs_vm.c --- a/njs/njs_vm.c Fri Jun 09 20:28:15 2017 +0300 +++ b/njs/njs_vm.c Tue Jun 13 14:33:51 2017 +0300 @@ -1207,8 +1207,7 @@ njs_vmcode_property_foreach(njs_vm_t *vm vm->retval.data.u.next = next; - memset(&next->lhe, 0, sizeof(nxt_lvlhsh_each_t)); - next->lhe.proto = &njs_object_hash_proto; + nxt_lvlhsh_each_init(&next->lhe, &njs_object_hash_proto); next->index = -1; if (njs_is_array(object) && object->data.u.array->length != 0) { diff -r 499ed5aa4f98 -r 096d526744d5 nxt/test/lvlhsh_unit_test.c --- a/nxt/test/lvlhsh_unit_test.c Fri Jun 09 20:28:15 2017 +0300 +++ b/nxt/test/lvlhsh_unit_test.c Tue Jun 13 14:33:51 2017 +0300 @@ -236,8 +236,7 @@ lvlhsh_unit_test(nxt_uint_t n) } } - memset(&lhe, 0, sizeof(nxt_lvlhsh_each_t)); - lhe.proto = &lvlhsh_proto; + nxt_lvlhsh_each_init(&lhe, &lvlhsh_proto); for (i = 0; i < n + 1; i++) { if (nxt_lvlhsh_each(&lh, &lhe) == NULL) { From arut at nginx.com Tue Jun 13 11:44:48 2017 From: arut at nginx.com (Roman Arutyunyan) Date: Tue, 13 Jun 2017 14:44:48 +0300 Subject: slice module issue if redirected origin and have have fist slice In-Reply-To: References: Message-ID: <20170613114448.GQ77454@Romans-MacBook-Air.local> Hi George, On Mon, Jun 12, 2017 at 10:02:16AM +0300, George . wrote: > ??Hi, > I've discovered following strange issue with http_slice_module > If I have a named location for internal 302 redirect and caching one slice > makes further request for whole object to brake upstream redirected request > (missing Rage header, see frame 254 in the attached capture ? > slice_redirect_problem.pcapng > > ? ). What happens is: - client requests 0-4m - nginx creates the request for the 1st slice and proxies it to 8081 - after receiving 302, the request is redirected to @fetch_from_redirected_origin and the first slice is saved in the cache Note that in @fetch_from_redirected_origin there's a completely separate slice context. By this time nginx only knows what client sent. Previous slice context is completely lost as well as all other modules' contexts. Coincidentally, it does what you expect because only the first slice was requested. Then you request the entire file: - client request the entire file - first slice is sent from the cache - nginx creates a subrequest for the 2nd slice: 4m-8m and proxies it to 8081 - after receiving 302, the subrequest is redirected to @fetch_from_redirected_origin After the redirect nginx does not have any idea that it should fetch the second slice. Moreover, the $slice_range variable is not filled with actual range when first accessed in a subrequest (after error_page redirect it looks like the first access), so it remains empty. That's why the entire file is requested. But even if the variable was valid, that would still be bad since the slice context is lost after error_page redirect. You would get the whole file here instead of 4m-8m range. The takeaway is you should avoid using the slice module with redirects (error_page, X-Accel-Redirect) for fetching slices. Instead you should proxy directly to the origin server. > If there is no cached slice everything is okey (2nd capture? > slice_redirect_no_problem.pcapng > > ?) No, it's not ok. The first redirect to @fetch_from_redirected_origin leads to caching all file slices instead of the first one. > Problem appears in main branch and also nginx/1.12 ... and may be in all > versions > > nginx version: nginx/1.13.2 > built by gcc 4.9.2 (Debian 4.9.2-10) > configure arguments: --prefix=/home/george/run/nginx_hg > --with-http_slice_module > > > > > nginx.conf > user cdnuser cdnuser; > worker_processes 1; > > error_log logs/error.log debug; > > events { > worker_connections 1024; > } > > > http { > include mime.types; > default_type application/octet-stream; > > > sendfile on; > tcp_nopush on; > > proxy_cache_path /home/george/run/nginx_hg/cache/ > keys_zone=zone_uid_default:4m levels=2:1 inactive=360d max_size=18329m; > > # our redirecting origin > server { > listen 8081; > > return 302 $scheme://127.0.0.1:8082$request_uri; > } > > # our final origin > server { > listen 8082; > add_header Cache-Control "max-age=3600"; > root /home/george/run/nginx_hg/root; > } > > server { > listen 8080; > server_name localhost; > > recursive_error_pages on; > proxy_intercept_errors on; > > > location / { > slice 4m; > proxy_cache zone_uid_default; > proxy_cache_key $uri$is_args$args$slice_range; > proxy_set_header Range $slice_range; > > proxy_pass http://localhost:8081; > > error_page 301 302 307 = @fetch_from_redirected_origin; > } > > location @fetch_from_redirected_origin { > slice 4m; > > internal; > > set $my_upstream_http_location $upstream_http_location; > > proxy_cache zone_uid_default; > proxy_cache_key $uri$is_args$args$slice_range; > proxy_set_header Range $slice_range; > > proxy_pass $my_upstream_http_location; > } > } > } > > > How to reproduce: > > 1. Create some empty object in our emulated origin > mkdir /home/george/run/nginx_hg/root > dd if=/dev/zero of=/home/george/run/nginx_hg/root/some_object bs=64M > count=1 > > 2. Ask our caching proxy for one 4m slice, so it will be cached > curl -v -r 0-4194303 "http://127.0.0.1:8080/some_object" --header "Host: > localhost" -o /dev/null > > 3. See it really there > george at george ~/run/nginx_hg $ head > /home/george/run/nginx_hg/cache/81/c/00214df7041ea53dd335ed5b055bfc81 > ?:Y?:Y??:YV??r ? "593aa9cb-4000000" > KEY: /some_objectbytes=0-4194303 > HTTP/1.1 206 Partial Content > Server: nginx/1.13.2 > Date: Fri, 09 Jun 2017 14:16:20 GMT > Content-Type: application/octet-stream > Content-Length: 4194304 > Last-Modified: Fri, 09 Jun 2017 13:59:39 GMT > Connection: close > ETag: "593aa9cb-4000000" > > 4. This time request the whole object > curl -v "http://127.0.0.1:8080/some_object" --header "Host: localhost" -o > /dev/null > > > ?? > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel -- Roman Arutyunyan From piotrsikora at google.com Tue Jun 13 12:19:54 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Tue, 13 Jun 2017 05:19:54 -0700 Subject: [PATCH 2 of 3] HTTP/2: added support for trailers in HTTP responses In-Reply-To: <07a5d26b49f04425ff54.1497356393@piotrsikora.sfo.corp.google.com> References: <07a5d26b49f04425ff54.1497356393@piotrsikora.sfo.corp.google.com> Message-ID: <73f67e06ab103e0368d1.1497356394@piotrsikora.sfo.corp.google.com> # HG changeset patch # User Piotr Sikora # Date 1490351854 25200 # Fri Mar 24 03:37:34 2017 -0700 # Node ID 73f67e06ab103e0368d1810c6f8cac5c70c4e246 # Parent 07a5d26b49f04425ff54cc998f885aa987b7823f HTTP/2: added support for trailers in HTTP responses. Signed-off-by: Piotr Sikora diff -r 07a5d26b49f0 -r 73f67e06ab10 src/http/v2/ngx_http_v2_filter_module.c --- a/src/http/v2/ngx_http_v2_filter_module.c +++ b/src/http/v2/ngx_http_v2_filter_module.c @@ -50,13 +50,17 @@ #define NGX_HTTP_V2_SERVER_INDEX 54 #define NGX_HTTP_V2_VARY_INDEX 59 +#define NGX_HTTP_V2_NO_TRAILERS (ngx_http_v2_out_frame_t *) -1 + static u_char *ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len, u_char *tmp, ngx_uint_t lower); static u_char *ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix, ngx_uint_t value); static ngx_http_v2_out_frame_t *ngx_http_v2_create_headers_frame( - ngx_http_request_t *r, u_char *pos, u_char *end); + ngx_http_request_t *r, u_char *pos, u_char *end, ngx_uint_t fin); +static ngx_http_v2_out_frame_t *ngx_http_v2_create_trailers_frame( + ngx_http_request_t *r); static ngx_chain_t *ngx_http_v2_send_chain(ngx_connection_t *fc, ngx_chain_t *in, off_t limit); @@ -612,7 +616,7 @@ ngx_http_v2_header_filter(ngx_http_reque header[i].value.len, tmp); } - frame = ngx_http_v2_create_headers_frame(r, start, pos); + frame = ngx_http_v2_create_headers_frame(r, start, pos, r->header_only); if (frame == NULL) { return NGX_ERROR; } @@ -636,6 +640,118 @@ ngx_http_v2_header_filter(ngx_http_reque } +static ngx_http_v2_out_frame_t * +ngx_http_v2_create_trailers_frame(ngx_http_request_t *r) +{ + u_char *pos, *start, *tmp; + size_t len, tmp_len; + ngx_uint_t i; + ngx_list_part_t *part; + ngx_table_elt_t *header; + + len = 0; + tmp_len = 0; + + part = &r->headers_out.trailers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].hash == 0) { + continue; + } + + if (header[i].key.len > NGX_HTTP_V2_MAX_FIELD) { + ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, + "too long response trailer name: \"%V\"", + &header[i].key); + return NULL; + } + + if (header[i].value.len > NGX_HTTP_V2_MAX_FIELD) { + ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, + "too long response trailer value: \"%V: %V\"", + &header[i].key, &header[i].value); + return NULL; + } + + len += 1 + NGX_HTTP_V2_INT_OCTETS + header[i].key.len + + NGX_HTTP_V2_INT_OCTETS + header[i].value.len; + + if (header[i].key.len > tmp_len) { + tmp_len = header[i].key.len; + } + + if (header[i].value.len > tmp_len) { + tmp_len = header[i].value.len; + } + } + + if (len == 0) { + return NGX_HTTP_V2_NO_TRAILERS; + } + + tmp = ngx_palloc(r->pool, tmp_len); + pos = ngx_pnalloc(r->pool, len); + + if (pos == NULL || tmp == NULL) { + return NULL; + } + + start = pos; + + part = &r->headers_out.trailers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].hash == 0) { + continue; + } + +#if (NGX_DEBUG) + if (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP) { + ngx_strlow(tmp, header[i].key.data, header[i].key.len); + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http2 output trailer: \"%*s: %V\"", + header[i].key.len, tmp, &header[i].value); + } +#endif + + *pos++ = 0; + + pos = ngx_http_v2_write_name(pos, header[i].key.data, + header[i].key.len, tmp); + + pos = ngx_http_v2_write_value(pos, header[i].value.data, + header[i].value.len, tmp); + } + + return ngx_http_v2_create_headers_frame(r, start, pos, 1); +} + + static u_char * ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len, u_char *tmp, ngx_uint_t lower) @@ -686,7 +802,7 @@ ngx_http_v2_write_int(u_char *pos, ngx_u static ngx_http_v2_out_frame_t * ngx_http_v2_create_headers_frame(ngx_http_request_t *r, u_char *pos, - u_char *end) + u_char *end, ngx_uint_t fin) { u_char type, flags; size_t rest, frame_size; @@ -707,12 +823,12 @@ ngx_http_v2_create_headers_frame(ngx_htt frame->stream = stream; frame->length = rest; frame->blocked = 1; - frame->fin = r->header_only; + frame->fin = fin; ll = &frame->first; type = NGX_HTTP_V2_HEADERS_FRAME; - flags = r->header_only ? NGX_HTTP_V2_END_STREAM_FLAG : NGX_HTTP_V2_NO_FLAG; + flags = fin ? NGX_HTTP_V2_END_STREAM_FLAG : NGX_HTTP_V2_NO_FLAG; frame_size = stream->connection->frame_size; for ( ;; ) { @@ -776,7 +892,7 @@ ngx_http_v2_create_headers_frame(ngx_htt continue; } - b->last_buf = r->header_only; + b->last_buf = fin; cl->next = NULL; frame->last = cl; @@ -798,7 +914,7 @@ ngx_http_v2_send_chain(ngx_connection_t ngx_http_request_t *r; ngx_http_v2_stream_t *stream; ngx_http_v2_loc_conf_t *h2lcf; - ngx_http_v2_out_frame_t *frame; + ngx_http_v2_out_frame_t *frame, *trailers; ngx_http_v2_connection_t *h2c; r = fc->data; @@ -872,6 +988,8 @@ ngx_http_v2_send_chain(ngx_connection_t frame_size = (h2lcf->chunk_size < h2c->frame_size) ? h2lcf->chunk_size : h2c->frame_size; + trailers = NGX_HTTP_V2_NO_TRAILERS; + #if (NGX_SUPPRESS_WARN) cl = NULL; #endif @@ -934,19 +1052,39 @@ ngx_http_v2_send_chain(ngx_connection_t size -= rest; } - frame = ngx_http_v2_filter_get_data_frame(stream, frame_size, out, cl); - if (frame == NULL) { - return NGX_CHAIN_ERROR; + if (cl->buf->last_buf) { + trailers = ngx_http_v2_create_trailers_frame(r); + if (trailers == NULL) { + return NGX_CHAIN_ERROR; + } + + if (trailers != NGX_HTTP_V2_NO_TRAILERS) { + cl->buf->last_buf = 0; + } } - ngx_http_v2_queue_frame(h2c, frame); + if (frame_size || cl->buf->last_buf) { + frame = ngx_http_v2_filter_get_data_frame(stream, frame_size, + out, cl); + if (frame == NULL) { + return NGX_CHAIN_ERROR; + } - h2c->send_window -= frame_size; + ngx_http_v2_queue_frame(h2c, frame); - stream->send_window -= frame_size; - stream->queued++; + h2c->send_window -= frame_size; + + stream->send_window -= frame_size; + stream->queued++; + } if (in == NULL) { + + if (trailers != NGX_HTTP_V2_NO_TRAILERS) { + ngx_http_v2_queue_frame(h2c, trailers); + stream->queued++; + } + break; } From piotrsikora at google.com Tue Jun 13 12:19:53 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Tue, 13 Jun 2017 05:19:53 -0700 Subject: [PATCH 1 of 3] Added support for trailers in HTTP responses Message-ID: <07a5d26b49f04425ff54.1497356393@piotrsikora.sfo.corp.google.com> # HG changeset patch # User Piotr Sikora # Date 1490351854 25200 # Fri Mar 24 03:37:34 2017 -0700 # Node ID 07a5d26b49f04425ff54cc998f885aa987b7823f # Parent e6f399a176e7cae0fa08f1183d31315bce3b9ecb Added support for trailers in HTTP responses. Example: ngx_table_elt_t *h; h = ngx_list_push(&r->headers_out.trailers); if (h == NULL) { return NGX_ERROR; } ngx_str_set(&h->key, "Fun"); ngx_str_set(&h->value, "with trailers"); h->hash = ngx_hash_key_lc(h->key.data, h->key.len); The code above adds "Fun: with trailers" trailer to the response. Modules that want to emit trailers must set r->expect_trailers = 1 in header filter, otherwise they might not be emitted for HTTP/1.1 responses that aren't already chunked. This change also adds $sent_trailer_* variables. Signed-off-by: Piotr Sikora diff -r e6f399a176e7 -r 07a5d26b49f0 src/http/modules/ngx_http_chunked_filter_module.c --- a/src/http/modules/ngx_http_chunked_filter_module.c +++ b/src/http/modules/ngx_http_chunked_filter_module.c @@ -17,6 +17,8 @@ typedef struct { static ngx_int_t ngx_http_chunked_filter_init(ngx_conf_t *cf); +static ngx_chain_t *ngx_http_chunked_create_trailers(ngx_http_request_t *r, + ngx_http_chunked_filter_ctx_t *ctx); static ngx_http_module_t ngx_http_chunked_filter_module_ctx = { @@ -69,27 +71,29 @@ ngx_http_chunked_header_filter(ngx_http_ return ngx_http_next_header_filter(r); } - if (r->headers_out.content_length_n == -1) { - if (r->http_version < NGX_HTTP_VERSION_11) { + if (r->headers_out.content_length_n == -1 + || r->expect_trailers) + { + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (r->http_version >= NGX_HTTP_VERSION_11 + && clcf->chunked_transfer_encoding) + { + if (r->expect_trailers) { + ngx_http_clear_content_length(r); + } + + r->chunked = 1; + + ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_chunked_filter_ctx_t)); + if (ctx == NULL) { + return NGX_ERROR; + } + + ngx_http_set_ctx(r, ctx, ngx_http_chunked_filter_module); + + } else if (r->headers_out.content_length_n == -1) { r->keepalive = 0; - - } else { - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - - if (clcf->chunked_transfer_encoding) { - r->chunked = 1; - - ctx = ngx_pcalloc(r->pool, - sizeof(ngx_http_chunked_filter_ctx_t)); - if (ctx == NULL) { - return NGX_ERROR; - } - - ngx_http_set_ctx(r, ctx, ngx_http_chunked_filter_module); - - } else { - r->keepalive = 0; - } } } @@ -179,26 +183,17 @@ ngx_http_chunked_body_filter(ngx_http_re } if (cl->buf->last_buf) { - tl = ngx_chain_get_free_buf(r->pool, &ctx->free); + tl = ngx_http_chunked_create_trailers(r, ctx); if (tl == NULL) { return NGX_ERROR; } - b = tl->buf; - - b->tag = (ngx_buf_tag_t) &ngx_http_chunked_filter_module; - b->temporary = 0; - b->memory = 1; - b->last_buf = 1; - b->pos = (u_char *) CRLF "0" CRLF CRLF; - b->last = b->pos + 7; - cl->buf->last_buf = 0; *ll = tl; if (size == 0) { - b->pos += 2; + tl->buf->pos += 2; } } else if (size > 0) { @@ -230,6 +225,109 @@ ngx_http_chunked_body_filter(ngx_http_re } +static ngx_chain_t * +ngx_http_chunked_create_trailers(ngx_http_request_t *r, + ngx_http_chunked_filter_ctx_t *ctx) +{ + size_t len; + ngx_buf_t *b; + ngx_uint_t i; + ngx_chain_t *cl; + ngx_list_part_t *part; + ngx_table_elt_t *header; + + len = 0; + + part = &r->headers_out.trailers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].hash == 0) { + continue; + } + + len += header[i].key.len + sizeof(": ") - 1 + + header[i].value.len + sizeof(CRLF) - 1; + } + + cl = ngx_chain_get_free_buf(r->pool, &ctx->free); + if (cl == NULL) { + return NULL; + } + + b = cl->buf; + + b->tag = (ngx_buf_tag_t) &ngx_http_chunked_filter_module; + b->temporary = 0; + b->memory = 1; + b->last_buf = 1; + + if (len == 0) { + b->pos = (u_char *) CRLF "0" CRLF CRLF; + b->last = b->pos + sizeof(CRLF "0" CRLF CRLF) - 1; + return cl; + } + + len += sizeof(CRLF "0" CRLF CRLF) - 1; + + b->pos = ngx_palloc(r->pool, len); + if (b->pos == NULL) { + return NULL; + } + + b->last = b->pos; + + *b->last++ = CR; *b->last++ = LF; + *b->last++ = '0'; + *b->last++ = CR; *b->last++ = LF; + + part = &r->headers_out.trailers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].hash == 0) { + continue; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http trailer: \"%V: %V\"", + &header[i].key, &header[i].value); + + b->last = ngx_copy(b->last, header[i].key.data, header[i].key.len); + *b->last++ = ':'; *b->last++ = ' '; + + b->last = ngx_copy(b->last, header[i].value.data, header[i].value.len); + *b->last++ = CR; *b->last++ = LF; + } + + *b->last++ = CR; *b->last++ = LF; + + return cl; +} + + static ngx_int_t ngx_http_chunked_filter_init(ngx_conf_t *cf) { diff -r e6f399a176e7 -r 07a5d26b49f0 src/http/ngx_http_core_module.c --- a/src/http/ngx_http_core_module.c +++ b/src/http/ngx_http_core_module.c @@ -2485,6 +2485,13 @@ ngx_http_subrequest(ngx_http_request_t * return NGX_ERROR; } + if (ngx_list_init(&sr->headers_out.trailers, r->pool, 4, + sizeof(ngx_table_elt_t)) + != NGX_OK) + { + return NGX_ERROR; + } + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); sr->main_conf = cscf->ctx->main_conf; sr->srv_conf = cscf->ctx->srv_conf; diff -r e6f399a176e7 -r 07a5d26b49f0 src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -562,6 +562,14 @@ ngx_http_create_request(ngx_connection_t return NULL; } + if (ngx_list_init(&r->headers_out.trailers, r->pool, 4, + sizeof(ngx_table_elt_t)) + != NGX_OK) + { + ngx_destroy_pool(r->pool); + return NULL; + } + r->ctx = ngx_pcalloc(r->pool, sizeof(void *) * ngx_http_max_module); if (r->ctx == NULL) { ngx_destroy_pool(r->pool); diff -r e6f399a176e7 -r 07a5d26b49f0 src/http/ngx_http_request.h --- a/src/http/ngx_http_request.h +++ b/src/http/ngx_http_request.h @@ -252,6 +252,7 @@ typedef struct { typedef struct { ngx_list_t headers; + ngx_list_t trailers; ngx_uint_t status; ngx_str_t status_line; @@ -514,6 +515,7 @@ struct ngx_http_request_s { unsigned pipeline:1; unsigned chunked:1; unsigned header_only:1; + unsigned expect_trailers:1; unsigned keepalive:1; unsigned lingering_close:1; unsigned discard_body:1; diff -r e6f399a176e7 -r 07a5d26b49f0 src/http/ngx_http_variables.c --- a/src/http/ngx_http_variables.c +++ b/src/http/ngx_http_variables.c @@ -38,6 +38,8 @@ static ngx_int_t ngx_http_variable_unkno ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_unknown_header_out(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_unknown_trailer_out(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_request_line(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_cookie(ngx_http_request_t *r, @@ -365,6 +367,9 @@ static ngx_http_variable_t ngx_http_cor { ngx_string("sent_http_"), NULL, ngx_http_variable_unknown_header_out, 0, NGX_HTTP_VAR_PREFIX, 0 }, + { ngx_string("sent_trailer_"), NULL, ngx_http_variable_unknown_trailer_out, + 0, NGX_HTTP_VAR_PREFIX, 0 }, + { ngx_string("cookie_"), NULL, ngx_http_variable_cookie, 0, NGX_HTTP_VAR_PREFIX, 0 }, @@ -934,6 +939,16 @@ ngx_http_variable_unknown_header_out(ngx } +static ngx_int_t +ngx_http_variable_unknown_trailer_out(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + return ngx_http_variable_unknown_header(v, (ngx_str_t *) data, + &r->headers_out.trailers.part, + sizeof("sent_trailer_") - 1); +} + + ngx_int_t ngx_http_variable_unknown_header(ngx_http_variable_value_t *v, ngx_str_t *var, ngx_list_part_t *part, size_t prefix) From piotrsikora at google.com Tue Jun 13 12:19:55 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Tue, 13 Jun 2017 05:19:55 -0700 Subject: [PATCH 3 of 3] Headers filter: added "add_trailer" directive In-Reply-To: <07a5d26b49f04425ff54.1497356393@piotrsikora.sfo.corp.google.com> References: <07a5d26b49f04425ff54.1497356393@piotrsikora.sfo.corp.google.com> Message-ID: <46150fb672e7b92cfbe6.1497356395@piotrsikora.sfo.corp.google.com> # HG changeset patch # User Piotr Sikora # Date 1490351854 25200 # Fri Mar 24 03:37:34 2017 -0700 # Node ID 46150fb672e7b92cfbe678bad71df187fcb25ae6 # Parent 73f67e06ab103e0368d1810c6f8cac5c70c4e246 Headers filter: added "add_trailer" directive. Trailers added using this directive are evaluated after response body is processed by output filters (but before it's written to the wire), so it's possible to use variables calculated from the response body as the trailer value. Signed-off-by: Piotr Sikora diff -r 73f67e06ab10 -r 46150fb672e7 src/http/modules/ngx_http_headers_filter_module.c --- a/src/http/modules/ngx_http_headers_filter_module.c +++ b/src/http/modules/ngx_http_headers_filter_module.c @@ -48,6 +48,7 @@ typedef struct { time_t expires_time; ngx_http_complex_value_t *expires_value; ngx_array_t *headers; + ngx_array_t *trailers; } ngx_http_headers_conf_t; @@ -105,7 +106,15 @@ static ngx_command_t ngx_http_headers_f |NGX_CONF_TAKE23, ngx_http_headers_add, NGX_HTTP_LOC_CONF_OFFSET, - 0, + offsetof(ngx_http_headers_conf_t, headers), + NULL }, + + { ngx_string("add_trailer"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF + |NGX_CONF_TAKE23, + ngx_http_headers_add, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_headers_conf_t, trailers), NULL }, ngx_null_command @@ -144,6 +153,7 @@ ngx_module_t ngx_http_headers_filter_mo static ngx_http_output_header_filter_pt ngx_http_next_header_filter; +static ngx_http_output_body_filter_pt ngx_http_next_body_filter; static ngx_int_t @@ -154,10 +164,15 @@ ngx_http_headers_filter(ngx_http_request ngx_http_header_val_t *h; ngx_http_headers_conf_t *conf; + if (r != r->main) { + return ngx_http_next_header_filter(r); + } + conf = ngx_http_get_module_loc_conf(r, ngx_http_headers_filter_module); - if ((conf->expires == NGX_HTTP_EXPIRES_OFF && conf->headers == NULL) - || r != r->main) + if (conf->expires == NGX_HTTP_EXPIRES_OFF + && conf->headers == NULL + && conf->trailers == NULL) { return ngx_http_next_header_filter(r); } @@ -206,11 +221,101 @@ ngx_http_headers_filter(ngx_http_request } } + if (conf->trailers) { + h = conf->trailers->elts; + for (i = 0; i < conf->trailers->nelts; i++) { + + if (!safe_status && !h[i].always) { + continue; + } + + r->expect_trailers = 1; + break; + } + } + return ngx_http_next_header_filter(r); } static ngx_int_t +ngx_http_trailers_filter(ngx_http_request_t *r, ngx_chain_t *in) +{ + ngx_str_t value; + ngx_uint_t i, safe_status; + ngx_chain_t *cl; + ngx_table_elt_t *t; + ngx_http_header_val_t *h; + ngx_http_headers_conf_t *conf; + + conf = ngx_http_get_module_loc_conf(r, ngx_http_headers_filter_module); + + if (in == NULL + || conf->trailers == NULL + || !r->expect_trailers + || r->header_only) + { + return ngx_http_next_body_filter(r, in); + } + + for (cl = in; cl; cl = cl->next) { + if (cl->buf->last_buf) { + break; + } + } + + if (cl == NULL) { + return ngx_http_next_body_filter(r, in); + } + + switch (r->headers_out.status) { + + case NGX_HTTP_OK: + case NGX_HTTP_CREATED: + case NGX_HTTP_NO_CONTENT: + case NGX_HTTP_PARTIAL_CONTENT: + case NGX_HTTP_MOVED_PERMANENTLY: + case NGX_HTTP_MOVED_TEMPORARILY: + case NGX_HTTP_SEE_OTHER: + case NGX_HTTP_NOT_MODIFIED: + case NGX_HTTP_TEMPORARY_REDIRECT: + case NGX_HTTP_PERMANENT_REDIRECT: + safe_status = 1; + break; + + default: + safe_status = 0; + break; + } + + h = conf->trailers->elts; + for (i = 0; i < conf->trailers->nelts; i++) { + + if (!safe_status && !h[i].always) { + continue; + } + + if (ngx_http_complex_value(r, &h[i].value, &value) != NGX_OK) { + return NGX_ERROR; + } + + if (value.len) { + t = ngx_list_push(&r->headers_out.trailers); + if (t == NULL) { + return NGX_ERROR; + } + + t->key = h[i].key; + t->value = value; + t->hash = 1; + } + } + + return ngx_http_next_body_filter(r, in); +} + + +static ngx_int_t ngx_http_set_expires(ngx_http_request_t *r, ngx_http_headers_conf_t *conf) { char *err; @@ -557,6 +662,7 @@ ngx_http_headers_create_conf(ngx_conf_t * set by ngx_pcalloc(): * * conf->headers = NULL; + * conf->trailers = NULL; * conf->expires_time = 0; * conf->expires_value = NULL; */ @@ -587,6 +693,10 @@ ngx_http_headers_merge_conf(ngx_conf_t * conf->headers = prev->headers; } + if (conf->trailers == NULL) { + conf->trailers = prev->trailers; + } + return NGX_CONF_OK; } @@ -597,6 +707,9 @@ ngx_http_headers_filter_init(ngx_conf_t ngx_http_next_header_filter = ngx_http_top_header_filter; ngx_http_top_header_filter = ngx_http_headers_filter; + ngx_http_next_body_filter = ngx_http_top_body_filter; + ngx_http_top_body_filter = ngx_http_trailers_filter; + return NGX_OK; } @@ -674,42 +787,49 @@ ngx_http_headers_add(ngx_conf_t *cf, ngx { ngx_http_headers_conf_t *hcf = conf; - ngx_str_t *value; - ngx_uint_t i; - ngx_http_header_val_t *hv; - ngx_http_set_header_t *set; - ngx_http_compile_complex_value_t ccv; + ngx_str_t *value; + ngx_uint_t i; + ngx_array_t **headers; + ngx_http_header_val_t *hv; + ngx_http_set_header_t *set; + ngx_http_compile_complex_value_t ccv; value = cf->args->elts; - if (hcf->headers == NULL) { - hcf->headers = ngx_array_create(cf->pool, 1, - sizeof(ngx_http_header_val_t)); - if (hcf->headers == NULL) { + headers = (ngx_array_t **) ((char *) hcf + cmd->offset); + + if (*headers == NULL) { + *headers = ngx_array_create(cf->pool, 1, + sizeof(ngx_http_header_val_t)); + if (*headers == NULL) { return NGX_CONF_ERROR; } } - hv = ngx_array_push(hcf->headers); + hv = ngx_array_push(*headers); if (hv == NULL) { return NGX_CONF_ERROR; } hv->key = value[1]; - hv->handler = ngx_http_add_header; + hv->handler = NULL; hv->offset = 0; hv->always = 0; - set = ngx_http_set_headers; - for (i = 0; set[i].name.len; i++) { - if (ngx_strcasecmp(value[1].data, set[i].name.data) != 0) { - continue; + if (headers == &hcf->headers) { + hv->handler = ngx_http_add_header; + + set = ngx_http_set_headers; + for (i = 0; set[i].name.len; i++) { + if (ngx_strcasecmp(value[1].data, set[i].name.data) != 0) { + continue; + } + + hv->offset = set[i].offset; + hv->handler = set[i].handler; + + break; } - - hv->offset = set[i].offset; - hv->handler = set[i].handler; - - break; } if (value[2].len == 0) { From piotrsikora at google.com Tue Jun 13 12:19:45 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Tue, 13 Jun 2017 05:19:45 -0700 Subject: [PATCH 1 of 4] HTTP/2: reject HTTP/2 requests with "Connection" header Message-ID: <10c3f4c37f96ef496eff.1497356385@piotrsikora.sfo.corp.google.com> # HG changeset patch # User Piotr Sikora # Date 1490516709 25200 # Sun Mar 26 01:25:09 2017 -0700 # Node ID 10c3f4c37f96ef496eff859b6f6815817e79455a # Parent e6f399a176e7cae0fa08f1183d31315bce3b9ecb HTTP/2: reject HTTP/2 requests with "Connection" header. While there, populate r->headers_in.connection. Signed-off-by: Piotr Sikora diff -r e6f399a176e7 -r 10c3f4c37f96 src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -1678,6 +1678,22 @@ static ngx_int_t ngx_http_process_connection(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset) { + if (r->headers_in.connection == NULL) { + r->headers_in.connection = h; + } + +#if (NGX_HTTP_V2) + + if (r->stream) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent HTTP/2 request with \"Connection\" header"); + + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + return NGX_ERROR; + } + +#endif + if (ngx_strcasestrn(h->value.data, "close", 5 - 1)) { r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE; From piotrsikora at google.com Tue Jun 13 12:19:46 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Tue, 13 Jun 2017 05:19:46 -0700 Subject: [PATCH 2 of 4] HTTP/2: reject HTTP/2 requests with invalid "TE" header value In-Reply-To: <10c3f4c37f96ef496eff.1497356385@piotrsikora.sfo.corp.google.com> References: <10c3f4c37f96ef496eff.1497356385@piotrsikora.sfo.corp.google.com> Message-ID: <349648a6f91f9bd5cc80.1497356386@piotrsikora.sfo.corp.google.com> # HG changeset patch # User Piotr Sikora # Date 1490516709 25200 # Sun Mar 26 01:25:09 2017 -0700 # Node ID 349648a6f91f9bd5cc80d22390b95c2239a8bfb3 # Parent 10c3f4c37f96ef496eff859b6f6815817e79455a HTTP/2: reject HTTP/2 requests with invalid "TE" header value. Signed-off-by: Piotr Sikora diff -r 10c3f4c37f96 -r 349648a6f91f src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -27,6 +27,8 @@ static ngx_int_t ngx_http_process_host(n ngx_table_elt_t *h, ngx_uint_t offset); static ngx_int_t ngx_http_process_connection(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); +static ngx_int_t ngx_http_process_te(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset); static ngx_int_t ngx_http_process_user_agent(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); @@ -128,6 +130,10 @@ ngx_http_header_t ngx_http_headers_in[] offsetof(ngx_http_headers_in_t, if_range), ngx_http_process_unique_header_line }, + { ngx_string("TE"), + offsetof(ngx_http_headers_in_t, te), + ngx_http_process_te }, + { ngx_string("Transfer-Encoding"), offsetof(ngx_http_headers_in_t, transfer_encoding), ngx_http_process_header_line }, @@ -1706,6 +1712,37 @@ ngx_http_process_connection(ngx_http_req static ngx_int_t +ngx_http_process_te(ngx_http_request_t *r, ngx_table_elt_t *h, + ngx_uint_t offset) +{ + if (r->headers_in.te == NULL) { + r->headers_in.te = h; + } + + if (h->value.len == sizeof("trailers") - 1 + && ngx_memcmp(h->value.data, "trailers", sizeof("trailers") - 1) == 0) + { + return NGX_OK; + } + +#if (NGX_HTTP_V2) + + if (r->stream) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent HTTP/2 request with invalid header value: " + "\"TE: %V\"", &h->value); + + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + return NGX_ERROR; + } + +#endif + + return NGX_OK; +} + + +static ngx_int_t ngx_http_process_user_agent(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset) { diff -r 10c3f4c37f96 -r 349648a6f91f src/http/ngx_http_request.h --- a/src/http/ngx_http_request.h +++ b/src/http/ngx_http_request.h @@ -196,6 +196,7 @@ typedef struct { ngx_table_elt_t *range; ngx_table_elt_t *if_range; + ngx_table_elt_t *te; ngx_table_elt_t *transfer_encoding; ngx_table_elt_t *expect; ngx_table_elt_t *upgrade; From piotrsikora at google.com Tue Jun 13 12:19:47 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Tue, 13 Jun 2017 05:19:47 -0700 Subject: [PATCH 3 of 4] HTTP/2: reject HTTP/2 requests with "Transfer-Encoding" header In-Reply-To: <10c3f4c37f96ef496eff.1497356385@piotrsikora.sfo.corp.google.com> References: <10c3f4c37f96ef496eff.1497356385@piotrsikora.sfo.corp.google.com> Message-ID: <6263d68cb96042d8f897.1497356387@piotrsikora.sfo.corp.google.com> # HG changeset patch # User Piotr Sikora # Date 1490516709 25200 # Sun Mar 26 01:25:09 2017 -0700 # Node ID 6263d68cb96042d8f8974a4a3945226227ce13b9 # Parent 349648a6f91f9bd5cc80d22390b95c2239a8bfb3 HTTP/2: reject HTTP/2 requests with "Transfer-Encoding" header. Signed-off-by: Piotr Sikora diff -r 349648a6f91f -r 6263d68cb960 src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -29,6 +29,8 @@ static ngx_int_t ngx_http_process_connec ngx_table_elt_t *h, ngx_uint_t offset); static ngx_int_t ngx_http_process_te(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); +static ngx_int_t ngx_http_process_transfer_encoding(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset); static ngx_int_t ngx_http_process_user_agent(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); @@ -136,7 +138,7 @@ ngx_http_header_t ngx_http_headers_in[] { ngx_string("Transfer-Encoding"), offsetof(ngx_http_headers_in_t, transfer_encoding), - ngx_http_process_header_line }, + ngx_http_process_transfer_encoding }, { ngx_string("Expect"), offsetof(ngx_http_headers_in_t, expect), @@ -1743,6 +1745,49 @@ ngx_http_process_te(ngx_http_request_t * static ngx_int_t +ngx_http_process_transfer_encoding(ngx_http_request_t *r, ngx_table_elt_t *h, + ngx_uint_t offset) +{ + if (r->headers_in.transfer_encoding == NULL) { + r->headers_in.transfer_encoding = h; + } + +#if (NGX_HTTP_V2) + + if (r->stream) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent HTTP/2 request with \"Transfer-Encoding\" " + "header"); + + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + return NGX_ERROR; + } + +#endif + + if (h->value.len == 7 + && ngx_strncasecmp(h->value.data, (u_char *) "chunked", 7) == 0) + { + r->headers_in.chunked = 1; + return NGX_OK; + } + + if (h->value.len != 8 + || ngx_strncasecmp(h->value.data, (u_char *) "identity", 8) != 0) + { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent unknown \"Transfer-Encoding: %V\" header", + &h->value); + + ngx_http_finalize_request(r, NGX_HTTP_NOT_IMPLEMENTED); + return NGX_ERROR; + } + + return NGX_OK; +} + + +static ngx_int_t ngx_http_process_user_agent(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset) { @@ -1881,25 +1926,9 @@ ngx_http_process_request_header(ngx_http return NGX_ERROR; } - if (r->headers_in.transfer_encoding) { - if (r->headers_in.transfer_encoding->value.len == 7 - && ngx_strncasecmp(r->headers_in.transfer_encoding->value.data, - (u_char *) "chunked", 7) == 0) - { - r->headers_in.content_length = NULL; - r->headers_in.content_length_n = -1; - r->headers_in.chunked = 1; - - } else if (r->headers_in.transfer_encoding->value.len != 8 - || ngx_strncasecmp(r->headers_in.transfer_encoding->value.data, - (u_char *) "identity", 8) != 0) - { - ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, - "client sent unknown \"Transfer-Encoding\": \"%V\"", - &r->headers_in.transfer_encoding->value); - ngx_http_finalize_request(r, NGX_HTTP_NOT_IMPLEMENTED); - return NGX_ERROR; - } + if (r->headers_in.chunked) { + r->headers_in.content_length = NULL; + r->headers_in.content_length_n = -1; } if (r->headers_in.connection_type == NGX_HTTP_CONNECTION_KEEP_ALIVE) { From piotrsikora at google.com Tue Jun 13 12:19:48 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Tue, 13 Jun 2017 05:19:48 -0700 Subject: [PATCH 4 of 4] HTTP/2: reject HTTP/2 requests with connection-specific headers In-Reply-To: <10c3f4c37f96ef496eff.1497356385@piotrsikora.sfo.corp.google.com> References: <10c3f4c37f96ef496eff.1497356385@piotrsikora.sfo.corp.google.com> Message-ID: # HG changeset patch # User Piotr Sikora # Date 1490516709 25200 # Sun Mar 26 01:25:09 2017 -0700 # Node ID e2abc3bc3fc12b788d2631d3c47215acdc4ebbe6 # Parent 6263d68cb96042d8f8974a4a3945226227ce13b9 HTTP/2: reject HTTP/2 requests with connection-specific headers. Signed-off-by: Piotr Sikora diff -r 6263d68cb960 -r e2abc3bc3fc1 src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -19,6 +19,8 @@ static ngx_int_t ngx_http_alloc_large_he static ngx_int_t ngx_http_process_header_line(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); +static ngx_int_t ngx_http_process_http1_header_line(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset); static ngx_int_t ngx_http_process_unique_header_line(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); static ngx_int_t ngx_http_process_multi_header_lines(ngx_http_request_t *r, @@ -146,7 +148,7 @@ ngx_http_header_t ngx_http_headers_in[] { ngx_string("Upgrade"), offsetof(ngx_http_headers_in_t, upgrade), - ngx_http_process_header_line }, + ngx_http_process_http1_header_line }, #if (NGX_HTTP_GZIP) { ngx_string("Accept-Encoding"), @@ -161,8 +163,13 @@ ngx_http_header_t ngx_http_headers_in[] offsetof(ngx_http_headers_in_t, authorization), ngx_http_process_unique_header_line }, - { ngx_string("Keep-Alive"), offsetof(ngx_http_headers_in_t, keep_alive), - ngx_http_process_header_line }, + { ngx_string("Keep-Alive"), + offsetof(ngx_http_headers_in_t, keep_alive), + ngx_http_process_http1_header_line }, + + { ngx_string("Proxy-Connection"), + offsetof(ngx_http_headers_in_t, proxy_connection), + ngx_http_process_http1_header_line }, #if (NGX_HTTP_X_FORWARDED_FOR) { ngx_string("X-Forwarded-For"), @@ -1618,6 +1625,35 @@ ngx_http_process_header_line(ngx_http_re static ngx_int_t +ngx_http_process_http1_header_line(ngx_http_request_t *r, ngx_table_elt_t *h, + ngx_uint_t offset) +{ + ngx_table_elt_t **ph; + + ph = (ngx_table_elt_t **) ((char *) &r->headers_in + offset); + + if (*ph == NULL) { + *ph = h; + } + +#if (NGX_HTTP_V2) + + if (r->stream) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent HTTP/2 request with \"%V\" header", + &h->key); + + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + return NGX_ERROR; + } + +#endif + + return NGX_OK; +} + + +static ngx_int_t ngx_http_process_unique_header_line(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset) { diff -r 6263d68cb960 -r e2abc3bc3fc1 src/http/ngx_http_request.h --- a/src/http/ngx_http_request.h +++ b/src/http/ngx_http_request.h @@ -209,6 +209,7 @@ typedef struct { ngx_table_elt_t *authorization; ngx_table_elt_t *keep_alive; + ngx_table_elt_t *proxy_connection; #if (NGX_HTTP_X_FORWARDED_FOR) ngx_array_t x_forwarded_for; From piotrsikora at google.com Tue Jun 13 12:21:56 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Tue, 13 Jun 2017 05:21:56 -0700 Subject: [PATCH 1 of 3] Added support for trailers in HTTP responses In-Reply-To: <20170606122554.GD55433@mdounin.ru> References: <41c09a2fd90410e25ad8.1496460825@piotrsikora.sfo.corp.google.com> <20170605162940.GY55433@mdounin.ru> <20170606122554.GD55433@mdounin.ru> Message-ID: Hey Maxim, > I've tried this as well, and decided that "if (len == > sizeof(...))" is slightly more readable, and also produces smaller > patch to your code. No strict preference though, feel free to > use any variant you think is better. I've ended up using "if (len == 0) { ... }" in the end, but applied rest of you changes. Thanks! Best regards, Piotr Sikora From piotrsikora at google.com Tue Jun 13 12:24:31 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Tue, 13 Jun 2017 05:24:31 -0700 Subject: [PATCH 2 of 3] HTTP/2: added support for trailers in HTTP responses In-Reply-To: <8319246.PdA5WZVuiQ@vbart-workstation> References: <8d74ff6c2015180f5c1f.1496460826@piotrsikora.sfo.corp.google.com> <8319246.PdA5WZVuiQ@vbart-workstation> Message-ID: Hey Valentin, > It's better to keep return values consistent with > ngx_http_v2_create_headers_frame() and introduce > NGX_HTTP_V2_NO_TRAILERS instead of NGX_HTTP_V2_FRAME_ERROR. NGX_HTTP_V2_NO_TRAILERS feels a bit weird, but whatever works for you is fine. > There's no reason to check "trailers" on each iteration. > I think you can put it inside the "if (in == NULL)" condition. Good catch, thanks! > Please consider the changes below. Applied (with removed empty lines between error message and "return NULL"). Thanks! Best regards, Piotr Sikora From piotrsikora at google.com Tue Jun 13 12:30:22 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Tue, 13 Jun 2017 05:30:22 -0700 Subject: [PATCH] HTTP/2: reject HTTP/2 requests with "Connection" header In-Reply-To: <42772973.0zRm91nVQn@vbart-workstation> References: <42772973.0zRm91nVQn@vbart-workstation> Message-ID: Hey Valentin, > Since HTTP/2 is a separate protocol and not just GET / HTTP/2.0, so > the r->stream pointer should be tested instead (like in many other > places). Done. I've also sent few more patches rejecting remaining invalid headers. Feel free to squash them together if you prefer. Best regards, Piotr Sikora From george at ucdn.com Tue Jun 13 13:39:08 2017 From: george at ucdn.com (George .) Date: Tue, 13 Jun 2017 16:39:08 +0300 Subject: slice module issue if redirected origin and have have fist slice In-Reply-To: <20170613114448.GQ77454@Romans-MacBook-Air.local> References: <20170613114448.GQ77454@Romans-MacBook-Air.local> Message-ID: Hi Roman, Thank you a lot for detailed explanation. Initially I thought that NGX_HTTP_SUBREQUEST_CLONE option to ngx_http_subrequest (your latest fix in slice module - Slice filter: fetch slices in cloned subrequests) was intended to make full context in subrequest to be kept during redirects and because of minor bug it does not work in my case. I'll need some solution for cases like this: 1. Origin redirects us (assuming that it not under our administration and we can't do anything) 2. Origin is missing a object and send us 404, so we have to fallback to other origin If you have some suggestion how to workaround this tricky situation - I be very thankful. George On Tue, Jun 13, 2017 at 2:44 PM, Roman Arutyunyan wrote: > Hi George, > > On Mon, Jun 12, 2017 at 10:02:16AM +0300, George . wrote: > > ??Hi, > > I've discovered following strange issue with http_slice_module > > If I have a named location for internal 302 redirect and caching one > slice > > makes further request for whole object to brake upstream redirected > request > > (missing Rage header, see frame 254 in the attached capture ? > > slice_redirect_problem.pcapng > > 0ByZ2nt00gtJ2NmtqVUU3OVozNXM/view?usp=drive_web> > > ? ). > > What happens is: > > - client requests 0-4m > - nginx creates the request for the 1st slice and proxies it to 8081 > - after receiving 302, the request is redirected to > @fetch_from_redirected_origin and the first slice is saved in the cache > > Note that in @fetch_from_redirected_origin there's a completely separate > slice context. By this time nginx only knows what client sent. Previous > slice context is completely lost as well as all other modules' contexts. > Coincidentally, it does what you expect because only the first slice was > requested. > > Then you request the entire file: > > - client request the entire file > - first slice is sent from the cache > - nginx creates a subrequest for the 2nd slice: 4m-8m and proxies it to > 8081 > - after receiving 302, the subrequest is redirected to > @fetch_from_redirected_origin > > After the redirect nginx does not have any idea that it should fetch the > second > slice. Moreover, the $slice_range variable is not filled with actual > range when > first accessed in a subrequest (after error_page redirect it looks like the > first access), so it remains empty. That's why the entire file is > requested. > But even if the variable was valid, that would still be bad since the slice > context is lost after error_page redirect. You would get the whole file > here > instead of 4m-8m range. > > The takeaway is you should avoid using the slice module with redirects > (error_page, X-Accel-Redirect) for fetching slices. Instead you should > proxy > directly to the origin server. > > > If there is no cached slice everything is okey (2nd capture? > > slice_redirect_no_problem.pcapng > > 0ByZ2nt00gtJ2SUpnc2VVbzBKdWc/view?usp=drive_web> > > ?) > > No, it's not ok. The first redirect to @fetch_from_redirected_origin leads > to caching all file slices instead of the first one. > > > Problem appears in main branch and also nginx/1.12 ... and may be in all > > versions > > > > nginx version: nginx/1.13.2 > > built by gcc 4.9.2 (Debian 4.9.2-10) > > configure arguments: --prefix=/home/george/run/nginx_hg > > --with-http_slice_module > > > > > > > > > > nginx.conf > > user cdnuser cdnuser; > > worker_processes 1; > > > > error_log logs/error.log debug; > > > > events { > > worker_connections 1024; > > } > > > > > > http { > > include mime.types; > > default_type application/octet-stream; > > > > > > sendfile on; > > tcp_nopush on; > > > > proxy_cache_path /home/george/run/nginx_hg/cache/ > > keys_zone=zone_uid_default:4m levels=2:1 inactive=360d max_size=18329m; > > > > # our redirecting origin > > server { > > listen 8081; > > > > return 302 $scheme://127.0.0.1:8082$request_uri; > > } > > > > # our final origin > > server { > > listen 8082; > > add_header Cache-Control "max-age=3600"; > > root /home/george/run/nginx_hg/root; > > } > > > > server { > > listen 8080; > > server_name localhost; > > > > recursive_error_pages on; > > proxy_intercept_errors on; > > > > > > location / { > > slice 4m; > > proxy_cache zone_uid_default; > > proxy_cache_key $uri$is_args$args$slice_range; > > proxy_set_header Range $slice_range; > > > > proxy_pass http://localhost:8081; > > > > error_page 301 302 307 = @fetch_from_redirected_origin; > > } > > > > location @fetch_from_redirected_origin { > > slice 4m; > > > > internal; > > > > set $my_upstream_http_location $upstream_http_location; > > > > proxy_cache zone_uid_default; > > proxy_cache_key $uri$is_args$args$slice_range; > > proxy_set_header Range $slice_range; > > > > proxy_pass $my_upstream_http_location; > > } > > } > > } > > > > > > How to reproduce: > > > > 1. Create some empty object in our emulated origin > > mkdir /home/george/run/nginx_hg/root > > dd if=/dev/zero of=/home/george/run/nginx_hg/root/some_object bs=64M > > count=1 > > > > 2. Ask our caching proxy for one 4m slice, so it will be cached > > curl -v -r 0-4194303 "http://127.0.0.1:8080/some_object" --header "Host: > > localhost" -o /dev/null > > > > 3. See it really there > > george at george ~/run/nginx_hg $ head > > /home/george/run/nginx_hg/cache/81/c/00214df7041ea53dd335ed5b055bfc81 > > ?:Y?:Y??:YV??r ? "593aa9cb-4000000" > > KEY: /some_objectbytes=0-4194303 > > HTTP/1.1 206 Partial Content > > Server: nginx/1.13.2 > > Date: Fri, 09 Jun 2017 14:16:20 GMT > > Content-Type: application/octet-stream > > Content-Length: 4194304 > > Last-Modified: Fri, 09 Jun 2017 13:59:39 GMT > > Connection: close > > ETag: "593aa9cb-4000000" > > > > 4. This time request the whole object > > curl -v "http://127.0.0.1:8080/some_object" --header "Host: localhost" > -o > > /dev/null > > > > > > ?? > > > _______________________________________________ > > nginx-devel mailing list > > nginx-devel at nginx.org > > http://mailman.nginx.org/mailman/listinfo/nginx-devel > > > -- > Roman Arutyunyan > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel -------------- next part -------------- An HTML attachment was scrubbed... URL: From vbart at nginx.com Tue Jun 13 14:03:19 2017 From: vbart at nginx.com (Valentin Bartenev) Date: Tue, 13 Jun 2017 14:03:19 +0000 Subject: [nginx] HTTP/2: reject HTTP/2 requests without ":scheme" pseudo-header. Message-ID: details: http://hg.nginx.org/nginx/rev/3c55863e6887 branches: changeset: 7029:3c55863e6887 user: Piotr Sikora date: Tue Jun 13 17:01:08 2017 +0300 description: HTTP/2: reject HTTP/2 requests without ":scheme" pseudo-header. Signed-off-by: Piotr Sikora diffstat: src/http/v2/ngx_http_v2.c | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diffs (11 lines): diff -r e6f399a176e7 -r 3c55863e6887 src/http/v2/ngx_http_v2.c --- a/src/http/v2/ngx_http_v2.c Wed Jun 07 18:46:36 2017 +0300 +++ b/src/http/v2/ngx_http_v2.c Tue Jun 13 17:01:08 2017 +0300 @@ -3332,6 +3332,7 @@ ngx_http_v2_construct_request_line(ngx_h static const u_char ending[] = " HTTP/2.0"; if (r->method_name.len == 0 + || r->schema_start == NULL || r->unparsed_uri.len == 0) { ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); From vbart at nginx.com Tue Jun 13 14:04:32 2017 From: vbart at nginx.com (Valentin V. Bartenev) Date: Tue, 13 Jun 2017 17:04:32 +0300 Subject: [PATCH] HTTP/2: reject HTTP/2 requests without ":scheme" pseudo-header In-Reply-To: <2695745.s2C5zqsLaN@vbart-workstation> References: <6bb029b1df11662ba11e.1490517677@piotrsikora.sfo.corp.google.com> <2695745.s2C5zqsLaN@vbart-workstation> Message-ID: <3040583.cn0FhNP0d3@vbart-workstation> On Thursday 08 June 2017 20:45:18 Valentin V. Bartenev wrote: > On Wednesday 31 May 2017 15:48:34 Piotr Sikora via nginx-devel wrote: > > Hey Valentin, > > > > > As the 1.11 branch is going to be stable soon, it's a good idea to postpone > > > any changes that explicitly affect interoperability (at least till 1.13). > > > > Any thoughts on this now that 1.12 branched? > > > [..] > > If there will be no other objections against the change, I'll commit > this patch next week. > Committed: http://hg.nginx.org/nginx/rev/3c55863e6887 wbr, Valentin V. Bartenev From arut at nginx.com Tue Jun 13 14:12:02 2017 From: arut at nginx.com (Roman Arutyunyan) Date: Tue, 13 Jun 2017 17:12:02 +0300 Subject: slice module issue if redirected origin and have have fist slice In-Reply-To: References: <20170613114448.GQ77454@Romans-MacBook-Air.local> Message-ID: <20170613141202.GR77454@Romans-MacBook-Air.local> Hi George, On Tue, Jun 13, 2017 at 04:39:08PM +0300, George . wrote: > Hi Roman, > > Thank you a lot for detailed explanation. > Initially I thought that NGX_HTTP_SUBREQUEST_CLONE option to > ngx_http_subrequest (your latest fix in slice module - Slice filter: fetch > slices in cloned subrequests) was intended to make full context in > subrequest to be kept during redirects and because of minor bug it does not > work in my case. > > I'll need some solution for cases like this: > > 1. Origin redirects us (assuming that it not under our administration and > we can't do anything) You can add an intermediate proxy between the sliced proxy and the origin. At that proxy you have the proper Range header, so you are not limited with the slice module limitations that I mentioned. You can even reuse the same cache if this proxy is in the same nginx instance. > 2. Origin is missing a object and send us 404, so we have to fallback to > other origin You can use proxy_next_upstream to switch to the next origin in certain cases, 404 response code is one of them. > If you have some suggestion how to workaround this tricky situation - I be > very thankful. > George > > On Tue, Jun 13, 2017 at 2:44 PM, Roman Arutyunyan wrote: > > > Hi George, > > > > On Mon, Jun 12, 2017 at 10:02:16AM +0300, George . wrote: > > > ??Hi, > > > I've discovered following strange issue with http_slice_module > > > If I have a named location for internal 302 redirect and caching one > > slice > > > makes further request for whole object to brake upstream redirected > > request > > > (missing Rage header, see frame 254 in the attached capture ? > > > slice_redirect_problem.pcapng > > > > 0ByZ2nt00gtJ2NmtqVUU3OVozNXM/view?usp=drive_web> > > > ? ). > > > > What happens is: > > > > - client requests 0-4m > > - nginx creates the request for the 1st slice and proxies it to 8081 > > - after receiving 302, the request is redirected to > > @fetch_from_redirected_origin and the first slice is saved in the cache > > > > Note that in @fetch_from_redirected_origin there's a completely separate > > slice context. By this time nginx only knows what client sent. Previous > > slice context is completely lost as well as all other modules' contexts. > > Coincidentally, it does what you expect because only the first slice was > > requested. > > > > Then you request the entire file: > > > > - client request the entire file > > - first slice is sent from the cache > > - nginx creates a subrequest for the 2nd slice: 4m-8m and proxies it to > > 8081 > > - after receiving 302, the subrequest is redirected to > > @fetch_from_redirected_origin > > > > After the redirect nginx does not have any idea that it should fetch the > > second > > slice. Moreover, the $slice_range variable is not filled with actual > > range when > > first accessed in a subrequest (after error_page redirect it looks like the > > first access), so it remains empty. That's why the entire file is > > requested. > > But even if the variable was valid, that would still be bad since the slice > > context is lost after error_page redirect. You would get the whole file > > here > > instead of 4m-8m range. > > > > The takeaway is you should avoid using the slice module with redirects > > (error_page, X-Accel-Redirect) for fetching slices. Instead you should > > proxy > > directly to the origin server. > > > > > If there is no cached slice everything is okey (2nd capture? > > > slice_redirect_no_problem.pcapng > > > > 0ByZ2nt00gtJ2SUpnc2VVbzBKdWc/view?usp=drive_web> > > > ?) > > > > No, it's not ok. The first redirect to @fetch_from_redirected_origin leads > > to caching all file slices instead of the first one. > > > > > Problem appears in main branch and also nginx/1.12 ... and may be in all > > > versions > > > > > > nginx version: nginx/1.13.2 > > > built by gcc 4.9.2 (Debian 4.9.2-10) > > > configure arguments: --prefix=/home/george/run/nginx_hg > > > --with-http_slice_module > > > > > > > > > > > > > > > nginx.conf > > > user cdnuser cdnuser; > > > worker_processes 1; > > > > > > error_log logs/error.log debug; > > > > > > events { > > > worker_connections 1024; > > > } > > > > > > > > > http { > > > include mime.types; > > > default_type application/octet-stream; > > > > > > > > > sendfile on; > > > tcp_nopush on; > > > > > > proxy_cache_path /home/george/run/nginx_hg/cache/ > > > keys_zone=zone_uid_default:4m levels=2:1 inactive=360d max_size=18329m; > > > > > > # our redirecting origin > > > server { > > > listen 8081; > > > > > > return 302 $scheme://127.0.0.1:8082$request_uri; > > > } > > > > > > # our final origin > > > server { > > > listen 8082; > > > add_header Cache-Control "max-age=3600"; > > > root /home/george/run/nginx_hg/root; > > > } > > > > > > server { > > > listen 8080; > > > server_name localhost; > > > > > > recursive_error_pages on; > > > proxy_intercept_errors on; > > > > > > > > > location / { > > > slice 4m; > > > proxy_cache zone_uid_default; > > > proxy_cache_key $uri$is_args$args$slice_range; > > > proxy_set_header Range $slice_range; > > > > > > proxy_pass http://localhost:8081; > > > > > > error_page 301 302 307 = @fetch_from_redirected_origin; > > > } > > > > > > location @fetch_from_redirected_origin { > > > slice 4m; > > > > > > internal; > > > > > > set $my_upstream_http_location $upstream_http_location; > > > > > > proxy_cache zone_uid_default; > > > proxy_cache_key $uri$is_args$args$slice_range; > > > proxy_set_header Range $slice_range; > > > > > > proxy_pass $my_upstream_http_location; > > > } > > > } > > > } > > > > > > > > > How to reproduce: > > > > > > 1. Create some empty object in our emulated origin > > > mkdir /home/george/run/nginx_hg/root > > > dd if=/dev/zero of=/home/george/run/nginx_hg/root/some_object bs=64M > > > count=1 > > > > > > 2. Ask our caching proxy for one 4m slice, so it will be cached > > > curl -v -r 0-4194303 "http://127.0.0.1:8080/some_object" --header "Host: > > > localhost" -o /dev/null > > > > > > 3. See it really there > > > george at george ~/run/nginx_hg $ head > > > /home/george/run/nginx_hg/cache/81/c/00214df7041ea53dd335ed5b055bfc81 > > > ?:Y?:Y??:YV??r ? "593aa9cb-4000000" > > > KEY: /some_objectbytes=0-4194303 > > > HTTP/1.1 206 Partial Content > > > Server: nginx/1.13.2 > > > Date: Fri, 09 Jun 2017 14:16:20 GMT > > > Content-Type: application/octet-stream > > > Content-Length: 4194304 > > > Last-Modified: Fri, 09 Jun 2017 13:59:39 GMT > > > Connection: close > > > ETag: "593aa9cb-4000000" > > > > > > 4. This time request the whole object > > > curl -v "http://127.0.0.1:8080/some_object" --header "Host: localhost" > > -o > > > /dev/null > > > > > > > > > ?? > > > > > _______________________________________________ > > > nginx-devel mailing list > > > nginx-devel at nginx.org > > > http://mailman.nginx.org/mailman/listinfo/nginx-devel > > > > > > -- > > Roman Arutyunyan > > _______________________________________________ > > nginx-devel mailing list > > nginx-devel at nginx.org > > http://mailman.nginx.org/mailman/listinfo/nginx-devel > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel -- Roman Arutyunyan From mdounin at mdounin.ru Tue Jun 13 14:51:16 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Tue, 13 Jun 2017 14:51:16 +0000 Subject: [nginx] Configure: fix compilation on MSYS2 / MinGW64. Message-ID: details: http://hg.nginx.org/nginx/rev/4a343228c55e branches: changeset: 7030:4a343228c55e user: Orgad Shaneh date: Tue Jun 06 18:13:39 2017 +0300 description: Configure: fix compilation on MSYS2 / MinGW64. diffstat: auto/configure | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diffs (12 lines): diff --git a/auto/configure b/auto/configure --- a/auto/configure +++ b/auto/configure @@ -36,7 +36,7 @@ if test -z "$NGX_PLATFORM"; then NGX_PLATFORM="$NGX_SYSTEM:$NGX_RELEASE:$NGX_MACHINE"; case "$NGX_SYSTEM" in - MINGW32_*) + MINGW32_* | MINGW64_* | MSYS_*) NGX_PLATFORM=win32 ;; esac From mdounin at mdounin.ru Tue Jun 13 14:51:19 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Tue, 13 Jun 2017 14:51:19 +0000 Subject: [nginx] Configure: use .exe for binaries for all win32 compilers. Message-ID: details: http://hg.nginx.org/nginx/rev/b7b7f3a0cc28 branches: changeset: 7031:b7b7f3a0cc28 user: Orgad Shaneh date: Tue Jun 06 19:37:34 2017 +0300 description: Configure: use .exe for binaries for all win32 compilers. diffstat: auto/cc/bcc | 1 - auto/cc/msvc | 1 - auto/cc/owc | 1 - auto/os/win32 | 1 + 4 files changed, 1 insertions(+), 3 deletions(-) diffs (44 lines): diff --git a/auto/cc/bcc b/auto/cc/bcc --- a/auto/cc/bcc +++ b/auto/cc/bcc @@ -62,7 +62,6 @@ ngx_include_opt="-I" ngx_objout="-o" ngx_binout="-e" ngx_objext="obj" -ngx_binext=".exe" ngx_long_start='@&&| ' diff --git a/auto/cc/msvc b/auto/cc/msvc --- a/auto/cc/msvc +++ b/auto/cc/msvc @@ -142,7 +142,6 @@ ngx_pic_opt= ngx_objout="-Fo" ngx_binout="-Fe" ngx_objext="obj" -ngx_binext=".exe" ngx_long_start='@<< ' diff --git a/auto/cc/owc b/auto/cc/owc --- a/auto/cc/owc +++ b/auto/cc/owc @@ -84,7 +84,6 @@ ngx_include_opt="-i=" ngx_objout="-fo" ngx_binout="-fe=" ngx_objext="obj" -ngx_binext=".exe" ngx_regex_dirsep='\\' ngx_dirsep="\\" diff --git a/auto/os/win32 b/auto/os/win32 --- a/auto/os/win32 +++ b/auto/os/win32 @@ -13,6 +13,7 @@ NGX_ICONS="$NGX_WIN32_ICONS" SELECT_SRCS=$WIN32_SELECT_SRCS ngx_pic_opt= +ngx_binext=".exe" case "$NGX_CC_NAME" in From xeioex at nginx.com Tue Jun 13 14:51:36 2017 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Tue, 13 Jun 2017 14:51:36 +0000 Subject: [njs] Passing value and default attributes to njs_object_prop_alloc(). Message-ID: details: http://hg.nginx.org/njs/rev/8b5f5dbcbfe7 branches: changeset: 363:8b5f5dbcbfe7 user: Dmitry Volyntsev date: Tue Jun 13 17:49:05 2017 +0300 description: Passing value and default attributes to njs_object_prop_alloc(). diffstat: njs/njs_object.c | 26 ++++++++++---------------- njs/njs_object.h | 3 ++- njs/njs_regexp.c | 6 ++---- njs/njs_vm.c | 2 +- 4 files changed, 15 insertions(+), 22 deletions(-) diffs (133 lines): diff -r 096d526744d5 -r 8b5f5dbcbfe7 njs/njs_object.c --- a/njs/njs_object.c Tue Jun 13 14:33:51 2017 +0300 +++ b/njs/njs_object.c Tue Jun 13 17:49:05 2017 +0300 @@ -181,7 +181,8 @@ njs_object_hash_test(nxt_lvlhsh_query_t njs_object_prop_t * -njs_object_prop_alloc(njs_vm_t *vm, const njs_value_t *name) +njs_object_prop_alloc(njs_vm_t *vm, const njs_value_t *name, + const njs_value_t *value, uint8_t attributes) { njs_object_prop_t *prop; @@ -189,15 +190,16 @@ njs_object_prop_alloc(njs_vm_t *vm, cons sizeof(njs_object_prop_t)); if (nxt_fast_path(prop != NULL)) { - prop->value = njs_value_void; + /* GC: retain. */ + prop->value = *value; /* GC: retain. */ prop->name = *name; prop->type = NJS_PROPERTY; - prop->enumerable = 1; - prop->writable = 1; - prop->configurable = 1; + prop->enumerable = attributes; + prop->writable = attributes; + prop->configurable = attributes; } return prop; @@ -494,16 +496,12 @@ njs_define_property(njs_vm_t *vm, njs_ob ret = nxt_lvlhsh_find(&object->hash, &lhq); if (ret != NXT_OK) { - prop = njs_object_prop_alloc(vm, name); + prop = njs_object_prop_alloc(vm, name, &njs_value_void, 0); if (nxt_slow_path(prop == NULL)) { return NXT_ERROR; } - prop->configurable = 0; - prop->enumerable = 0; - prop->writable = 0; - lhq.value = prop; } else { @@ -647,7 +645,7 @@ njs_property_prototype_create(njs_vm_t * static const njs_value_t prototype_string = njs_string("prototype"); - prop = njs_object_prop_alloc(vm, &prototype_string); + prop = njs_object_prop_alloc(vm, &prototype_string, &njs_value_void, 0); if (nxt_slow_path(prop == NULL)) { return NULL; } @@ -658,10 +656,6 @@ njs_property_prototype_create(njs_vm_t * prop->value.type = prototype->type; prop->value.data.truth = 1; - prop->enumerable = 0; - prop->writable = 0; - prop->configurable = 0; - lhq.value = prop; lhq.key_hash = NJS_PROTOTYPE_HASH; lhq.key = nxt_string_value("prototype"); @@ -835,7 +829,7 @@ njs_property_constructor_create(njs_vm_t static const njs_value_t constructor_string = njs_string("constructor"); - prop = njs_object_prop_alloc(vm, &constructor_string); + prop = njs_object_prop_alloc(vm, &constructor_string, constructor, 1); if (nxt_slow_path(prop == NULL)) { return NULL; } diff -r 096d526744d5 -r 8b5f5dbcbfe7 njs/njs_object.h --- a/njs/njs_object.h Tue Jun 13 14:33:51 2017 +0300 +++ b/njs/njs_object.h Tue Jun 13 17:49:05 2017 +0300 @@ -47,7 +47,8 @@ nxt_int_t njs_object_hash_create(njs_vm_ const njs_object_prop_t *prop, nxt_uint_t n); njs_ret_t njs_object_constructor(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, njs_index_t unused); -njs_object_prop_t *njs_object_prop_alloc(njs_vm_t *vm, const njs_value_t *name); +njs_object_prop_t *njs_object_prop_alloc(njs_vm_t *vm, const njs_value_t *name, + const njs_value_t *value, uint8_t attributes); njs_ret_t njs_primitive_prototype_get_proto(njs_vm_t *vm, njs_value_t *value); njs_ret_t njs_object_prototype_create(njs_vm_t *vm, njs_value_t *value); njs_value_t *njs_property_prototype_create(njs_vm_t *vm, nxt_lvlhsh_t *hash, diff -r 096d526744d5 -r 8b5f5dbcbfe7 njs/njs_regexp.c --- a/njs/njs_regexp.c Tue Jun 13 14:33:51 2017 +0300 +++ b/njs/njs_regexp.c Tue Jun 13 17:49:05 2017 +0300 @@ -749,7 +749,7 @@ njs_regexp_exec_result(njs_vm_t *vm, njs } } - prop = njs_object_prop_alloc(vm, &njs_string_index); + prop = njs_object_prop_alloc(vm, &njs_string_index, &njs_value_void, 1); if (nxt_slow_path(prop == NULL)) { goto fail; } @@ -774,13 +774,11 @@ njs_regexp_exec_result(njs_vm_t *vm, njs goto fail; } - prop = njs_object_prop_alloc(vm, &njs_string_input); + prop = njs_object_prop_alloc(vm, &njs_string_input, ®exp->string, 1); if (nxt_slow_path(prop == NULL)) { goto fail; } - njs_string_copy(&prop->value, ®exp->string); - lhq.key_hash = NJS_INPUT_HASH; lhq.key = nxt_string_value("input"); lhq.value = prop; diff -r 096d526744d5 -r 8b5f5dbcbfe7 njs/njs_vm.c --- a/njs/njs_vm.c Tue Jun 13 14:33:51 2017 +0300 +++ b/njs/njs_vm.c Tue Jun 13 17:49:05 2017 +0300 @@ -683,7 +683,7 @@ njs_vmcode_property_set(njs_vm_t *vm, nj break; case NXT_DECLINED: - prop = njs_object_prop_alloc(vm, &pq.value); + prop = njs_object_prop_alloc(vm, &pq.value, &njs_value_void, 1); if (nxt_slow_path(prop == NULL)) { return NXT_ERROR; } From orgads at gmail.com Tue Jun 13 14:52:24 2017 From: orgads at gmail.com (Orgad Shaneh) Date: Tue, 13 Jun 2017 17:52:24 +0300 Subject: [nginx] Configure: use .exe for binaries for all win32 compilers. In-Reply-To: References: Message-ID: On Tue, Jun 13, 2017 at 5:51 PM, Maxim Dounin wrote: > details: http://hg.nginx.org/nginx/rev/b7b7f3a0cc28 > branches: > changeset: 7031:b7b7f3a0cc28 > user: Orgad Shaneh > date: Tue Jun 06 19:37:34 2017 +0300 > description: > Configure: use .exe for binaries for all win32 compilers. > > diffstat: > > auto/cc/bcc | 1 - > auto/cc/msvc | 1 - > auto/cc/owc | 1 - > auto/os/win32 | 1 + > 4 files changed, 1 insertions(+), 3 deletions(-) > > diffs (44 lines): > > diff --git a/auto/cc/bcc b/auto/cc/bcc > --- a/auto/cc/bcc > +++ b/auto/cc/bcc > @@ -62,7 +62,6 @@ ngx_include_opt="-I" > ngx_objout="-o" > ngx_binout="-e" > ngx_objext="obj" > -ngx_binext=".exe" > > ngx_long_start='@&&| > ' > diff --git a/auto/cc/msvc b/auto/cc/msvc > --- a/auto/cc/msvc > +++ b/auto/cc/msvc > @@ -142,7 +142,6 @@ ngx_pic_opt= > ngx_objout="-Fo" > ngx_binout="-Fe" > ngx_objext="obj" > -ngx_binext=".exe" > > ngx_long_start='@<< > ' > diff --git a/auto/cc/owc b/auto/cc/owc > --- a/auto/cc/owc > +++ b/auto/cc/owc > @@ -84,7 +84,6 @@ ngx_include_opt="-i=" > ngx_objout="-fo" > ngx_binout="-fe=" > ngx_objext="obj" > -ngx_binext=".exe" > > ngx_regex_dirsep='\\' > ngx_dirsep="\\" > diff --git a/auto/os/win32 b/auto/os/win32 > --- a/auto/os/win32 > +++ b/auto/os/win32 > @@ -13,6 +13,7 @@ NGX_ICONS="$NGX_WIN32_ICONS" > SELECT_SRCS=$WIN32_SELECT_SRCS > > ngx_pic_opt= > +ngx_binext=".exe" > > case "$NGX_CC_NAME" in > > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel Thanks. I didn't get to it. - Orgad From xeioex at nginx.com Tue Jun 13 14:52:30 2017 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Tue, 13 Jun 2017 14:52:30 +0000 Subject: [njs] Object.getOwnPropertyDescriptor() method. Message-ID: details: http://hg.nginx.org/njs/rev/cf6b4a543eea branches: changeset: 364:cf6b4a543eea user: Dmitry Volyntsev date: Tue Jun 13 17:52:11 2017 +0300 description: Object.getOwnPropertyDescriptor() method. diffstat: njs/njs_object.c | 151 +++++++++++++++++++++++++++++++++++++++++++++++ njs/test/njs_unit_test.c | 33 ++++++++++ 2 files changed, 184 insertions(+), 0 deletions(-) diffs (211 lines): diff -r 8b5f5dbcbfe7 -r cf6b4a543eea njs/njs_object.c --- a/njs/njs_object.c Tue Jun 13 17:49:05 2017 +0300 +++ b/njs/njs_object.c Tue Jun 13 17:52:11 2017 +0300 @@ -557,6 +557,148 @@ njs_define_property(njs_vm_t *vm, njs_ob } +static const njs_value_t njs_object_value_string = njs_string("value"); +static const njs_value_t njs_object_configurable_string = + njs_string("configurable"); +static const njs_value_t njs_object_enumerable_string = + njs_string("enumerable"); +static const njs_value_t njs_object_writable_string = + njs_string("writable"); + + +static njs_ret_t +njs_object_get_own_property_descriptor(njs_vm_t *vm, njs_value_t *args, + nxt_uint_t nargs, njs_index_t unused) +{ + uint32_t index; + nxt_int_t ret; + njs_array_t *array; + njs_object_t *descriptor; + njs_object_prop_t *pr, *prop, array_prop; + const njs_value_t *value; + nxt_lvlhsh_query_t lhq; + + if (nargs < 3 || !njs_is_object(&args[1])) { + vm->exception = &njs_exception_type_error; + return NXT_ERROR; + } + + prop = NULL; + + if (njs_is_array(&args[1])) { + array = args[1].data.u.array; + index = njs_string_to_index(&args[2]); + + if (index < array->length && njs_is_valid(&array->start[index])) { + prop = &array_prop; + + array_prop.name = args[2]; + array_prop.value = array->start[index]; + + array_prop.configurable = 1; + array_prop.enumerable = 1; + array_prop.writable = 1; + } + } + + lhq.proto = &njs_object_hash_proto; + + if (prop == NULL) { + njs_string_get(&args[2], &lhq.key); + lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length); + + ret = nxt_lvlhsh_find(&args[1].data.u.object->hash, &lhq); + + if (ret != NXT_OK) { + vm->retval = njs_string_void; + return NXT_OK; + } + + prop = lhq.value; + } + + descriptor = njs_object_alloc(vm); + if (nxt_slow_path(descriptor == NULL)) { + return NXT_ERROR; + } + + lhq.replace = 0; + lhq.pool = vm->mem_cache_pool; + + lhq.key = nxt_string_value("value"); + lhq.key_hash = NJS_VALUE_HASH; + + pr = njs_object_prop_alloc(vm, &njs_object_value_string, &prop->value, 1); + if (nxt_slow_path(pr == NULL)) { + return NXT_ERROR; + } + + lhq.value = pr; + + ret = nxt_lvlhsh_insert(&descriptor->hash, &lhq); + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } + + lhq.key = nxt_string_value("configurable"); + lhq.key_hash = NJS_CONFIGURABLE_HASH; + + value = (prop->configurable == 1) ? &njs_string_true : &njs_string_false; + + pr = njs_object_prop_alloc(vm, &njs_object_configurable_string, value, 1); + if (nxt_slow_path(pr == NULL)) { + return NXT_ERROR; + } + + lhq.value = pr; + + ret = nxt_lvlhsh_insert(&descriptor->hash, &lhq); + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } + + lhq.key = nxt_string_value("enumerable"); + lhq.key_hash = NJS_ENUMERABLE_HASH; + + value = (prop->enumerable == 1) ? &njs_string_true : &njs_string_false; + + pr = njs_object_prop_alloc(vm, &njs_object_enumerable_string, value, 1); + if (nxt_slow_path(pr == NULL)) { + return NXT_ERROR; + } + + lhq.value = pr; + + ret = nxt_lvlhsh_insert(&descriptor->hash, &lhq); + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } + + lhq.key = nxt_string_value("writable"); + lhq.key_hash = NJS_WRITABABLE_HASH; + + value = (prop->writable == 1) ? &njs_string_true : &njs_string_false; + + pr = njs_object_prop_alloc(vm, &njs_object_writable_string, value, 1); + if (nxt_slow_path(pr == NULL)) { + return NXT_ERROR; + } + + lhq.value = pr; + + ret = nxt_lvlhsh_insert(&descriptor->hash, &lhq); + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } + + vm->retval.data.u.object = descriptor; + vm->retval.type = NJS_OBJECT; + vm->retval.data.truth = 1; + + return NXT_OK; +} + + static njs_ret_t njs_object_get_prototype_of(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, njs_index_t unused) @@ -732,6 +874,15 @@ static const njs_object_prop_t njs_obje NJS_OBJECT_ARG), }, + /* Object.getOwnPropertyDescriptor(). */ + { + .type = NJS_METHOD, + .name = njs_long_string("getOwnPropertyDescriptor"), + .value = njs_native_function(njs_object_get_own_property_descriptor, 0, + NJS_SKIP_ARG, NJS_OBJECT_ARG, + NJS_STRING_ARG), + }, + /* Object.getPrototypeOf(). */ { .type = NJS_METHOD, diff -r 8b5f5dbcbfe7 -r cf6b4a543eea njs/test/njs_unit_test.c --- a/njs/test/njs_unit_test.c Tue Jun 13 17:49:05 2017 +0300 +++ b/njs/test/njs_unit_test.c Tue Jun 13 17:52:11 2017 +0300 @@ -6038,6 +6038,39 @@ static njs_unit_test_t njs_test[] = "1..isPrototypeOf(p)"), nxt_string("false") }, + { nxt_string("Object.getOwnPropertyDescriptor({a:1}, 'a').value"), + nxt_string("1") }, + + { nxt_string("Object.getOwnPropertyDescriptor({a:1}, 'a').configurable"), + nxt_string("true") }, + + { nxt_string("Object.getOwnPropertyDescriptor({a:1}, 'a').enumerable"), + nxt_string("true") }, + + { nxt_string("Object.getOwnPropertyDescriptor({a:1}, 'a').writable"), + nxt_string("true") }, + + { nxt_string("Object.getOwnPropertyDescriptor({a:1}, 'b')"), + nxt_string("undefined") }, + + { nxt_string("Object.getOwnPropertyDescriptor({}, 'a')"), + nxt_string("undefined") }, + + { nxt_string("Object.getOwnPropertyDescriptor([3,4], '1').value"), + nxt_string("4") }, + + { nxt_string("Object.getOwnPropertyDescriptor([3,4], 1).value"), + nxt_string("4") }, + + { nxt_string("Object.getOwnPropertyDescriptor([3,4], '3')"), + nxt_string("undefined") }, + + { nxt_string("Object.getOwnPropertyDescriptor([], '0')"), + nxt_string("undefined") }, + + { nxt_string("Object.getOwnPropertyDescriptor(1, '0')"), + nxt_string("TypeError") }, + { nxt_string("var d = new Date(''); d +' '+ d.getTime()"), nxt_string("Invalid Date NaN") }, From mdounin at mdounin.ru Tue Jun 13 16:54:45 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Tue, 13 Jun 2017 19:54:45 +0300 Subject: PSK Support In-Reply-To: <145451D4E6785E4DA4DFA353A58CB7B2015E15DA0A@OLAWPA-EXMB04.ad.garmin.com> References: <145451D4E6785E4DA4DFA353A58CB7B2015E15B7E3@OLAWPA-EXMB04.ad.garmin.com> <20170605130916.GX55433@mdounin.ru> <145451D4E6785E4DA4DFA353A58CB7B2015E15C57F@OLAWPA-EXMB04.ad.garmin.com> <20170605183944.GB55433@mdounin.ru> <145451D4E6785E4DA4DFA353A58CB7B2015E15CE05@OLAWPA-EXMB04.ad.garmin.com> <20170607181327.GS55433@mdounin.ru> <145451D4E6785E4DA4DFA353A58CB7B2015E15DA0A@OLAWPA-EXMB04.ad.garmin.com> Message-ID: <20170613165445.GK55433@mdounin.ru> Hello! On Fri, Jun 09, 2017 at 03:40:15AM +0000, Karstens, Nate wrote: > Maxim, > > OK, we can skip the patch for turning off the certificate > warnings (and just use a dummy certificate) and just support a > single PSK file. > > The {HEX} prefix seems OK. I think it would also be good to > support an {ASC}. It is unlikely that anyone would have an > ASCII-based PSK that starts with {HEX}, but using {ASC} would > provide a way to make prevent that case. If somebody want to use a key which starts with {HEX}, an obvious solution would be to convert it to hex. Supporting an additional prefix for plain-text keys might be an option too (in auth_basic it is called {PLAIN}, see nginx.org/r/auth_basic_user_file), but I think that it would be good to interpret non-prefixed keys in a way compatible with stunnel. So there will be 3 options: identity:key identity:{PLAIN}key identity:{HEX}6b6579 > Also, instead of referring to text-based PSKs as ASCII, maybe > they should be UTF8-encoded and referred to as {TXT}? I would rather avoid saying anything about character encoding, much like nginx does in most of the other places. The {PLAIN} seems to be neutral enough. -- Maxim Dounin http://nginx.org/ From mdounin at mdounin.ru Tue Jun 13 21:53:02 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Wed, 14 Jun 2017 00:53:02 +0300 Subject: [PATCH] Proxy: split configured header names and values In-Reply-To: References: Message-ID: <20170613215302.GL55433@mdounin.ru> Hello! On Sat, Jun 03, 2017 at 08:04:00PM -0700, Piotr Sikora via nginx-devel wrote: > # HG changeset patch > # User Piotr Sikora > # Date 1489618535 25200 > # Wed Mar 15 15:55:35 2017 -0700 > # Node ID ff79d6887fc92d0344eac3e87339583265241e36 > # Parent 716852cce9136d977b81a2d1b8b6f9fbca0dce49 > Proxy: split configured header names and values. > > Previously, each configured header was represented in one of two ways, > depending on whether or not its value included any variables. > > If the value didn't include any variables, then it would be represented > as as a single script that contained complete header line with HTTP/1.1 > delimiters, i.e.: > > "Header: value\r\n" > > But if the value included any variables, then it would be represented > as a series of three scripts: first contained header name and the ":" > delimiter, second evaluated to header value, and third contained only Nitpicking: the ": " delimiter. > "\r\n", i.e.: > > "Header:" Same here, missed space. > "$value" > "\r\n" > > This commit changes that, so that each configured header is represented > as a series of two scripts: first contains only header name, and second > contains (or evaluates to) only header value, i.e.: > > "Header" > "$value" > > or > > "Header" > "value" > > This not only makes things more consistent, but also allows header name > and value to be accessed separately. > > Signed-off-by: Piotr Sikora Note that current code is written this way to minimize runtime processing costs: if we know a header contents in advance, we simply generate a static string with the header value. If contents of the header are not known, it is represented so it will be possible to test if it is empty or not at runtime. All additional syntax constructions, such as ": " and CRLFs, are included in the overall script so that required buffer size can be estimated simply by executing all the len scripts, and the complete request header can be generated with minimal additional processing. I'm not really object the change though, as current implementation looks overcomplicated, and I'm not really sure the optimization in question makes any real difference (and if it is, if the difference is positive). It would be interesting to do some benchmarking of the old and new variants. Below some comments about the code. Generic recommendation is to keep the code similar to the fastcgi/uwsgi/scgi modules. > > diff -r 716852cce913 -r ff79d6887fc9 src/http/modules/ngx_http_proxy_module.c > --- a/src/http/modules/ngx_http_proxy_module.c > +++ b/src/http/modules/ngx_http_proxy_module.c > @@ -1144,6 +1144,7 @@ static ngx_int_t > ngx_http_proxy_create_request(ngx_http_request_t *r) > { > size_t len, uri_len, loc_len, body_len; > + size_t key_len, val_len; > uintptr_t escape; > ngx_buf_t *b; > ngx_str_t method; > @@ -1258,10 +1259,17 @@ ngx_http_proxy_create_request(ngx_http_r > le.flushed = 1; > > while (*(uintptr_t *) le.ip) { > - while (*(uintptr_t *) le.ip) { > + lcode = *(ngx_http_script_len_code_pt *) le.ip; > + key_len = lcode(&le); > + > + for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) { > lcode = *(ngx_http_script_len_code_pt *) le.ip; > - len += lcode(&le); > } > + > + if (val_len) { > + len += key_len + sizeof(": ") - 1 + val_len + sizeof(CRLF) - 1; > + } > + > le.ip += sizeof(uintptr_t); > } > Re-shaping this cycle to look more like the one in other modules would be a plus, while (*(uintptr_t *) le.ip) { lcode = *(ngx_http_script_len_code_pt *) le.ip; key_len = lcode(&le); for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) { lcode = *(ngx_http_script_len_code_pt *) le.ip; } le.ip += sizeof(uintptr_t); if (val_len == 0) { continue; } len += key_len + sizeof(": ") - 1 + val_len + sizeof(CRLF) - 1; } > @@ -1363,28 +1371,32 @@ ngx_http_proxy_create_request(ngx_http_r > > while (*(uintptr_t *) le.ip) { > lcode = *(ngx_http_script_len_code_pt *) le.ip; > - > - /* skip the header line name length */ > (void) lcode(&le); > > - if (*(ngx_http_script_len_code_pt *) le.ip) { > - > - for (len = 0; *(uintptr_t *) le.ip; len += lcode(&le)) { > - lcode = *(ngx_http_script_len_code_pt *) le.ip; > - } > - > - e.skip = (len == sizeof(CRLF) - 1) ? 1 : 0; > - > - } else { > - e.skip = 0; > + for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) { > + lcode = *(ngx_http_script_len_code_pt *) le.ip; > } > > le.ip += sizeof(uintptr_t); > > + e.skip = (val_len == 0) ? 1 : 0; > + > + code = *(ngx_http_script_code_pt *) e.ip; > + code((ngx_http_script_engine_t *) &e); > + > + if (!e.skip) { > + *e.pos++ = ':'; *e.pos++ = ' '; > + } > + > while (*(uintptr_t *) e.ip) { > code = *(ngx_http_script_code_pt *) e.ip; > code((ngx_http_script_engine_t *) &e); > } > + > + if (!e.skip) { > + *e.pos++ = CR; *e.pos++ = LF; > + } > + > e.ip += sizeof(uintptr_t); > } Same here, while (*(uintptr_t *) le.ip) { lcode = *(ngx_http_script_len_code_pt *) le.ip; (void) lcode(&le); for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) { lcode = *(ngx_http_script_len_code_pt *) le.ip; } le.ip += sizeof(uintptr_t); if (val_len == 0) { e.skip = 1; while (*(uintptr_t *) e.ip) { code = *(ngx_http_script_code_pt *) e.ip; code((ngx_http_script_engine_t *) &e); } e.ip += sizeof(uintptr_t); e.skip = 0; continue; } code = *(ngx_http_script_code_pt *) e.ip; code((ngx_http_script_engine_t *) &e); *e.pos++ = ':'; *e.pos++ = ' '; while (*(uintptr_t *) e.ip) { code = *(ngx_http_script_code_pt *) e.ip; code((ngx_http_script_engine_t *) &e); } e.ip += sizeof(uintptr_t); *e.pos++ = CR; *e.pos++ = LF; } > > @@ -3498,6 +3510,30 @@ ngx_http_proxy_init_headers(ngx_conf_t * > continue; > } > > + copy = ngx_array_push_n(headers->lengths, > + sizeof(ngx_http_script_copy_code_t)); > + if (copy == NULL) { > + return NGX_ERROR; > + } > + > + copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code; > + copy->len = src[i].key.len; > + > + size = (sizeof(ngx_http_script_copy_code_t) > + + src[i].key.len + sizeof(uintptr_t) - 1) > + & ~(sizeof(uintptr_t) - 1); The last line is indented as if "&" operation is in parentheses. It would be better to use the same form as in the fastcgi module: size = (sizeof(ngx_http_script_copy_code_t) + src[i].key.len + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1); > + > + copy = ngx_array_push_n(headers->values, size); > + if (copy == NULL) { > + return NGX_ERROR; > + } > + > + copy->code = ngx_http_script_copy_code; > + copy->len = src[i].key.len; > + > + p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t); > + ngx_memcpy(p, src[i].key.data, src[i].key.len); > + > if (ngx_http_script_variables_count(&src[i].value) == 0) { > copy = ngx_array_push_n(headers->lengths, > sizeof(ngx_http_script_copy_code_t)); > @@ -3507,14 +3543,10 @@ ngx_http_proxy_init_headers(ngx_conf_t * > > copy->code = (ngx_http_script_code_pt) > ngx_http_script_copy_len_code; > - copy->len = src[i].key.len + sizeof(": ") - 1 > - + src[i].value.len + sizeof(CRLF) - 1; > - > + copy->len = src[i].value.len; > > size = (sizeof(ngx_http_script_copy_code_t) > - + src[i].key.len + sizeof(": ") - 1 > - + src[i].value.len + sizeof(CRLF) - 1 > - + sizeof(uintptr_t) - 1) > + + src[i].value.len + sizeof(uintptr_t) - 1) > & ~(sizeof(uintptr_t) - 1); > > copy = ngx_array_push_n(headers->values, size); > @@ -3523,45 +3555,12 @@ ngx_http_proxy_init_headers(ngx_conf_t * > } > > copy->code = ngx_http_script_copy_code; > - copy->len = src[i].key.len + sizeof(": ") - 1 > - + src[i].value.len + sizeof(CRLF) - 1; > + copy->len = src[i].value.len; > > p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t); > - > - p = ngx_cpymem(p, src[i].key.data, src[i].key.len); > - *p++ = ':'; *p++ = ' '; > - p = ngx_cpymem(p, src[i].value.data, src[i].value.len); > - *p++ = CR; *p = LF; > + ngx_memcpy(p, src[i].value.data, src[i].value.len); > It makes a little sense to preserve special processing for the "no variables" case here with . Just calling the ngx_http_script_compile() function as in the "else" clause will do the same. > } else { > - copy = ngx_array_push_n(headers->lengths, > - sizeof(ngx_http_script_copy_code_t)); > - if (copy == NULL) { > - return NGX_ERROR; > - } > - > - copy->code = (ngx_http_script_code_pt) > - ngx_http_script_copy_len_code; > - copy->len = src[i].key.len + sizeof(": ") - 1; > - > - > - size = (sizeof(ngx_http_script_copy_code_t) > - + src[i].key.len + sizeof(": ") - 1 + sizeof(uintptr_t) - 1) > - & ~(sizeof(uintptr_t) - 1); > - > - copy = ngx_array_push_n(headers->values, size); > - if (copy == NULL) { > - return NGX_ERROR; > - } > - > - copy->code = ngx_http_script_copy_code; > - copy->len = src[i].key.len + sizeof(": ") - 1; > - > - p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t); > - p = ngx_cpymem(p, src[i].key.data, src[i].key.len); > - *p++ = ':'; *p = ' '; > - > - > ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); > > sc.cf = cf; > @@ -3573,33 +3572,6 @@ ngx_http_proxy_init_headers(ngx_conf_t * > if (ngx_http_script_compile(&sc) != NGX_OK) { > return NGX_ERROR; > } > - > - > - copy = ngx_array_push_n(headers->lengths, > - sizeof(ngx_http_script_copy_code_t)); > - if (copy == NULL) { > - return NGX_ERROR; > - } > - > - copy->code = (ngx_http_script_code_pt) > - ngx_http_script_copy_len_code; > - copy->len = sizeof(CRLF) - 1; > - > - > - size = (sizeof(ngx_http_script_copy_code_t) > - + sizeof(CRLF) - 1 + sizeof(uintptr_t) - 1) > - & ~(sizeof(uintptr_t) - 1); > - > - copy = ngx_array_push_n(headers->values, size); > - if (copy == NULL) { > - return NGX_ERROR; > - } > - > - copy->code = ngx_http_script_copy_code; > - copy->len = sizeof(CRLF) - 1; > - > - p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t); > - *p++ = CR; *p = LF; > } > > code = ngx_array_push_n(headers->lengths, sizeof(uintptr_t)); Full patch with the above comments below: diff --git a/src/http/modules/ngx_http_proxy_module.c b/src/http/modules/ngx_http_proxy_module.c --- a/src/http/modules/ngx_http_proxy_module.c +++ b/src/http/modules/ngx_http_proxy_module.c @@ -1259,18 +1259,20 @@ ngx_http_proxy_create_request(ngx_http_r le.flushed = 1; while (*(uintptr_t *) le.ip) { + lcode = *(ngx_http_script_len_code_pt *) le.ip; key_len = lcode(&le); for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) { lcode = *(ngx_http_script_len_code_pt *) le.ip; } - - if (val_len) { - len += key_len + sizeof(": ") - 1 + val_len + sizeof(CRLF) - 1; + le.ip += sizeof(uintptr_t); + + if (val_len == 0) { + continue; } - le.ip += sizeof(uintptr_t); + len += key_len + sizeof(": ") - 1 + val_len + sizeof(CRLF) - 1; } @@ -1370,34 +1372,41 @@ ngx_http_proxy_create_request(ngx_http_r le.ip = headers->lengths->elts; while (*(uintptr_t *) le.ip) { + lcode = *(ngx_http_script_len_code_pt *) le.ip; (void) lcode(&le); for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) { lcode = *(ngx_http_script_len_code_pt *) le.ip; } - le.ip += sizeof(uintptr_t); - e.skip = (val_len == 0) ? 1 : 0; + if (val_len == 0) { + e.skip = 1; + + while (*(uintptr_t *) e.ip) { + code = *(ngx_http_script_code_pt *) e.ip; + code((ngx_http_script_engine_t *) &e); + } + e.ip += sizeof(uintptr_t); + + e.skip = 0; + + continue; + } code = *(ngx_http_script_code_pt *) e.ip; code((ngx_http_script_engine_t *) &e); - if (!e.skip) { - *e.pos++ = ':'; *e.pos++ = ' '; - } + *e.pos++ = ':'; *e.pos++ = ' '; while (*(uintptr_t *) e.ip) { code = *(ngx_http_script_code_pt *) e.ip; code((ngx_http_script_engine_t *) &e); } - - if (!e.skip) { - *e.pos++ = CR; *e.pos++ = LF; - } - e.ip += sizeof(uintptr_t); + + *e.pos++ = CR; *e.pos++ = LF; } b->last = e.pos; @@ -3521,7 +3530,7 @@ ngx_http_proxy_init_headers(ngx_conf_t * size = (sizeof(ngx_http_script_copy_code_t) + src[i].key.len + sizeof(uintptr_t) - 1) - & ~(sizeof(uintptr_t) - 1); + & ~(sizeof(uintptr_t) - 1); copy = ngx_array_push_n(headers->values, size); if (copy == NULL) { @@ -3534,44 +3543,16 @@ ngx_http_proxy_init_headers(ngx_conf_t * p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t); ngx_memcpy(p, src[i].key.data, src[i].key.len); - if (ngx_http_script_variables_count(&src[i].value) == 0) { - copy = ngx_array_push_n(headers->lengths, - sizeof(ngx_http_script_copy_code_t)); - if (copy == NULL) { - return NGX_ERROR; - } - - copy->code = (ngx_http_script_code_pt) - ngx_http_script_copy_len_code; - copy->len = src[i].value.len; - - size = (sizeof(ngx_http_script_copy_code_t) - + src[i].value.len + sizeof(uintptr_t) - 1) - & ~(sizeof(uintptr_t) - 1); - - copy = ngx_array_push_n(headers->values, size); - if (copy == NULL) { - return NGX_ERROR; - } - - copy->code = ngx_http_script_copy_code; - copy->len = src[i].value.len; - - p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t); - ngx_memcpy(p, src[i].value.data, src[i].value.len); - - } else { - ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); - - sc.cf = cf; - sc.source = &src[i].value; - sc.flushes = &headers->flushes; - sc.lengths = &headers->lengths; - sc.values = &headers->values; - - if (ngx_http_script_compile(&sc) != NGX_OK) { - return NGX_ERROR; - } + ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); + + sc.cf = cf; + sc.source = &src[i].value; + sc.flushes = &headers->flushes; + sc.lengths = &headers->lengths; + sc.values = &headers->values; + + if (ngx_http_script_compile(&sc) != NGX_OK) { + return NGX_ERROR; } code = ngx_array_push_n(headers->lengths, sizeof(uintptr_t)); -- Maxim Dounin http://nginx.org/ From Nate.Karstens at garmin.com Wed Jun 14 01:10:55 2017 From: Nate.Karstens at garmin.com (Karstens, Nate) Date: Wed, 14 Jun 2017 01:10:55 +0000 Subject: PSK Support In-Reply-To: <20170613165445.GK55433@mdounin.ru> References: <145451D4E6785E4DA4DFA353A58CB7B2015E15B7E3@OLAWPA-EXMB04.ad.garmin.com> <20170605130916.GX55433@mdounin.ru> <145451D4E6785E4DA4DFA353A58CB7B2015E15C57F@OLAWPA-EXMB04.ad.garmin.com> <20170605183944.GB55433@mdounin.ru> <145451D4E6785E4DA4DFA353A58CB7B2015E15CE05@OLAWPA-EXMB04.ad.garmin.com> <20170607181327.GS55433@mdounin.ru> <145451D4E6785E4DA4DFA353A58CB7B2015E15DA0A@OLAWPA-EXMB04.ad.garmin.com> <20170613165445.GK55433@mdounin.ru> Message-ID: <145451D4E6785E4DA4DFA353A58CB7B2015E16B5C3@OLAWPA-EXMB03.ad.garmin.com> OK, sounds good to me! I'll hopefully have some new patches available in a couple of days. Any thoughts on using regular expressions to validate the format of the password file and extract strings? Specifically, does any string matching have to use regular expressions (protected by NGX_PCRE), or is the use of regular expressions optional? Nate -----Original Message----- From: nginx-devel [mailto:nginx-devel-bounces at nginx.org] On Behalf Of Maxim Dounin Sent: Tuesday, June 13, 2017 9:55 AM To: nginx-devel at nginx.org Subject: Re: PSK Support Hello! On Fri, Jun 09, 2017 at 03:40:15AM +0000, Karstens, Nate wrote: > Maxim, > > OK, we can skip the patch for turning off the certificate warnings > (and just use a dummy certificate) and just support a single PSK file. > > The {HEX} prefix seems OK. I think it would also be good to support an > {ASC}. It is unlikely that anyone would have an ASCII-based PSK that > starts with {HEX}, but using {ASC} would provide a way to make prevent > that case. If somebody want to use a key which starts with {HEX}, an obvious solution would be to convert it to hex. Supporting an additional prefix for plain-text keys might be an option too (in auth_basic it is called {PLAIN}, see nginx.org/r/auth_basic_user_file), but I think that it would be good to interpret non-prefixed keys in a way compatible with stunnel. So there will be 3 options: identity:key identity:{PLAIN}key identity:{HEX}6b6579 > Also, instead of referring to text-based PSKs as ASCII, maybe they > should be UTF8-encoded and referred to as {TXT}? I would rather avoid saying anything about character encoding, much like nginx does in most of the other places. The {PLAIN} seems to be neutral enough. -- Maxim Dounin http://nginx.org/ _______________________________________________ nginx-devel mailing list nginx-devel at nginx.org http://mailman.nginx.org/mailman/listinfo/nginx-devel ________________________________ CONFIDENTIALITY NOTICE: This email and any attachments are for the sole use of the intended recipient(s) and contain information that may be Garmin confidential and/or Garmin legally privileged. If you have received this email in error, please notify the sender by reply email and delete the message. Any disclosure, copying, distribution or use of this communication (including attachments) by someone other than the intended recipient is prohibited. Thank you. From ru at nginx.com Wed Jun 14 09:34:07 2017 From: ru at nginx.com (Ruslan Ermilov) Date: Wed, 14 Jun 2017 09:34:07 +0000 Subject: [nginx] Removed excessive casts for ngx_file_info(). Message-ID: details: http://hg.nginx.org/nginx/rev/d48c8cdac201 branches: changeset: 7032:d48c8cdac201 user: Ruslan Ermilov date: Wed Jun 14 12:29:52 2017 +0300 description: Removed excessive casts for ngx_file_info(). diffstat: src/core/ngx_cycle.c | 4 +--- src/core/ngx_file.c | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diffs (28 lines): diff -r b7b7f3a0cc28 -r d48c8cdac201 src/core/ngx_cycle.c --- a/src/core/ngx_cycle.c Tue Jun 06 19:37:34 2017 +0300 +++ b/src/core/ngx_cycle.c Wed Jun 14 12:29:52 2017 +0300 @@ -1124,9 +1124,7 @@ ngx_reopen_files(ngx_cycle_t *cycle, ngx if (user != (ngx_uid_t) NGX_CONF_UNSET_UINT) { ngx_file_info_t fi; - if (ngx_file_info((const char *) file[i].name.data, &fi) - == NGX_FILE_ERROR) - { + if (ngx_file_info(file[i].name.data, &fi) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, ngx_file_info_n " \"%s\" failed", file[i].name.data); diff -r b7b7f3a0cc28 -r d48c8cdac201 src/core/ngx_file.c --- a/src/core/ngx_file.c Tue Jun 06 19:37:34 2017 +0300 +++ b/src/core/ngx_file.c Wed Jun 14 12:29:52 2017 +0300 @@ -622,9 +622,7 @@ ngx_create_paths(ngx_cycle_t *cycle, ngx { ngx_file_info_t fi; - if (ngx_file_info((const char *) path[i]->name.data, &fi) - == NGX_FILE_ERROR) - { + if (ngx_file_info(path[i]->name.data, &fi) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, ngx_file_info_n " \"%s\" failed", path[i]->name.data); return NGX_ERROR; From vbart at nginx.com Wed Jun 14 15:12:25 2017 From: vbart at nginx.com (Valentin V. Bartenev) Date: Wed, 14 Jun 2017 18:12:25 +0300 Subject: [PATCH 2 of 3] HTTP/2: added support for trailers in HTTP responses In-Reply-To: <73f67e06ab103e0368d1.1497356394@piotrsikora.sfo.corp.google.com> References: <07a5d26b49f04425ff54.1497356393@piotrsikora.sfo.corp.google.com> <73f67e06ab103e0368d1.1497356394@piotrsikora.sfo.corp.google.com> Message-ID: <1518974.YucKKet8tM@vbart-workstation> On Tuesday 13 June 2017 05:19:54 Piotr Sikora via nginx-devel wrote: > # HG changeset patch > # User Piotr Sikora > # Date 1490351854 25200 > # Fri Mar 24 03:37:34 2017 -0700 > # Node ID 73f67e06ab103e0368d1810c6f8cac5c70c4e246 > # Parent 07a5d26b49f04425ff54cc998f885aa987b7823f > HTTP/2: added support for trailers in HTTP responses. > > Signed-off-by: Piotr Sikora > [..] > + > + if (header[i].key.len > NGX_HTTP_V2_MAX_FIELD) { > + ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, > + "too long response trailer name: \"%V\"", > + &header[i].key); > + return NULL; > + } > + > + if (header[i].value.len > NGX_HTTP_V2_MAX_FIELD) { > + ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, > + "too long response trailer value: \"%V: %V\"", > + &header[i].key, &header[i].value); > + return NULL; > + } [..] I've overlooked this while doing previous review, but it looks strange. Why do you use NGX_LOG_WARN for trailers headers? It results in finalizing request with an error (in case of HTTP/2 it means RST_STREAM). For main headers the NGX_LOG_CRIT level is used. It looks too serious, but the WARN level is too low. It seems the right log level for both cases is NGX_LOG_ERR. So I'm going to commit the patch below: # HG changeset patch # User Valentin Bartenev # Date 1497453034 -10800 # Wed Jun 14 18:10:34 2017 +0300 # Node ID 460ed2f4e160bc6b0953456d1218381cdfc7e40b # Parent 3c55863e6887ee764265fd43a878b3f793e0a7b9 HTTP/2: adjusted log level for too long response headers. It's likely an upstream fault and the CRIT log level looks too high. diff -r 3c55863e6887 -r 460ed2f4e160 src/http/v2/ngx_http_v2_filter_module.c --- a/src/http/v2/ngx_http_v2_filter_module.c Tue Jun 13 17:01:08 2017 +0300 +++ b/src/http/v2/ngx_http_v2_filter_module.c Wed Jun 14 18:10:34 2017 +0300 @@ -385,14 +385,14 @@ ngx_http_v2_header_filter(ngx_http_reque } if (header[i].key.len > NGX_HTTP_V2_MAX_FIELD) { - ngx_log_error(NGX_LOG_CRIT, fc->log, 0, + ngx_log_error(NGX_LOG_ERR, fc->log, 0, "too long response header name: \"%V\"", &header[i].key); return NGX_ERROR; } if (header[i].value.len > NGX_HTTP_V2_MAX_FIELD) { - ngx_log_error(NGX_LOG_CRIT, fc->log, 0, + ngx_log_error(NGX_LOG_ERR, fc->log, 0, "too long response header value: \"%V: %V\"", &header[i].key, &header[i].value); return NGX_ERROR; From mdounin at mdounin.ru Wed Jun 14 16:54:47 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Wed, 14 Jun 2017 19:54:47 +0300 Subject: [PATCH 2 of 3] HTTP/2: added support for trailers in HTTP responses In-Reply-To: <1518974.YucKKet8tM@vbart-workstation> References: <07a5d26b49f04425ff54.1497356393@piotrsikora.sfo.corp.google.com> <73f67e06ab103e0368d1.1497356394@piotrsikora.sfo.corp.google.com> <1518974.YucKKet8tM@vbart-workstation> Message-ID: <20170614165446.GR55433@mdounin.ru> Hello! On Wed, Jun 14, 2017 at 06:12:25PM +0300, Valentin V. Bartenev wrote: > On Tuesday 13 June 2017 05:19:54 Piotr Sikora via nginx-devel wrote: > > # HG changeset patch > > # User Piotr Sikora > > # Date 1490351854 25200 > > # Fri Mar 24 03:37:34 2017 -0700 > > # Node ID 73f67e06ab103e0368d1810c6f8cac5c70c4e246 > > # Parent 07a5d26b49f04425ff54cc998f885aa987b7823f > > HTTP/2: added support for trailers in HTTP responses. > > > > Signed-off-by: Piotr Sikora > > > [..] > > + > > + if (header[i].key.len > NGX_HTTP_V2_MAX_FIELD) { > > + ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, > > + "too long response trailer name: \"%V\"", > > + &header[i].key); > > + return NULL; > > + } > > + > > + if (header[i].value.len > NGX_HTTP_V2_MAX_FIELD) { > > + ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, > > + "too long response trailer value: \"%V: %V\"", > > + &header[i].key, &header[i].value); > > + return NULL; > > + } > [..] > > I've overlooked this while doing previous review, but it looks strange. > > Why do you use NGX_LOG_WARN for trailers headers? It results in > finalizing request with an error (in case of HTTP/2 it means RST_STREAM). > > For main headers the NGX_LOG_CRIT level is used. It looks too serious, > but the WARN level is too low. > > It seems the right log level for both cases is NGX_LOG_ERR. > > So I'm going to commit the patch below: [...] Given that the limit is about 2 megabytes, I don't think that this can be triggered in practice by a backend. As such, NGX_LOG_CRIT looks logical, as the only practically possible reason for the test to fail is a bug somewhere. -- Maxim Dounin http://nginx.org/ From vbart at nginx.com Wed Jun 14 17:06:02 2017 From: vbart at nginx.com (Valentin V. Bartenev) Date: Wed, 14 Jun 2017 20:06:02 +0300 Subject: [PATCH 2 of 3] HTTP/2: added support for trailers in HTTP responses In-Reply-To: <20170614165446.GR55433@mdounin.ru> References: <07a5d26b49f04425ff54.1497356393@piotrsikora.sfo.corp.google.com> <1518974.YucKKet8tM@vbart-workstation> <20170614165446.GR55433@mdounin.ru> Message-ID: <8276593.jtoS58EG7g@vbart-workstation> On Wednesday 14 June 2017 19:54:47 Maxim Dounin wrote: > Hello! > > On Wed, Jun 14, 2017 at 06:12:25PM +0300, Valentin V. Bartenev wrote: > > > On Tuesday 13 June 2017 05:19:54 Piotr Sikora via nginx-devel wrote: > > > # HG changeset patch > > > # User Piotr Sikora > > > # Date 1490351854 25200 > > > # Fri Mar 24 03:37:34 2017 -0700 > > > # Node ID 73f67e06ab103e0368d1810c6f8cac5c70c4e246 > > > # Parent 07a5d26b49f04425ff54cc998f885aa987b7823f > > > HTTP/2: added support for trailers in HTTP responses. > > > > > > Signed-off-by: Piotr Sikora > > > > > [..] > > > + > > > + if (header[i].key.len > NGX_HTTP_V2_MAX_FIELD) { > > > + ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, > > > + "too long response trailer name: \"%V\"", > > > + &header[i].key); > > > + return NULL; > > > + } > > > + > > > + if (header[i].value.len > NGX_HTTP_V2_MAX_FIELD) { > > > + ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, > > > + "too long response trailer value: \"%V: %V\"", > > > + &header[i].key, &header[i].value); > > > + return NULL; > > > + } > > [..] > > > > I've overlooked this while doing previous review, but it looks strange. > > > > Why do you use NGX_LOG_WARN for trailers headers? It results in > > finalizing request with an error (in case of HTTP/2 it means RST_STREAM). > > > > For main headers the NGX_LOG_CRIT level is used. It looks too serious, > > but the WARN level is too low. > > > > It seems the right log level for both cases is NGX_LOG_ERR. > > > > So I'm going to commit the patch below: > > [...] > > Given that the limit is about 2 megabytes, I don't think that this > can be triggered in practice by a backend. As such, NGX_LOG_CRIT > looks logical, as the only practically possible reason for the > test to fail is a bug somewhere. > Fair enough, I agree. Anyway, that's should be the same for trailers (i.e. CRIT instead of WARN). wbr, Valentin V. Bartenev From mdounin at mdounin.ru Wed Jun 14 17:33:34 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Wed, 14 Jun 2017 20:33:34 +0300 Subject: [PATCH 2 of 3] HTTP/2: added support for trailers in HTTP responses In-Reply-To: <8276593.jtoS58EG7g@vbart-workstation> References: <07a5d26b49f04425ff54.1497356393@piotrsikora.sfo.corp.google.com> <1518974.YucKKet8tM@vbart-workstation> <20170614165446.GR55433@mdounin.ru> <8276593.jtoS58EG7g@vbart-workstation> Message-ID: <20170614173333.GS55433@mdounin.ru> Hello! On Wed, Jun 14, 2017 at 08:06:02PM +0300, Valentin V. Bartenev wrote: > On Wednesday 14 June 2017 19:54:47 Maxim Dounin wrote: > > Hello! > > > > On Wed, Jun 14, 2017 at 06:12:25PM +0300, Valentin V. Bartenev wrote: > > > > > On Tuesday 13 June 2017 05:19:54 Piotr Sikora via nginx-devel wrote: > > > > # HG changeset patch > > > > # User Piotr Sikora > > > > # Date 1490351854 25200 > > > > # Fri Mar 24 03:37:34 2017 -0700 > > > > # Node ID 73f67e06ab103e0368d1810c6f8cac5c70c4e246 > > > > # Parent 07a5d26b49f04425ff54cc998f885aa987b7823f > > > > HTTP/2: added support for trailers in HTTP responses. > > > > > > > > Signed-off-by: Piotr Sikora > > > > > > > [..] > > > > + > > > > + if (header[i].key.len > NGX_HTTP_V2_MAX_FIELD) { > > > > + ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, > > > > + "too long response trailer name: \"%V\"", > > > > + &header[i].key); > > > > + return NULL; > > > > + } > > > > + > > > > + if (header[i].value.len > NGX_HTTP_V2_MAX_FIELD) { > > > > + ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, > > > > + "too long response trailer value: \"%V: %V\"", > > > > + &header[i].key, &header[i].value); > > > > + return NULL; > > > > + } > > > [..] > > > > > > I've overlooked this while doing previous review, but it looks strange. > > > > > > Why do you use NGX_LOG_WARN for trailers headers? It results in > > > finalizing request with an error (in case of HTTP/2 it means RST_STREAM). > > > > > > For main headers the NGX_LOG_CRIT level is used. It looks too serious, > > > but the WARN level is too low. > > > > > > It seems the right log level for both cases is NGX_LOG_ERR. > > > > > > So I'm going to commit the patch below: > > > > [...] > > > > Given that the limit is about 2 megabytes, I don't think that this > > can be triggered in practice by a backend. As such, NGX_LOG_CRIT > > looks logical, as the only practically possible reason for the > > test to fail is a bug somewhere. > > > > Fair enough, I agree. > > Anyway, that's should be the same for trailers (i.e. CRIT instead of WARN). Sure. The other patches looks good to me, so I'm going to commit the series with the following additional change to this patch unless there are objections: --- a/src/http/v2/ngx_http_v2_filter_module.c +++ b/src/http/v2/ngx_http_v2_filter_module.c @@ -672,14 +672,14 @@ ngx_http_v2_create_trailers_frame(ngx_ht } if (header[i].key.len > NGX_HTTP_V2_MAX_FIELD) { - ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, + ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0, "too long response trailer name: \"%V\"", &header[i].key); return NULL; } if (header[i].value.len > NGX_HTTP_V2_MAX_FIELD) { - ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, + ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0, "too long response trailer value: \"%V: %V\"", &header[i].key, &header[i].value); return NULL; -- Maxim Dounin http://nginx.org/ From mdounin at mdounin.ru Wed Jun 14 19:00:47 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Wed, 14 Jun 2017 22:00:47 +0300 Subject: [PATCH 4 of 4] HTTP/2: reject HTTP/2 requests with connection-specific headers In-Reply-To: References: <10c3f4c37f96ef496eff.1497356385@piotrsikora.sfo.corp.google.com> Message-ID: <20170614190047.GU55433@mdounin.ru> Hello! On Tue, Jun 13, 2017 at 05:19:48AM -0700, Piotr Sikora via nginx-devel wrote: > # HG changeset patch > # User Piotr Sikora > # Date 1490516709 25200 > # Sun Mar 26 01:25:09 2017 -0700 > # Node ID e2abc3bc3fc12b788d2631d3c47215acdc4ebbe6 > # Parent 6263d68cb96042d8f8974a4a3945226227ce13b9 > HTTP/2: reject HTTP/2 requests with connection-specific headers. > > Signed-off-by: Piotr Sikora > > diff -r 6263d68cb960 -r e2abc3bc3fc1 src/http/ngx_http_request.c > --- a/src/http/ngx_http_request.c > +++ b/src/http/ngx_http_request.c > @@ -19,6 +19,8 @@ static ngx_int_t ngx_http_alloc_large_he > > static ngx_int_t ngx_http_process_header_line(ngx_http_request_t *r, > ngx_table_elt_t *h, ngx_uint_t offset); > +static ngx_int_t ngx_http_process_http1_header_line(ngx_http_request_t *r, > + ngx_table_elt_t *h, ngx_uint_t offset); > static ngx_int_t ngx_http_process_unique_header_line(ngx_http_request_t *r, > ngx_table_elt_t *h, ngx_uint_t offset); > static ngx_int_t ngx_http_process_multi_header_lines(ngx_http_request_t *r, > @@ -146,7 +148,7 @@ ngx_http_header_t ngx_http_headers_in[] > > { ngx_string("Upgrade"), > offsetof(ngx_http_headers_in_t, upgrade), > - ngx_http_process_header_line }, > + ngx_http_process_http1_header_line }, > > #if (NGX_HTTP_GZIP) > { ngx_string("Accept-Encoding"), > @@ -161,8 +163,13 @@ ngx_http_header_t ngx_http_headers_in[] > offsetof(ngx_http_headers_in_t, authorization), > ngx_http_process_unique_header_line }, > > - { ngx_string("Keep-Alive"), offsetof(ngx_http_headers_in_t, keep_alive), > - ngx_http_process_header_line }, > + { ngx_string("Keep-Alive"), > + offsetof(ngx_http_headers_in_t, keep_alive), > + ngx_http_process_http1_header_line }, > + > + { ngx_string("Proxy-Connection"), > + offsetof(ngx_http_headers_in_t, proxy_connection), > + ngx_http_process_http1_header_line }, I'm highly sceptical about the whole series in general, and this patch specifically. In particular, the "Proxy-Connection" header is not something even defined by any standard, and even in its non-standard [broken] meaning never expected to be used in connections to nginx. Not to mention that Proxy-Authorization, a standard-defined hop-by-hop (connection-specific in terms of HTTP/2) header, is not checked anywhere. Additionally, I really think that disabling upgrades is one of the big mistakes of HTTP/2. It would be much more logical to interpret a HTTP/2 stream as a connection to upgrade, and allow to multiplex arbitrary protocols via a single HTTP/2 connection. Unless there are practical reasons for these changes, I would rather reject the series. -- Maxim Dounin http://nginx.org/ From mdounin at mdounin.ru Wed Jun 14 19:16:07 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Wed, 14 Jun 2017 22:16:07 +0300 Subject: PSK Support In-Reply-To: <145451D4E6785E4DA4DFA353A58CB7B2015E16B5C3@OLAWPA-EXMB03.ad.garmin.com> References: <145451D4E6785E4DA4DFA353A58CB7B2015E15B7E3@OLAWPA-EXMB04.ad.garmin.com> <20170605130916.GX55433@mdounin.ru> <145451D4E6785E4DA4DFA353A58CB7B2015E15C57F@OLAWPA-EXMB04.ad.garmin.com> <20170605183944.GB55433@mdounin.ru> <145451D4E6785E4DA4DFA353A58CB7B2015E15CE05@OLAWPA-EXMB04.ad.garmin.com> <20170607181327.GS55433@mdounin.ru> <145451D4E6785E4DA4DFA353A58CB7B2015E15DA0A@OLAWPA-EXMB04.ad.garmin.com> <20170613165445.GK55433@mdounin.ru> <145451D4E6785E4DA4DFA353A58CB7B2015E16B5C3@OLAWPA-EXMB03.ad.garmin.com> Message-ID: <20170614191607.GW55433@mdounin.ru> Hello! On Wed, Jun 14, 2017 at 01:10:55AM +0000, Karstens, Nate wrote: > OK, sounds good to me! I'll hopefully have some new patches > available in a couple of days. > > Any thoughts on using regular expressions to validate the format > of the password file and extract strings? Specifically, does any > string matching have to use regular expressions (protected by > NGX_PCRE), or is the use of regular expressions optional? In general nginx doesn't use regular expressions internally, as usually there are better ways to validate things. Also, it is expected to work fine without regular expressions, so using regular expressions to validate anything means that the feature won't be available without pcre compiled in. If you are looking for ways to simplify the patch, consider only preserving plain-text secrets, and/or using Base64 instead of hex (as there is standard ngx_decode_base64() function available). -- Maxim Dounin http://nginx.org/ From ru at nginx.com Thu Jun 15 10:05:49 2017 From: ru at nginx.com (Ruslan Ermilov) Date: Thu, 15 Jun 2017 10:05:49 +0000 Subject: [nginx] Gzip: fixed style in $gzip_ratio variable handler. Message-ID: details: http://hg.nginx.org/nginx/rev/ab5117642647 branches: changeset: 7033:ab5117642647 user: Ruslan Ermilov date: Wed Jun 14 12:49:20 2017 +0300 description: Gzip: fixed style in $gzip_ratio variable handler. The current style in variable handlers returning NGX_OK is to either set v->not_found to 1, or to initialize the entire ngx_http_variable_value_t structure. In theory, always setting v->valid = 1 for NGX_OK would be useful, which would mean that the value was computed and is thus valid, including the special case of v->not_found = 1. But currently that's not the case and causes the (v->valid || v->not_found) check to access an uninitialized v->valid value, which is safe only because its value doesn't matter when v->not_found is set. diffstat: src/http/modules/ngx_http_gzip_filter_module.c | 8 ++++---- 1 files changed, 4 insertions(+), 4 deletions(-) diffs (25 lines): diff -r d48c8cdac201 -r ab5117642647 src/http/modules/ngx_http_gzip_filter_module.c --- a/src/http/modules/ngx_http_gzip_filter_module.c Wed Jun 14 12:29:52 2017 +0300 +++ b/src/http/modules/ngx_http_gzip_filter_module.c Wed Jun 14 12:49:20 2017 +0300 @@ -1084,10 +1084,6 @@ ngx_http_gzip_ratio_variable(ngx_http_re ngx_uint_t zint, zfrac; ngx_http_gzip_ctx_t *ctx; - v->valid = 1; - v->no_cacheable = 0; - v->not_found = 0; - ctx = ngx_http_get_module_ctx(r, ngx_http_gzip_filter_module); if (ctx == NULL || ctx->zout == 0) { @@ -1095,6 +1091,10 @@ ngx_http_gzip_ratio_variable(ngx_http_re return NGX_OK; } + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = ngx_pnalloc(r->pool, NGX_INT32_LEN + 3); if (v->data == NULL) { return NGX_ERROR; From mdounin at mdounin.ru Thu Jun 15 15:45:11 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Thu, 15 Jun 2017 15:45:11 +0000 Subject: [nginx] Added support for trailers in HTTP responses. Message-ID: details: http://hg.nginx.org/nginx/rev/1b068a4e82d8 branches: changeset: 7034:1b068a4e82d8 user: Piotr Sikora date: Fri Mar 24 03:37:34 2017 -0700 description: Added support for trailers in HTTP responses. Example: ngx_table_elt_t *h; h = ngx_list_push(&r->headers_out.trailers); if (h == NULL) { return NGX_ERROR; } ngx_str_set(&h->key, "Fun"); ngx_str_set(&h->value, "with trailers"); h->hash = ngx_hash_key_lc(h->key.data, h->key.len); The code above adds "Fun: with trailers" trailer to the response. Modules that want to emit trailers must set r->expect_trailers = 1 in header filter, otherwise they might not be emitted for HTTP/1.1 responses that aren't already chunked. This change also adds $sent_trailer_* variables. Signed-off-by: Piotr Sikora diffstat: src/http/modules/ngx_http_chunked_filter_module.c | 154 ++++++++++++++++++---- src/http/ngx_http_core_module.c | 7 + src/http/ngx_http_request.c | 8 + src/http/ngx_http_request.h | 2 + src/http/ngx_http_variables.c | 15 ++ 5 files changed, 158 insertions(+), 28 deletions(-) diffs (291 lines): diff --git a/src/http/modules/ngx_http_chunked_filter_module.c b/src/http/modules/ngx_http_chunked_filter_module.c --- a/src/http/modules/ngx_http_chunked_filter_module.c +++ b/src/http/modules/ngx_http_chunked_filter_module.c @@ -17,6 +17,8 @@ typedef struct { static ngx_int_t ngx_http_chunked_filter_init(ngx_conf_t *cf); +static ngx_chain_t *ngx_http_chunked_create_trailers(ngx_http_request_t *r, + ngx_http_chunked_filter_ctx_t *ctx); static ngx_http_module_t ngx_http_chunked_filter_module_ctx = { @@ -69,27 +71,29 @@ ngx_http_chunked_header_filter(ngx_http_ return ngx_http_next_header_filter(r); } - if (r->headers_out.content_length_n == -1) { - if (r->http_version < NGX_HTTP_VERSION_11) { - r->keepalive = 0; - - } else { - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - - if (clcf->chunked_transfer_encoding) { - r->chunked = 1; + if (r->headers_out.content_length_n == -1 + || r->expect_trailers) + { + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - ctx = ngx_pcalloc(r->pool, - sizeof(ngx_http_chunked_filter_ctx_t)); - if (ctx == NULL) { - return NGX_ERROR; - } + if (r->http_version >= NGX_HTTP_VERSION_11 + && clcf->chunked_transfer_encoding) + { + if (r->expect_trailers) { + ngx_http_clear_content_length(r); + } - ngx_http_set_ctx(r, ctx, ngx_http_chunked_filter_module); + r->chunked = 1; - } else { - r->keepalive = 0; + ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_chunked_filter_ctx_t)); + if (ctx == NULL) { + return NGX_ERROR; } + + ngx_http_set_ctx(r, ctx, ngx_http_chunked_filter_module); + + } else if (r->headers_out.content_length_n == -1) { + r->keepalive = 0; } } @@ -179,26 +183,17 @@ ngx_http_chunked_body_filter(ngx_http_re } if (cl->buf->last_buf) { - tl = ngx_chain_get_free_buf(r->pool, &ctx->free); + tl = ngx_http_chunked_create_trailers(r, ctx); if (tl == NULL) { return NGX_ERROR; } - b = tl->buf; - - b->tag = (ngx_buf_tag_t) &ngx_http_chunked_filter_module; - b->temporary = 0; - b->memory = 1; - b->last_buf = 1; - b->pos = (u_char *) CRLF "0" CRLF CRLF; - b->last = b->pos + 7; - cl->buf->last_buf = 0; *ll = tl; if (size == 0) { - b->pos += 2; + tl->buf->pos += 2; } } else if (size > 0) { @@ -230,6 +225,109 @@ ngx_http_chunked_body_filter(ngx_http_re } +static ngx_chain_t * +ngx_http_chunked_create_trailers(ngx_http_request_t *r, + ngx_http_chunked_filter_ctx_t *ctx) +{ + size_t len; + ngx_buf_t *b; + ngx_uint_t i; + ngx_chain_t *cl; + ngx_list_part_t *part; + ngx_table_elt_t *header; + + len = 0; + + part = &r->headers_out.trailers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].hash == 0) { + continue; + } + + len += header[i].key.len + sizeof(": ") - 1 + + header[i].value.len + sizeof(CRLF) - 1; + } + + cl = ngx_chain_get_free_buf(r->pool, &ctx->free); + if (cl == NULL) { + return NULL; + } + + b = cl->buf; + + b->tag = (ngx_buf_tag_t) &ngx_http_chunked_filter_module; + b->temporary = 0; + b->memory = 1; + b->last_buf = 1; + + if (len == 0) { + b->pos = (u_char *) CRLF "0" CRLF CRLF; + b->last = b->pos + sizeof(CRLF "0" CRLF CRLF) - 1; + return cl; + } + + len += sizeof(CRLF "0" CRLF CRLF) - 1; + + b->pos = ngx_palloc(r->pool, len); + if (b->pos == NULL) { + return NULL; + } + + b->last = b->pos; + + *b->last++ = CR; *b->last++ = LF; + *b->last++ = '0'; + *b->last++ = CR; *b->last++ = LF; + + part = &r->headers_out.trailers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].hash == 0) { + continue; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http trailer: \"%V: %V\"", + &header[i].key, &header[i].value); + + b->last = ngx_copy(b->last, header[i].key.data, header[i].key.len); + *b->last++ = ':'; *b->last++ = ' '; + + b->last = ngx_copy(b->last, header[i].value.data, header[i].value.len); + *b->last++ = CR; *b->last++ = LF; + } + + *b->last++ = CR; *b->last++ = LF; + + return cl; +} + + static ngx_int_t ngx_http_chunked_filter_init(ngx_conf_t *cf) { diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c --- a/src/http/ngx_http_core_module.c +++ b/src/http/ngx_http_core_module.c @@ -2485,6 +2485,13 @@ ngx_http_subrequest(ngx_http_request_t * return NGX_ERROR; } + if (ngx_list_init(&sr->headers_out.trailers, r->pool, 4, + sizeof(ngx_table_elt_t)) + != NGX_OK) + { + return NGX_ERROR; + } + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); sr->main_conf = cscf->ctx->main_conf; sr->srv_conf = cscf->ctx->srv_conf; diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -562,6 +562,14 @@ ngx_http_create_request(ngx_connection_t return NULL; } + if (ngx_list_init(&r->headers_out.trailers, r->pool, 4, + sizeof(ngx_table_elt_t)) + != NGX_OK) + { + ngx_destroy_pool(r->pool); + return NULL; + } + r->ctx = ngx_pcalloc(r->pool, sizeof(void *) * ngx_http_max_module); if (r->ctx == NULL) { ngx_destroy_pool(r->pool); diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h --- a/src/http/ngx_http_request.h +++ b/src/http/ngx_http_request.h @@ -252,6 +252,7 @@ typedef struct { typedef struct { ngx_list_t headers; + ngx_list_t trailers; ngx_uint_t status; ngx_str_t status_line; @@ -514,6 +515,7 @@ struct ngx_http_request_s { unsigned pipeline:1; unsigned chunked:1; unsigned header_only:1; + unsigned expect_trailers:1; unsigned keepalive:1; unsigned lingering_close:1; unsigned discard_body:1; diff --git a/src/http/ngx_http_variables.c b/src/http/ngx_http_variables.c --- a/src/http/ngx_http_variables.c +++ b/src/http/ngx_http_variables.c @@ -38,6 +38,8 @@ static ngx_int_t ngx_http_variable_unkno ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_unknown_header_out(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_unknown_trailer_out(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_request_line(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_cookie(ngx_http_request_t *r, @@ -365,6 +367,9 @@ static ngx_http_variable_t ngx_http_cor { ngx_string("sent_http_"), NULL, ngx_http_variable_unknown_header_out, 0, NGX_HTTP_VAR_PREFIX, 0 }, + { ngx_string("sent_trailer_"), NULL, ngx_http_variable_unknown_trailer_out, + 0, NGX_HTTP_VAR_PREFIX, 0 }, + { ngx_string("cookie_"), NULL, ngx_http_variable_cookie, 0, NGX_HTTP_VAR_PREFIX, 0 }, @@ -934,6 +939,16 @@ ngx_http_variable_unknown_header_out(ngx } +static ngx_int_t +ngx_http_variable_unknown_trailer_out(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + return ngx_http_variable_unknown_header(v, (ngx_str_t *) data, + &r->headers_out.trailers.part, + sizeof("sent_trailer_") - 1); +} + + ngx_int_t ngx_http_variable_unknown_header(ngx_http_variable_value_t *v, ngx_str_t *var, ngx_list_part_t *part, size_t prefix) From mdounin at mdounin.ru Thu Jun 15 15:45:13 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Thu, 15 Jun 2017 15:45:13 +0000 Subject: [nginx] HTTP/2: added support for trailers in HTTP responses. Message-ID: details: http://hg.nginx.org/nginx/rev/4e784e095a97 branches: changeset: 7035:4e784e095a97 user: Piotr Sikora date: Fri Mar 24 03:37:34 2017 -0700 description: HTTP/2: added support for trailers in HTTP responses. Signed-off-by: Piotr Sikora diffstat: src/http/v2/ngx_http_v2_filter_module.c | 166 +++++++++++++++++++++++++++++-- 1 files changed, 152 insertions(+), 14 deletions(-) diffs (248 lines): diff --git a/src/http/v2/ngx_http_v2_filter_module.c b/src/http/v2/ngx_http_v2_filter_module.c --- a/src/http/v2/ngx_http_v2_filter_module.c +++ b/src/http/v2/ngx_http_v2_filter_module.c @@ -50,13 +50,17 @@ #define NGX_HTTP_V2_SERVER_INDEX 54 #define NGX_HTTP_V2_VARY_INDEX 59 +#define NGX_HTTP_V2_NO_TRAILERS (ngx_http_v2_out_frame_t *) -1 + static u_char *ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len, u_char *tmp, ngx_uint_t lower); static u_char *ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix, ngx_uint_t value); static ngx_http_v2_out_frame_t *ngx_http_v2_create_headers_frame( - ngx_http_request_t *r, u_char *pos, u_char *end); + ngx_http_request_t *r, u_char *pos, u_char *end, ngx_uint_t fin); +static ngx_http_v2_out_frame_t *ngx_http_v2_create_trailers_frame( + ngx_http_request_t *r); static ngx_chain_t *ngx_http_v2_send_chain(ngx_connection_t *fc, ngx_chain_t *in, off_t limit); @@ -612,7 +616,7 @@ ngx_http_v2_header_filter(ngx_http_reque header[i].value.len, tmp); } - frame = ngx_http_v2_create_headers_frame(r, start, pos); + frame = ngx_http_v2_create_headers_frame(r, start, pos, r->header_only); if (frame == NULL) { return NGX_ERROR; } @@ -636,6 +640,118 @@ ngx_http_v2_header_filter(ngx_http_reque } +static ngx_http_v2_out_frame_t * +ngx_http_v2_create_trailers_frame(ngx_http_request_t *r) +{ + u_char *pos, *start, *tmp; + size_t len, tmp_len; + ngx_uint_t i; + ngx_list_part_t *part; + ngx_table_elt_t *header; + + len = 0; + tmp_len = 0; + + part = &r->headers_out.trailers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].hash == 0) { + continue; + } + + if (header[i].key.len > NGX_HTTP_V2_MAX_FIELD) { + ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0, + "too long response trailer name: \"%V\"", + &header[i].key); + return NULL; + } + + if (header[i].value.len > NGX_HTTP_V2_MAX_FIELD) { + ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0, + "too long response trailer value: \"%V: %V\"", + &header[i].key, &header[i].value); + return NULL; + } + + len += 1 + NGX_HTTP_V2_INT_OCTETS + header[i].key.len + + NGX_HTTP_V2_INT_OCTETS + header[i].value.len; + + if (header[i].key.len > tmp_len) { + tmp_len = header[i].key.len; + } + + if (header[i].value.len > tmp_len) { + tmp_len = header[i].value.len; + } + } + + if (len == 0) { + return NGX_HTTP_V2_NO_TRAILERS; + } + + tmp = ngx_palloc(r->pool, tmp_len); + pos = ngx_pnalloc(r->pool, len); + + if (pos == NULL || tmp == NULL) { + return NULL; + } + + start = pos; + + part = &r->headers_out.trailers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].hash == 0) { + continue; + } + +#if (NGX_DEBUG) + if (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP) { + ngx_strlow(tmp, header[i].key.data, header[i].key.len); + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http2 output trailer: \"%*s: %V\"", + header[i].key.len, tmp, &header[i].value); + } +#endif + + *pos++ = 0; + + pos = ngx_http_v2_write_name(pos, header[i].key.data, + header[i].key.len, tmp); + + pos = ngx_http_v2_write_value(pos, header[i].value.data, + header[i].value.len, tmp); + } + + return ngx_http_v2_create_headers_frame(r, start, pos, 1); +} + + static u_char * ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len, u_char *tmp, ngx_uint_t lower) @@ -686,7 +802,7 @@ ngx_http_v2_write_int(u_char *pos, ngx_u static ngx_http_v2_out_frame_t * ngx_http_v2_create_headers_frame(ngx_http_request_t *r, u_char *pos, - u_char *end) + u_char *end, ngx_uint_t fin) { u_char type, flags; size_t rest, frame_size; @@ -707,12 +823,12 @@ ngx_http_v2_create_headers_frame(ngx_htt frame->stream = stream; frame->length = rest; frame->blocked = 1; - frame->fin = r->header_only; + frame->fin = fin; ll = &frame->first; type = NGX_HTTP_V2_HEADERS_FRAME; - flags = r->header_only ? NGX_HTTP_V2_END_STREAM_FLAG : NGX_HTTP_V2_NO_FLAG; + flags = fin ? NGX_HTTP_V2_END_STREAM_FLAG : NGX_HTTP_V2_NO_FLAG; frame_size = stream->connection->frame_size; for ( ;; ) { @@ -776,7 +892,7 @@ ngx_http_v2_create_headers_frame(ngx_htt continue; } - b->last_buf = r->header_only; + b->last_buf = fin; cl->next = NULL; frame->last = cl; @@ -798,7 +914,7 @@ ngx_http_v2_send_chain(ngx_connection_t ngx_http_request_t *r; ngx_http_v2_stream_t *stream; ngx_http_v2_loc_conf_t *h2lcf; - ngx_http_v2_out_frame_t *frame; + ngx_http_v2_out_frame_t *frame, *trailers; ngx_http_v2_connection_t *h2c; r = fc->data; @@ -872,6 +988,8 @@ ngx_http_v2_send_chain(ngx_connection_t frame_size = (h2lcf->chunk_size < h2c->frame_size) ? h2lcf->chunk_size : h2c->frame_size; + trailers = NGX_HTTP_V2_NO_TRAILERS; + #if (NGX_SUPPRESS_WARN) cl = NULL; #endif @@ -934,19 +1052,39 @@ ngx_http_v2_send_chain(ngx_connection_t size -= rest; } - frame = ngx_http_v2_filter_get_data_frame(stream, frame_size, out, cl); - if (frame == NULL) { - return NGX_CHAIN_ERROR; + if (cl->buf->last_buf) { + trailers = ngx_http_v2_create_trailers_frame(r); + if (trailers == NULL) { + return NGX_CHAIN_ERROR; + } + + if (trailers != NGX_HTTP_V2_NO_TRAILERS) { + cl->buf->last_buf = 0; + } } - ngx_http_v2_queue_frame(h2c, frame); + if (frame_size || cl->buf->last_buf) { + frame = ngx_http_v2_filter_get_data_frame(stream, frame_size, + out, cl); + if (frame == NULL) { + return NGX_CHAIN_ERROR; + } - h2c->send_window -= frame_size; + ngx_http_v2_queue_frame(h2c, frame); - stream->send_window -= frame_size; - stream->queued++; + h2c->send_window -= frame_size; + + stream->send_window -= frame_size; + stream->queued++; + } if (in == NULL) { + + if (trailers != NGX_HTTP_V2_NO_TRAILERS) { + ngx_http_v2_queue_frame(h2c, trailers); + stream->queued++; + } + break; } From mdounin at mdounin.ru Thu Jun 15 15:45:16 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Thu, 15 Jun 2017 15:45:16 +0000 Subject: [nginx] Headers filter: added "add_trailer" directive. Message-ID: details: http://hg.nginx.org/nginx/rev/8666da1ecf33 branches: changeset: 7036:8666da1ecf33 user: Piotr Sikora date: Fri Mar 24 03:37:34 2017 -0700 description: Headers filter: added "add_trailer" directive. Trailers added using this directive are evaluated after response body is processed by output filters (but before it's written to the wire), so it's possible to use variables calculated from the response body as the trailer value. Signed-off-by: Piotr Sikora diffstat: src/http/modules/ngx_http_headers_filter_module.c | 166 ++++++++++++++++++--- 1 files changed, 143 insertions(+), 23 deletions(-) diffs (255 lines): diff --git a/src/http/modules/ngx_http_headers_filter_module.c b/src/http/modules/ngx_http_headers_filter_module.c --- a/src/http/modules/ngx_http_headers_filter_module.c +++ b/src/http/modules/ngx_http_headers_filter_module.c @@ -48,6 +48,7 @@ typedef struct { time_t expires_time; ngx_http_complex_value_t *expires_value; ngx_array_t *headers; + ngx_array_t *trailers; } ngx_http_headers_conf_t; @@ -105,7 +106,15 @@ static ngx_command_t ngx_http_headers_f |NGX_CONF_TAKE23, ngx_http_headers_add, NGX_HTTP_LOC_CONF_OFFSET, - 0, + offsetof(ngx_http_headers_conf_t, headers), + NULL }, + + { ngx_string("add_trailer"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF + |NGX_CONF_TAKE23, + ngx_http_headers_add, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_headers_conf_t, trailers), NULL }, ngx_null_command @@ -144,6 +153,7 @@ ngx_module_t ngx_http_headers_filter_mo static ngx_http_output_header_filter_pt ngx_http_next_header_filter; +static ngx_http_output_body_filter_pt ngx_http_next_body_filter; static ngx_int_t @@ -154,10 +164,15 @@ ngx_http_headers_filter(ngx_http_request ngx_http_header_val_t *h; ngx_http_headers_conf_t *conf; + if (r != r->main) { + return ngx_http_next_header_filter(r); + } + conf = ngx_http_get_module_loc_conf(r, ngx_http_headers_filter_module); - if ((conf->expires == NGX_HTTP_EXPIRES_OFF && conf->headers == NULL) - || r != r->main) + if (conf->expires == NGX_HTTP_EXPIRES_OFF + && conf->headers == NULL + && conf->trailers == NULL) { return ngx_http_next_header_filter(r); } @@ -206,11 +221,101 @@ ngx_http_headers_filter(ngx_http_request } } + if (conf->trailers) { + h = conf->trailers->elts; + for (i = 0; i < conf->trailers->nelts; i++) { + + if (!safe_status && !h[i].always) { + continue; + } + + r->expect_trailers = 1; + break; + } + } + return ngx_http_next_header_filter(r); } static ngx_int_t +ngx_http_trailers_filter(ngx_http_request_t *r, ngx_chain_t *in) +{ + ngx_str_t value; + ngx_uint_t i, safe_status; + ngx_chain_t *cl; + ngx_table_elt_t *t; + ngx_http_header_val_t *h; + ngx_http_headers_conf_t *conf; + + conf = ngx_http_get_module_loc_conf(r, ngx_http_headers_filter_module); + + if (in == NULL + || conf->trailers == NULL + || !r->expect_trailers + || r->header_only) + { + return ngx_http_next_body_filter(r, in); + } + + for (cl = in; cl; cl = cl->next) { + if (cl->buf->last_buf) { + break; + } + } + + if (cl == NULL) { + return ngx_http_next_body_filter(r, in); + } + + switch (r->headers_out.status) { + + case NGX_HTTP_OK: + case NGX_HTTP_CREATED: + case NGX_HTTP_NO_CONTENT: + case NGX_HTTP_PARTIAL_CONTENT: + case NGX_HTTP_MOVED_PERMANENTLY: + case NGX_HTTP_MOVED_TEMPORARILY: + case NGX_HTTP_SEE_OTHER: + case NGX_HTTP_NOT_MODIFIED: + case NGX_HTTP_TEMPORARY_REDIRECT: + case NGX_HTTP_PERMANENT_REDIRECT: + safe_status = 1; + break; + + default: + safe_status = 0; + break; + } + + h = conf->trailers->elts; + for (i = 0; i < conf->trailers->nelts; i++) { + + if (!safe_status && !h[i].always) { + continue; + } + + if (ngx_http_complex_value(r, &h[i].value, &value) != NGX_OK) { + return NGX_ERROR; + } + + if (value.len) { + t = ngx_list_push(&r->headers_out.trailers); + if (t == NULL) { + return NGX_ERROR; + } + + t->key = h[i].key; + t->value = value; + t->hash = 1; + } + } + + return ngx_http_next_body_filter(r, in); +} + + +static ngx_int_t ngx_http_set_expires(ngx_http_request_t *r, ngx_http_headers_conf_t *conf) { char *err; @@ -557,6 +662,7 @@ ngx_http_headers_create_conf(ngx_conf_t * set by ngx_pcalloc(): * * conf->headers = NULL; + * conf->trailers = NULL; * conf->expires_time = 0; * conf->expires_value = NULL; */ @@ -587,6 +693,10 @@ ngx_http_headers_merge_conf(ngx_conf_t * conf->headers = prev->headers; } + if (conf->trailers == NULL) { + conf->trailers = prev->trailers; + } + return NGX_CONF_OK; } @@ -597,6 +707,9 @@ ngx_http_headers_filter_init(ngx_conf_t ngx_http_next_header_filter = ngx_http_top_header_filter; ngx_http_top_header_filter = ngx_http_headers_filter; + ngx_http_next_body_filter = ngx_http_top_body_filter; + ngx_http_top_body_filter = ngx_http_trailers_filter; + return NGX_OK; } @@ -674,42 +787,49 @@ ngx_http_headers_add(ngx_conf_t *cf, ngx { ngx_http_headers_conf_t *hcf = conf; - ngx_str_t *value; - ngx_uint_t i; - ngx_http_header_val_t *hv; - ngx_http_set_header_t *set; - ngx_http_compile_complex_value_t ccv; + ngx_str_t *value; + ngx_uint_t i; + ngx_array_t **headers; + ngx_http_header_val_t *hv; + ngx_http_set_header_t *set; + ngx_http_compile_complex_value_t ccv; value = cf->args->elts; - if (hcf->headers == NULL) { - hcf->headers = ngx_array_create(cf->pool, 1, - sizeof(ngx_http_header_val_t)); - if (hcf->headers == NULL) { + headers = (ngx_array_t **) ((char *) hcf + cmd->offset); + + if (*headers == NULL) { + *headers = ngx_array_create(cf->pool, 1, + sizeof(ngx_http_header_val_t)); + if (*headers == NULL) { return NGX_CONF_ERROR; } } - hv = ngx_array_push(hcf->headers); + hv = ngx_array_push(*headers); if (hv == NULL) { return NGX_CONF_ERROR; } hv->key = value[1]; - hv->handler = ngx_http_add_header; + hv->handler = NULL; hv->offset = 0; hv->always = 0; - set = ngx_http_set_headers; - for (i = 0; set[i].name.len; i++) { - if (ngx_strcasecmp(value[1].data, set[i].name.data) != 0) { - continue; + if (headers == &hcf->headers) { + hv->handler = ngx_http_add_header; + + set = ngx_http_set_headers; + for (i = 0; set[i].name.len; i++) { + if (ngx_strcasecmp(value[1].data, set[i].name.data) != 0) { + continue; + } + + hv->offset = set[i].offset; + hv->handler = set[i].handler; + + break; } - - hv->offset = set[i].offset; - hv->handler = set[i].handler; - - break; } if (value[2].len == 0) { From mdounin at mdounin.ru Thu Jun 15 15:46:32 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Thu, 15 Jun 2017 18:46:32 +0300 Subject: [PATCH 2 of 3] HTTP/2: added support for trailers in HTTP responses In-Reply-To: <20170614173333.GS55433@mdounin.ru> References: <07a5d26b49f04425ff54.1497356393@piotrsikora.sfo.corp.google.com> <1518974.YucKKet8tM@vbart-workstation> <20170614165446.GR55433@mdounin.ru> <8276593.jtoS58EG7g@vbart-workstation> <20170614173333.GS55433@mdounin.ru> Message-ID: <20170615154632.GA55433@mdounin.ru> Hello! On Wed, Jun 14, 2017 at 08:33:34PM +0300, Maxim Dounin wrote: > Hello! > > On Wed, Jun 14, 2017 at 08:06:02PM +0300, Valentin V. Bartenev wrote: > > > On Wednesday 14 June 2017 19:54:47 Maxim Dounin wrote: > > > Hello! > > > > > > On Wed, Jun 14, 2017 at 06:12:25PM +0300, Valentin V. Bartenev wrote: > > > > > > > On Tuesday 13 June 2017 05:19:54 Piotr Sikora via nginx-devel wrote: > > > > > # HG changeset patch > > > > > # User Piotr Sikora > > > > > # Date 1490351854 25200 > > > > > # Fri Mar 24 03:37:34 2017 -0700 > > > > > # Node ID 73f67e06ab103e0368d1810c6f8cac5c70c4e246 > > > > > # Parent 07a5d26b49f04425ff54cc998f885aa987b7823f > > > > > HTTP/2: added support for trailers in HTTP responses. > > > > > > > > > > Signed-off-by: Piotr Sikora > > > > > > > > > [..] > > > > > + > > > > > + if (header[i].key.len > NGX_HTTP_V2_MAX_FIELD) { > > > > > + ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, > > > > > + "too long response trailer name: \"%V\"", > > > > > + &header[i].key); > > > > > + return NULL; > > > > > + } > > > > > + > > > > > + if (header[i].value.len > NGX_HTTP_V2_MAX_FIELD) { > > > > > + ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, > > > > > + "too long response trailer value: \"%V: %V\"", > > > > > + &header[i].key, &header[i].value); > > > > > + return NULL; > > > > > + } > > > > [..] > > > > > > > > I've overlooked this while doing previous review, but it looks strange. > > > > > > > > Why do you use NGX_LOG_WARN for trailers headers? It results in > > > > finalizing request with an error (in case of HTTP/2 it means RST_STREAM). > > > > > > > > For main headers the NGX_LOG_CRIT level is used. It looks too serious, > > > > but the WARN level is too low. > > > > > > > > It seems the right log level for both cases is NGX_LOG_ERR. > > > > > > > > So I'm going to commit the patch below: > > > > > > [...] > > > > > > Given that the limit is about 2 megabytes, I don't think that this > > > can be triggered in practice by a backend. As such, NGX_LOG_CRIT > > > looks logical, as the only practically possible reason for the > > > test to fail is a bug somewhere. > > > > > > > Fair enough, I agree. > > > > Anyway, that's should be the same for trailers (i.e. CRIT instead of WARN). > > Sure. > > The other patches looks good to me, so I'm going to commit the > series with the following additional change to this patch unless > there are objections: > > --- a/src/http/v2/ngx_http_v2_filter_module.c > +++ b/src/http/v2/ngx_http_v2_filter_module.c > @@ -672,14 +672,14 @@ ngx_http_v2_create_trailers_frame(ngx_ht > } > > if (header[i].key.len > NGX_HTTP_V2_MAX_FIELD) { > - ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, > + ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0, > "too long response trailer name: \"%V\"", > &header[i].key); > return NULL; > } > > if (header[i].value.len > NGX_HTTP_V2_MAX_FIELD) { > - ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, > + ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0, > "too long response trailer value: \"%V: %V\"", > &header[i].key, &header[i].value); > return NULL; Series committed with the above change. Thanks to all involved. -- Maxim Dounin http://nginx.org/ From ru at nginx.com Fri Jun 16 15:17:15 2017 From: ru at nginx.com (Ruslan Ermilov) Date: Fri, 16 Jun 2017 15:17:15 +0000 Subject: [nginx] Added memory barrier semantics to ngx_rwlock_unlock(). Message-ID: details: http://hg.nginx.org/nginx/rev/12efcdcb8a4b branches: changeset: 7037:12efcdcb8a4b user: Ruslan Ermilov date: Fri Jun 16 18:15:53 2017 +0300 description: Added memory barrier semantics to ngx_rwlock_unlock(). diffstat: src/core/ngx_rwlock.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diffs (12 lines): diff -r 8666da1ecf33 -r 12efcdcb8a4b src/core/ngx_rwlock.c --- a/src/core/ngx_rwlock.c Fri Mar 24 03:37:34 2017 -0700 +++ b/src/core/ngx_rwlock.c Fri Jun 16 18:15:53 2017 +0300 @@ -94,7 +94,7 @@ ngx_rwlock_unlock(ngx_atomic_t *lock) readers = *lock; if (readers == NGX_RWLOCK_WLOCK) { - *lock = 0; + (void) ngx_atomic_cmp_set(lock, NGX_RWLOCK_WLOCK, 0); return; } From ru at nginx.com Fri Jun 16 15:17:18 2017 From: ru at nginx.com (Ruslan Ermilov) Date: Fri, 16 Jun 2017 15:17:18 +0000 Subject: [nginx] Introduced ngx_rwlock_downgrade(). Message-ID: details: http://hg.nginx.org/nginx/rev/d1816a2696de branches: changeset: 7038:d1816a2696de user: Ruslan Ermilov date: Fri Jun 16 18:15:58 2017 +0300 description: Introduced ngx_rwlock_downgrade(). diffstat: src/core/ngx_rwlock.c | 9 +++++++++ src/core/ngx_rwlock.h | 1 + 2 files changed, 10 insertions(+), 0 deletions(-) diffs (30 lines): diff -r 12efcdcb8a4b -r d1816a2696de src/core/ngx_rwlock.c --- a/src/core/ngx_rwlock.c Fri Jun 16 18:15:53 2017 +0300 +++ b/src/core/ngx_rwlock.c Fri Jun 16 18:15:58 2017 +0300 @@ -109,6 +109,15 @@ ngx_rwlock_unlock(ngx_atomic_t *lock) } +void +ngx_rwlock_downgrade(ngx_atomic_t *lock) +{ + if (*lock == NGX_RWLOCK_WLOCK) { + *lock = 1; + } +} + + #else #if (NGX_HTTP_UPSTREAM_ZONE || NGX_STREAM_UPSTREAM_ZONE) diff -r 12efcdcb8a4b -r d1816a2696de src/core/ngx_rwlock.h --- a/src/core/ngx_rwlock.h Fri Jun 16 18:15:53 2017 +0300 +++ b/src/core/ngx_rwlock.h Fri Jun 16 18:15:58 2017 +0300 @@ -16,6 +16,7 @@ void ngx_rwlock_wlock(ngx_atomic_t *lock); void ngx_rwlock_rlock(ngx_atomic_t *lock); void ngx_rwlock_unlock(ngx_atomic_t *lock); +void ngx_rwlock_downgrade(ngx_atomic_t *lock); #endif /* _NGX_RWLOCK_H_INCLUDED_ */ From piotrsikora at google.com Sat Jun 17 20:48:33 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Sat, 17 Jun 2017 13:48:33 -0700 Subject: [PATCH 2 of 3] HTTP/2: added support for trailers in HTTP responses In-Reply-To: <1518974.YucKKet8tM@vbart-workstation> References: <07a5d26b49f04425ff54.1497356393@piotrsikora.sfo.corp.google.com> <73f67e06ab103e0368d1.1497356394@piotrsikora.sfo.corp.google.com> <1518974.YucKKet8tM@vbart-workstation> Message-ID: Hey Valentin, > I've overlooked this while doing previous review, but it looks strange. > > Why do you use NGX_LOG_WARN for trailers headers? It results in > finalizing request with an error (in case of HTTP/2 it means RST_STREAM). > > For main headers the NGX_LOG_CRIT level is used. It looks too serious, > but the WARN level is too low. Good catch, thanks! This was left-over from the initial version, which skipped too long trailers, instead of resetting stream. Best regards, Piotr Sikora From piotrsikora at google.com Sat Jun 17 20:48:45 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Sat, 17 Jun 2017 13:48:45 -0700 Subject: [PATCH 2 of 3] HTTP/2: added support for trailers in HTTP responses In-Reply-To: <20170615154632.GA55433@mdounin.ru> References: <07a5d26b49f04425ff54.1497356393@piotrsikora.sfo.corp.google.com> <1518974.YucKKet8tM@vbart-workstation> <20170614165446.GR55433@mdounin.ru> <8276593.jtoS58EG7g@vbart-workstation> <20170614173333.GS55433@mdounin.ru> <20170615154632.GA55433@mdounin.ru> Message-ID: Hey Maxim, > Series committed with the above change. > Thanks to all involved. Thanks! :) Best regards, Piotr Sikora From piotrsikora at google.com Sat Jun 17 20:57:38 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Sat, 17 Jun 2017 13:57:38 -0700 Subject: [PATCH 4 of 4] HTTP/2: reject HTTP/2 requests with connection-specific headers In-Reply-To: <20170614190047.GU55433@mdounin.ru> References: <10c3f4c37f96ef496eff.1497356385@piotrsikora.sfo.corp.google.com> <20170614190047.GU55433@mdounin.ru> Message-ID: Hey Maxim, > I'm highly sceptical about the whole series in general, and this > patch specifically. > > In particular, the "Proxy-Connection" header is not something even > defined by any standard, and even in its non-standard [broken] > meaning never expected to be used in connections to nginx. Not to > mention that Proxy-Authorization, a standard-defined hop-by-hop > (connection-specific in terms of HTTP/2) header, is not checked > anywhere. Proxy-Connection is mentioned (and discouraged) in RFC7230. > Additionally, I really think that disabling upgrades is one of the > big mistakes of HTTP/2. It would be much more logical to > interpret a HTTP/2 stream as a connection to upgrade, and allow to > multiplex arbitrary protocols via a single HTTP/2 connection. Unfortunately, I have to agree. > Unless there are practical reasons for these changes, I would > rather reject the series. The practical reason is that other implementations (e.g. nghttp2) reject requests with those headers, which leads to a weird behavior where NGINX accepts requests and proxies them to a HTTP/2 upstream which rejects them because they contain one of those headers. We could clear those headers in proxy module (I'm already doing that for most of the headers, anyway), but it feels like a workaround for broken clients. Having said that, I'm fine with dropping the whole patchset. Best regards, Piotr Sikora From piotrsikora at google.com Sat Jun 17 20:58:27 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Sat, 17 Jun 2017 13:58:27 -0700 Subject: [PATCH] Output chain: propagate flush and last_buf flags to send_chain() In-Reply-To: <20170608162956.GY55433@mdounin.ru> References: <2a48b9b6e67d91594c17.1496545447@piotrsikora.sfo.corp.google.com> <20170608162956.GY55433@mdounin.ru> Message-ID: Hey Maxim, > Note well that in HTTP/2-related code the special flag > c->need_last_buf is used to indicate that a (fake) connection > needs an information about last_buf, thus allowing HTTP/2 > c->send_chain() wrapper to add its own framing. If the goal is > the same, please consider using the same approach. Yes, it's exactly the same use case. I originally used c->need_last_buf, but then decided against it, since c->send_chain() is "pluggable" and therefore we shouldn't be assuming it's behavior, but I don't mind either way, as both solutions for work my use case. Best regards, Piotr Sikora From piotrsikora at google.com Sat Jun 17 20:59:44 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Sat, 17 Jun 2017 13:59:44 -0700 Subject: [PATCH] Output chain: propagate last_buf flag to c->send_chain() In-Reply-To: <20170608162956.GY55433@mdounin.ru> References: <20170608162956.GY55433@mdounin.ru> Message-ID: <3363bdf821c7110b5774.1497733184@piotrsikora.sfo.corp.google.com> # HG changeset patch # User Piotr Sikora # Date 1491708381 25200 # Sat Apr 08 20:26:21 2017 -0700 # Node ID 3363bdf821c7110b577437bd59c962653f3a144f # Parent d1816a2696de8c2faa1cd913a151e5f62a8620f3 Output chain: propagate last_buf flag to c->send_chain(). Signed-off-by: Piotr Sikora diff -r d1816a2696de -r 3363bdf821c7 src/core/ngx_output_chain.c --- a/src/core/ngx_output_chain.c +++ b/src/core/ngx_output_chain.c @@ -658,6 +658,7 @@ ngx_chain_writer(void *data, ngx_chain_t ngx_chain_writer_ctx_t *ctx = data; off_t size; + ngx_uint_t last; ngx_chain_t *cl, *ln, *chain; ngx_connection_t *c; @@ -689,9 +690,10 @@ ngx_chain_writer(void *data, ngx_chain_t size += ngx_buf_size(in->buf); - ngx_log_debug2(NGX_LOG_DEBUG_CORE, c->log, 0, - "chain writer buf fl:%d s:%uO", - in->buf->flush, ngx_buf_size(in->buf)); + ngx_log_debug3(NGX_LOG_DEBUG_CORE, c->log, 0, + "chain writer buf fl:%d l:%d s:%uO", + in->buf->flush, in->buf->last_buf, + ngx_buf_size(in->buf)); cl = ngx_alloc_chain_link(ctx->pool); if (cl == NULL) { @@ -707,6 +709,8 @@ ngx_chain_writer(void *data, ngx_chain_t ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0, "chain writer in: %p", ctx->out); + last = 0; + for (cl = ctx->out; cl; cl = cl->next) { #if 1 @@ -732,9 +736,16 @@ ngx_chain_writer(void *data, ngx_chain_t #endif size += ngx_buf_size(cl->buf); + + if (cl->buf->last_buf) { + last = 1; + } } - if (size == 0 && !c->buffered) { + if (size == 0 + && !c->buffered + && !(last && c->need_last_buf)) + { return NGX_OK; } From bartw at xs4all.nl Mon Jun 19 06:00:37 2017 From: bartw at xs4all.nl (Bart Warmerdam) Date: Mon, 19 Jun 2017 08:00:37 +0200 Subject: [PATCH] Avoid memory leak on failure to allocate name during resolving In-Reply-To: <47113258fb82bef901fde6d0f1e7f49a@xs4all.nl> References: <47113258fb82bef901fde6d0f1e7f49a@xs4all.nl> Message-ID: <6817b70e330867a728690a0db99668e7@xs4all.nl> # HG changeset patch # User Bart Warmerdam # Date 1497851445 -7200 # Mon Jun 19 07:50:45 2017 +0200 # Branch memleak_resolve_name # Node ID dd8c5ef0483cf0abe6f9f88b4bb9ba681aec7be4 # Parent d1816a2696de8c2faa1cd913a151e5f62a8620f3 Avoid leak on error allocating name diff -r d1816a2696de -r dd8c5ef0483c src/core/ngx_resolver.c --- a/src/core/ngx_resolver.c Fri Jun 16 18:15:58 2017 +0300 +++ b/src/core/ngx_resolver.c Mon Jun 19 07:50:45 2017 +0200 @@ -443,7 +443,7 @@ name.data = ngx_resolver_alloc(r, name.len); if (name.data == NULL) { - return NGX_ERROR; + goto resolve_error; } if (slen == ctx->service.len) { @@ -481,6 +481,7 @@ ngx_resolver_free(r, ctx->event); } +resolve_error: ngx_resolver_free(r, ctx); return NGX_ERROR; From bartw at xs4all.nl Mon Jun 19 06:08:38 2017 From: bartw at xs4all.nl (Bart Warmerdam) Date: Mon, 19 Jun 2017 08:08:38 +0200 Subject: [PATCH] Avoid using the result of i2d_SSL_SESSION when the session is invalid Message-ID: # HG changeset patch # User Bart Warmerdam # Date 1497852211 -7200 # Mon Jun 19 08:03:31 2017 +0200 # Branch i2d_ssl_session_length # Node ID 079afb2cb4be3ef06d07e96d1a54cc359b971631 # Parent d1816a2696de8c2faa1cd913a151e5f62a8620f3 Make sure to also take into account the 'return 0' response of i2d_SSL_SESSION, which is possible when the session is not valid diff -r d1816a2696de -r 079afb2cb4be src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c Fri Jun 16 18:15:58 2017 +0300 +++ b/src/event/ngx_event_openssl.c Mon Jun 19 08:03:31 2017 +0200 @@ -2458,9 +2458,9 @@ len = i2d_SSL_SESSION(sess, NULL); - /* do not cache too big session */ - - if (len > (int) NGX_SSL_MAX_SESSION_SIZE) { + /* do not cache too big or invalid session */ + + if (len > (int) NGX_SSL_MAX_SESSION_SIZE || len < 1) { return 0; } diff -r d1816a2696de -r 079afb2cb4be src/http/ngx_http_upstream_round_robin.c --- a/src/http/ngx_http_upstream_round_robin.c Fri Jun 16 18:15:58 2017 +0300 +++ b/src/http/ngx_http_upstream_round_robin.c Mon Jun 19 08:03:31 2017 +0200 @@ -755,9 +755,9 @@ len = i2d_SSL_SESSION(ssl_session, NULL); - /* do not cache too big session */ + /* do not cache too big or invalid session */ - if (len > NGX_SSL_MAX_SESSION_SIZE) { + if (len > NGX_SSL_MAX_SESSION_SIZE || len < 1) { return; } diff -r d1816a2696de -r 079afb2cb4be src/stream/ngx_stream_upstream_round_robin.c --- a/src/stream/ngx_stream_upstream_round_robin.c Fri Jun 16 18:15:58 2017 +0300 +++ b/src/stream/ngx_stream_upstream_round_robin.c Mon Jun 19 08:03:31 2017 +0200 @@ -787,9 +787,9 @@ len = i2d_SSL_SESSION(ssl_session, NULL); - /* do not cache too big session */ + /* do not cache too big or invalid session */ - if (len > NGX_SSL_MAX_SESSION_SIZE) { + if (len > NGX_SSL_MAX_SESSION_SIZE || len < 1) { return; } From ru at nginx.com Mon Jun 19 10:10:04 2017 From: ru at nginx.com (Ruslan Ermilov) Date: Mon, 19 Jun 2017 13:10:04 +0300 Subject: [PATCH] Avoid using the result of i2d_SSL_SESSION when the session is invalid In-Reply-To: References: Message-ID: <20170619101004.GA835@lo0.su> On Mon, Jun 19, 2017 at 08:08:38AM +0200, Bart Warmerdam wrote: > # HG changeset patch > # User Bart Warmerdam > # Date 1497852211 -7200 > # Mon Jun 19 08:03:31 2017 +0200 > # Branch i2d_ssl_session_length > # Node ID 079afb2cb4be3ef06d07e96d1a54cc359b971631 > # Parent d1816a2696de8c2faa1cd913a151e5f62a8620f3 > Make sure to also take into account the 'return 0' response of > i2d_SSL_SESSION, which is possible when the session is not valid A case of invalid session is already caught by checking the return value of SSL_get0_session() just prior to calling i2d_SSL_SESSION() in ngx_http_upstream_save_round_robin_peer_session() and ngx_stream_upstream_save_round_robin_peer_session(). The ngx_ssl_new_session() function is passed as an argument to SSL_CTX_sess_set_new_cb() that "sets the callback function, which is automatically called whenever a new session was negotiated." Do you think that it's possible for a session to be invalid in either of these cases? > diff -r d1816a2696de -r 079afb2cb4be src/event/ngx_event_openssl.c > --- a/src/event/ngx_event_openssl.c Fri Jun 16 18:15:58 2017 +0300 > +++ b/src/event/ngx_event_openssl.c Mon Jun 19 08:03:31 2017 +0200 > @@ -2458,9 +2458,9 @@ > > len = i2d_SSL_SESSION(sess, NULL); > > - /* do not cache too big session */ > - > - if (len > (int) NGX_SSL_MAX_SESSION_SIZE) { > + /* do not cache too big or invalid session */ > + > + if (len > (int) NGX_SSL_MAX_SESSION_SIZE || len < 1) { > return 0; > } > > diff -r d1816a2696de -r 079afb2cb4be > src/http/ngx_http_upstream_round_robin.c > --- a/src/http/ngx_http_upstream_round_robin.c Fri Jun 16 18:15:58 2017 > +0300 > +++ b/src/http/ngx_http_upstream_round_robin.c Mon Jun 19 08:03:31 2017 > +0200 > @@ -755,9 +755,9 @@ > > len = i2d_SSL_SESSION(ssl_session, NULL); > > - /* do not cache too big session */ > + /* do not cache too big or invalid session */ > > - if (len > NGX_SSL_MAX_SESSION_SIZE) { > + if (len > NGX_SSL_MAX_SESSION_SIZE || len < 1) { > return; > } > > diff -r d1816a2696de -r 079afb2cb4be > src/stream/ngx_stream_upstream_round_robin.c > --- a/src/stream/ngx_stream_upstream_round_robin.c Fri Jun 16 > 18:15:58 2017 +0300 > +++ b/src/stream/ngx_stream_upstream_round_robin.c Mon Jun 19 > 08:03:31 2017 +0200 > @@ -787,9 +787,9 @@ > > len = i2d_SSL_SESSION(ssl_session, NULL); > > - /* do not cache too big session */ > + /* do not cache too big or invalid session */ > > - if (len > NGX_SSL_MAX_SESSION_SIZE) { > + if (len > NGX_SSL_MAX_SESSION_SIZE || len < 1) { > return; > } > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel > -- Ruslan Ermilov Join us at nginx.conf, Sep. 6-8, Portland, OR https://www.nginx.com/nginxconf From arut at nginx.com Mon Jun 19 11:30:50 2017 From: arut at nginx.com (Roman Arutyunyan) Date: Mon, 19 Jun 2017 11:30:50 +0000 Subject: [nginx] Resolver: fixed allocation error handling while resolving SRV. Message-ID: details: http://hg.nginx.org/nginx/rev/a39bc74873fa branches: changeset: 7039:a39bc74873fa user: Bart Warmerdam date: Mon Jun 19 14:25:42 2017 +0300 description: Resolver: fixed allocation error handling while resolving SRV. diffstat: src/core/ngx_resolver.c | 4 +++- 1 files changed, 3 insertions(+), 1 deletions(-) diffs (21 lines): diff -r d1816a2696de -r a39bc74873fa src/core/ngx_resolver.c --- a/src/core/ngx_resolver.c Fri Jun 16 18:15:58 2017 +0300 +++ b/src/core/ngx_resolver.c Mon Jun 19 14:25:42 2017 +0300 @@ -443,7 +443,7 @@ ngx_resolve_name(ngx_resolver_ctx_t *ctx name.data = ngx_resolver_alloc(r, name.len); if (name.data == NULL) { - return NGX_ERROR; + goto failed; } if (slen == ctx->service.len) { @@ -481,6 +481,8 @@ ngx_resolve_name(ngx_resolver_ctx_t *ctx ngx_resolver_free(r, ctx->event); } +failed: + ngx_resolver_free(r, ctx); return NGX_ERROR; From arut at nginx.com Mon Jun 19 11:32:41 2017 From: arut at nginx.com (Roman Arutyunyan) Date: Mon, 19 Jun 2017 14:32:41 +0300 Subject: [PATCH] Avoid memory leak on failure to allocate name during resolving In-Reply-To: <6817b70e330867a728690a0db99668e7@xs4all.nl> References: <47113258fb82bef901fde6d0f1e7f49a@xs4all.nl> <6817b70e330867a728690a0db99668e7@xs4all.nl> Message-ID: <20170619113241.GB470@Romans-MacBook-Air.local> Hello Bart, On Mon, Jun 19, 2017 at 08:00:37AM +0200, Bart Warmerdam wrote: > > # HG changeset patch > # User Bart Warmerdam > # Date 1497851445 -7200 > # Mon Jun 19 07:50:45 2017 +0200 > # Branch memleak_resolve_name > # Node ID dd8c5ef0483cf0abe6f9f88b4bb9ba681aec7be4 > # Parent d1816a2696de8c2faa1cd913a151e5f62a8620f3 > Avoid leak on error allocating name > > diff -r d1816a2696de -r dd8c5ef0483c src/core/ngx_resolver.c > --- a/src/core/ngx_resolver.c Fri Jun 16 18:15:58 2017 +0300 > +++ b/src/core/ngx_resolver.c Mon Jun 19 07:50:45 2017 +0200 > @@ -443,7 +443,7 @@ > > name.data = ngx_resolver_alloc(r, name.len); > if (name.data == NULL) { > - return NGX_ERROR; > + goto resolve_error; > } > > if (slen == ctx->service.len) { > @@ -481,6 +481,7 @@ > ngx_resolver_free(r, ctx->event); > } > > +resolve_error: > ngx_resolver_free(r, ctx); > > return NGX_ERROR; Thanks! Committed with minor changes. -- Roman Arutyunyan From xeioex at nginx.com Mon Jun 19 11:49:28 2017 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Mon, 19 Jun 2017 11:49:28 +0000 Subject: [njs] Using njs_string_get() where appropriate. Message-ID: details: http://hg.nginx.org/njs/rev/7ed74a2e4c50 branches: changeset: 365:7ed74a2e4c50 user: Dmitry Volyntsev date: Wed Jun 14 17:58:10 2017 +0300 description: Using njs_string_get() where appropriate. diffstat: njs/njs_object.c | 11 +---------- njs/njs_vm.c | 11 +---------- njs/njs_vm.h | 4 ++-- 3 files changed, 4 insertions(+), 22 deletions(-) diffs (59 lines): diff -r cf6b4a543eea -r 7ed74a2e4c50 njs/njs_object.c --- a/njs/njs_object.c Tue Jun 13 17:52:11 2017 +0300 +++ b/njs/njs_object.c Wed Jun 14 17:58:10 2017 +0300 @@ -109,16 +109,7 @@ njs_object_hash_create(njs_vm_t *vm, nxt lhq.pool = vm->mem_cache_pool; do { - lhq.key.length = prop->name.short_string.size; - - if (lhq.key.length != NJS_STRING_LONG) { - lhq.key.start = (u_char *) prop->name.short_string.start; - - } else { - lhq.key.length = prop->name.data.string_size; - lhq.key.start = prop->name.data.u.string->start; - } - + njs_string_get(&prop->name, &lhq.key); lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length); lhq.value = (void *) prop; diff -r cf6b4a543eea -r 7ed74a2e4c50 njs/njs_vm.c --- a/njs/njs_vm.c Tue Jun 13 17:52:11 2017 +0300 +++ b/njs/njs_vm.c Wed Jun 14 17:58:10 2017 +0300 @@ -1028,16 +1028,7 @@ njs_property_query(njs_vm_t *vm, njs_pro if (nxt_fast_path(ret == NXT_OK)) { - pq->lhq.key.length = pq->value.short_string.size; - - if (pq->lhq.key.length != NJS_STRING_LONG) { - pq->lhq.key.start = pq->value.short_string.start; - - } else { - pq->lhq.key.length = pq->value.data.string_size; - pq->lhq.key.start = pq->value.data.u.string->start; - } - + njs_string_get(&pq->value, &pq->lhq.key); pq->lhq.key_hash = hash(pq->lhq.key.start, pq->lhq.key.length); if (obj == NULL) { diff -r cf6b4a543eea -r 7ed74a2e4c50 njs/njs_vm.h --- a/njs/njs_vm.h Tue Jun 13 17:52:11 2017 +0300 +++ b/njs/njs_vm.h Wed Jun 14 17:58:10 2017 +0300 @@ -412,11 +412,11 @@ typedef njs_ret_t (*njs_vmcode_operation do { \ if ((value)->short_string.size != NJS_STRING_LONG) { \ (str)->length = (value)->short_string.size; \ - (str)->start = (value)->short_string.start; \ + (str)->start = (u_char *) (value)->short_string.start; \ \ } else { \ (str)->length = (value)->data.string_size; \ - (str)->start = (value)->data.u.string->start; \ + (str)->start = (u_char *) (value)->data.u.string->start; \ } \ } while (0) From xeioex at nginx.com Mon Jun 19 11:49:29 2017 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Mon, 19 Jun 2017 11:49:29 +0000 Subject: [njs] Object.freeze() method. Message-ID: details: http://hg.nginx.org/njs/rev/824fbb7fcd35 branches: changeset: 366:824fbb7fcd35 user: Dmitry Volyntsev date: Mon Jun 19 14:39:56 2017 +0300 description: Object.freeze() method. diffstat: njs/njs_array.c | 3 +- njs/njs_builtin.c | 2 + njs/njs_date.c | 1 + njs/njs_function.c | 2 + njs/njs_object.c | 58 +++++++++++++++++++++++++++++ njs/njs_regexp.c | 1 + njs/njs_vm.c | 5 ++ njs/njs_vm.h | 7 ++- njs/test/njs_unit_test.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 169 insertions(+), 3 deletions(-) diffs (332 lines): diff -r 7ed74a2e4c50 -r 824fbb7fcd35 njs/njs_array.c --- a/njs/njs_array.c Wed Jun 14 17:58:10 2017 +0300 +++ b/njs/njs_array.c Mon Jun 19 14:39:56 2017 +0300 @@ -149,6 +149,7 @@ njs_array_alloc(njs_vm_t *vm, uint32_t l array->object.__proto__ = &vm->prototypes[NJS_PROTOTYPE_ARRAY].object; array->object.type = NJS_ARRAY; array->object.shared = 0; + array->object.extensible = 1; array->size = size; array->length = length; @@ -1941,7 +1942,7 @@ njs_array_string_sort(njs_vm_t *vm, njs_ static const njs_function_t njs_array_string_sort_function = { - .object.shared = 1, + .object = { .type = NJS_FUNCTION, .shared = 1, .extensible = 1 }, .native = 1, .continuation_size = NJS_CONTINUATION_SIZE, .args_types = { NJS_SKIP_ARG, NJS_STRING_ARG, NJS_STRING_ARG }, diff -r 7ed74a2e4c50 -r 824fbb7fcd35 njs/njs_builtin.c --- a/njs/njs_builtin.c Wed Jun 14 17:58:10 2017 +0300 +++ b/njs/njs_builtin.c Mon Jun 19 14:39:56 2017 +0300 @@ -206,6 +206,7 @@ njs_builtin_objects_create(njs_vm_t *vm) } functions[i].object.shared = 1; + functions[i].object.extensible = 1; functions[i].native = 1; functions[i].args_offset = 1; functions[i].u.native = native_functions[i].native; @@ -236,6 +237,7 @@ njs_builtin_objects_create(njs_vm_t *vm) for (i = NJS_CONSTRUCTOR_OBJECT; i < NJS_CONSTRUCTOR_MAX; i++) { constructors[i].object.shared = 0; + constructors[i].object.extensible = 1; constructors[i].native = 1; constructors[i].ctor = 1; constructors[i].args_offset = 1; diff -r 7ed74a2e4c50 -r 824fbb7fcd35 njs/njs_date.c --- a/njs/njs_date.c Wed Jun 14 17:58:10 2017 +0300 +++ b/njs/njs_date.c Mon Jun 19 14:39:56 2017 +0300 @@ -154,6 +154,7 @@ njs_date_constructor(njs_vm_t *vm, njs_v nxt_lvlhsh_init(&date->object.shared_hash); date->object.type = NJS_DATE; date->object.shared = 0; + date->object.extensible = 1; date->object.__proto__ = &vm->prototypes[NJS_PROTOTYPE_DATE].object; date->time = time; diff -r 7ed74a2e4c50 -r 824fbb7fcd35 njs/njs_function.c --- a/njs/njs_function.c Wed Jun 14 17:58:10 2017 +0300 +++ b/njs/njs_function.c Mon Jun 19 14:39:56 2017 +0300 @@ -44,6 +44,7 @@ njs_function_alloc(njs_vm_t *vm) function->object.shared_hash = vm->shared->function_prototype_hash; function->object.type = NJS_FUNCTION; function->object.shared = 1; + function->object.extensible = 1; function->args_offset = 1; function->u.lambda = nxt_mem_cache_zalloc(vm->mem_cache_pool, @@ -635,6 +636,7 @@ njs_function_prototype_bind(njs_vm_t *vm function->object.__proto__ = &vm->prototypes[NJS_PROTOTYPE_FUNCTION].object; function->object.shared = 0; + function->object.extensible = 1; if (nargs == 1) { args = (njs_value_t *) &njs_value_void; diff -r 7ed74a2e4c50 -r 824fbb7fcd35 njs/njs_object.c --- a/njs/njs_object.c Wed Jun 14 17:58:10 2017 +0300 +++ b/njs/njs_object.c Mon Jun 19 14:39:56 2017 +0300 @@ -43,6 +43,7 @@ njs_object_alloc(njs_vm_t *vm) object->__proto__ = &vm->prototypes[NJS_PROTOTYPE_OBJECT].object; object->type = NJS_OBJECT; object->shared = 0; + object->extensible = 1; } return object; @@ -86,6 +87,7 @@ njs_object_value_alloc(njs_vm_t *vm, con nxt_lvlhsh_init(&ov->object.shared_hash); ov->object.type = njs_object_value_type(type); ov->object.shared = 0; + ov->object.extensible = 1; index = njs_primitive_prototype_index(type); ov->object.__proto__ = &vm->prototypes[index].object; @@ -416,6 +418,11 @@ njs_object_define_property(njs_vm_t *vm, return NXT_ERROR; } + if (!args[1].data.u.object->extensible) { + vm->exception = &njs_exception_type_error; + return NXT_ERROR; + } + ret = njs_define_property(vm, args[1].data.u.object, &args[2], args[3].data.u.object); @@ -444,6 +451,11 @@ njs_object_define_properties(njs_vm_t *v return NXT_ERROR; } + if (!args[1].data.u.object->extensible) { + vm->exception = &njs_exception_type_error; + return NXT_ERROR; + } + nxt_lvlhsh_each_init(&lhe, &njs_object_hash_proto); object = args[1].data.u.object; @@ -704,6 +716,44 @@ njs_object_get_prototype_of(njs_vm_t *vm } +static njs_ret_t +njs_object_freeze(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + nxt_lvlhsh_t *hash; + njs_object_t *object; + njs_object_prop_t *prop; + nxt_lvlhsh_each_t lhe; + + if (nargs < 2 || !njs_is_object(&args[1])) { + vm->exception = &njs_exception_type_error; + return NXT_ERROR; + } + + object = args[1].data.u.object; + object->extensible = 0; + + nxt_lvlhsh_each_init(&lhe, &njs_object_hash_proto); + + hash = &object->hash; + + for ( ;; ) { + prop = nxt_lvlhsh_each(hash, &lhe); + + if (prop == NULL) { + break; + } + + prop->writable = 0; + prop->configurable = 0; + } + + vm->retval = args[1]; + + return NXT_OK; +} + + /* * The __proto__ property of booleans, numbers and strings primitives, * of objects created by Boolean(), Number(), and String() constructors, @@ -881,6 +931,14 @@ static const njs_object_prop_t njs_obje .value = njs_native_function(njs_object_get_prototype_of, 0, NJS_SKIP_ARG, NJS_OBJECT_ARG), }, + + /* Object.freeze(). */ + { + .type = NJS_METHOD, + .name = njs_string("freeze"), + .value = njs_native_function(njs_object_freeze, 0, + NJS_SKIP_ARG, NJS_OBJECT_ARG), + }, }; diff -r 7ed74a2e4c50 -r 824fbb7fcd35 njs/njs_regexp.c --- a/njs/njs_regexp.c Wed Jun 14 17:58:10 2017 +0300 +++ b/njs/njs_regexp.c Mon Jun 19 14:39:56 2017 +0300 @@ -469,6 +469,7 @@ njs_regexp_alloc(njs_vm_t *vm, njs_regex regexp->object.__proto__ = &vm->prototypes[NJS_PROTOTYPE_REGEXP].object; regexp->object.type = NJS_REGEXP; regexp->object.shared = 0; + regexp->object.extensible = 1; regexp->last_index = 0; regexp->pattern = pattern; } diff -r 7ed74a2e4c50 -r 824fbb7fcd35 njs/njs_vm.c --- a/njs/njs_vm.c Wed Jun 14 17:58:10 2017 +0300 +++ b/njs/njs_vm.c Mon Jun 19 14:39:56 2017 +0300 @@ -425,6 +425,7 @@ njs_vmcode_function(njs_vm_t *vm, njs_va function->u.lambda = lambda; function->object.shared_hash = vm->shared->function_prototype_hash; function->object.__proto__ = &vm->prototypes[NJS_PROTOTYPE_FUNCTION].object; + function->object.extensible = 1; function->args_offset = 1; if (nesting != 0) { @@ -683,6 +684,10 @@ njs_vmcode_property_set(njs_vm_t *vm, nj break; case NXT_DECLINED: + if (!object->data.u.object->extensible) { + return sizeof(njs_vmcode_prop_set_t); + } + prop = njs_object_prop_alloc(vm, &pq.value, &njs_value_void, 1); if (nxt_slow_path(prop == NULL)) { return NXT_ERROR; diff -r 7ed74a2e4c50 -r 824fbb7fcd35 njs/njs_vm.h --- a/njs/njs_vm.h Wed Jun 14 17:58:10 2017 +0300 +++ b/njs/njs_vm.h Mon Jun 19 14:39:56 2017 +0300 @@ -208,7 +208,8 @@ struct njs_object_s { /* The type is used in constructor prototypes. */ njs_value_type_t type:8; - uint8_t shared; /* 1 bit */ + uint8_t shared; /* 1 bit */ + uint8_t extensible; /* 1 bit */ }; @@ -339,7 +340,9 @@ typedef union { .args_types = { __VA_ARGS__ }, \ .args_offset = 1, \ .u.native = _function, \ - .object = { .type = NJS_FUNCTION, .shared = 1 }, \ + .object = { .type = NJS_FUNCTION, \ + .shared = 1, \ + .extensible = 1 }, \ } \ } \ } diff -r 7ed74a2e4c50 -r 824fbb7fcd35 njs/test/njs_unit_test.c --- a/njs/test/njs_unit_test.c Wed Jun 14 17:58:10 2017 +0300 +++ b/njs/test/njs_unit_test.c Mon Jun 19 14:39:56 2017 +0300 @@ -6071,6 +6071,99 @@ static njs_unit_test_t njs_test[] = { nxt_string("Object.getOwnPropertyDescriptor(1, '0')"), nxt_string("TypeError") }, + { nxt_string("Object.defineProperty(Object.freeze({}), 'b', {})"), + nxt_string("TypeError") }, + + { nxt_string("Object.defineProperties(Object.freeze({}), {b:{}})"), + nxt_string("TypeError") }, + + { nxt_string("var o = Object.freeze({a:1}); o.a = 2; o.a"), + nxt_string("1") }, + + { nxt_string("var o = Object.freeze({a:1}); delete o.a; o.a"), + nxt_string("1") }, + + { nxt_string("var o = Object.freeze({a:1}); o.b = 1; o.b"), + nxt_string("undefined") }, + + { nxt_string("var o = Object.freeze(Object.create({a:1})); o.a = 2; o.a"), + nxt_string("1") }, + + { nxt_string("var o = Object.freeze({a:{b:1}}); o.a.b = 2; o.a.b"), + nxt_string("2") }, + + { nxt_string("Object.defineProperty([1,2], 'a', {value:1}).a"), + nxt_string("1") }, + + { nxt_string("var a = Object.freeze([1,2]);" + "Object.defineProperty(a, 'a', {value:1}).a"), + nxt_string("TypeError") }, + + { nxt_string("var a = [1,2]; a.a = 1; Object.freeze(a);" + "delete a.a; a.a"), + nxt_string("1") }, + + { nxt_string("var a = [1,2]; a.a = 1; Object.freeze(a);" + "a.a = 2; a.a"), + nxt_string("1") }, + + { nxt_string("var a = Object.freeze([1,2]); a.a = 1; a.a"), + nxt_string("undefined") }, + + { nxt_string("Object.defineProperty(function() {}, 'a', {value:1}).a"), + nxt_string("1") }, + + { nxt_string("var f = Object.freeze(function() {});" + "Object.defineProperty(f, 'a', {value:1}).a"), + nxt_string("TypeError") }, + + { nxt_string("var f = function() {}; f.a = 1; Object.freeze(f);" + "delete f.a; f.a"), + nxt_string("1") }, + + { nxt_string("var f = function() {}; f.a = 1; Object.freeze(f);" + "f.a = 2; f.a"), + nxt_string("1") }, + + { nxt_string("var f = Object.freeze(function() {}); f.a = 1; f.a"), + nxt_string("undefined") }, + + { nxt_string("Object.defineProperty(new Date(''), 'a', {value:1}).a"), + nxt_string("1") }, + + { nxt_string("var d = Object.freeze(new Date(''));" + "Object.defineProperty(d, 'a', {value:1}).a"), + nxt_string("TypeError") }, + + { nxt_string("var d = new Date(''); d.a = 1; Object.freeze(d);" + "delete d.a; d.a"), + nxt_string("1") }, + + { nxt_string("var d = new Date(''); d.a = 1; Object.freeze(d);" + "d.a = 2; d.a"), + nxt_string("1") }, + + { nxt_string("var d = Object.freeze(new Date('')); d.a = 1; d.a"), + nxt_string("undefined") }, + + { nxt_string("Object.defineProperty(new RegExp(''), 'a', {value:1}).a"), + nxt_string("1") }, + + { nxt_string("var r = Object.freeze(new RegExp(''));" + "Object.defineProperty(r, 'a', {value:1}).a"), + nxt_string("TypeError") }, + + { nxt_string("var r = new RegExp(''); r.a = 1; Object.freeze(r);" + "delete r.a; r.a"), + nxt_string("1") }, + + { nxt_string("var r = new RegExp(''); r.a = 1; Object.freeze(r);" + "r.a = 2; r.a"), + nxt_string("1") }, + + { nxt_string("var r = Object.freeze(new RegExp('')); r.a = 1; r.a"), + nxt_string("undefined") }, + { nxt_string("var d = new Date(''); d +' '+ d.getTime()"), nxt_string("Invalid Date NaN") }, From xeioex at nginx.com Mon Jun 19 11:49:31 2017 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Mon, 19 Jun 2017 11:49:31 +0000 Subject: [njs] Object.preventExtensions() method. Message-ID: details: http://hg.nginx.org/njs/rev/4d5a5d618fca branches: changeset: 367:4d5a5d618fca user: Dmitry Volyntsev date: Mon Jun 19 14:40:14 2017 +0300 description: Object.preventExtensions() method. diffstat: njs/njs_object.c | 25 +++++++++++++++++++++++++ njs/test/njs_unit_test.c | 17 +++++++++++++++++ 2 files changed, 42 insertions(+), 0 deletions(-) diffs (69 lines): diff -r 824fbb7fcd35 -r 4d5a5d618fca njs/njs_object.c --- a/njs/njs_object.c Mon Jun 19 14:39:56 2017 +0300 +++ b/njs/njs_object.c Mon Jun 19 14:40:14 2017 +0300 @@ -754,6 +754,23 @@ njs_object_freeze(njs_vm_t *vm, njs_valu } +static njs_ret_t +njs_object_prevent_extensions(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + if (nargs < 2 || !njs_is_object(&args[1])) { + vm->exception = &njs_exception_type_error; + return NXT_ERROR; + } + + args[1].data.u.object->extensible = 0; + + vm->retval = args[1]; + + return NXT_OK; +} + + /* * The __proto__ property of booleans, numbers and strings primitives, * of objects created by Boolean(), Number(), and String() constructors, @@ -939,6 +956,14 @@ static const njs_object_prop_t njs_obje .value = njs_native_function(njs_object_freeze, 0, NJS_SKIP_ARG, NJS_OBJECT_ARG), }, + + /* Object.preventExtensions(). */ + { + .type = NJS_METHOD, + .name = njs_long_string("preventExtensions"), + .value = njs_native_function(njs_object_prevent_extensions, 0, + NJS_SKIP_ARG, NJS_OBJECT_ARG), + }, }; diff -r 824fbb7fcd35 -r 4d5a5d618fca njs/test/njs_unit_test.c --- a/njs/test/njs_unit_test.c Mon Jun 19 14:39:56 2017 +0300 +++ b/njs/test/njs_unit_test.c Mon Jun 19 14:40:14 2017 +0300 @@ -6164,6 +6164,23 @@ static njs_unit_test_t njs_test[] = { nxt_string("var r = Object.freeze(new RegExp('')); r.a = 1; r.a"), nxt_string("undefined") }, + { nxt_string("var o = Object.preventExtensions({a:1});" + "Object.defineProperty(o, 'b', {value:1})"), + nxt_string("TypeError") }, + + { nxt_string("var o = Object.preventExtensions({a:1});" + "Object.defineProperties(o, {b:{value:1}})"), + nxt_string("TypeError") }, + + { nxt_string("var o = Object.preventExtensions({a:1}); o.a = 2; o.a"), + nxt_string("2") }, + + { nxt_string("var o = Object.preventExtensions({a:1}); delete o.a; o.a"), + nxt_string("undefined") }, + + { nxt_string("var o = Object.preventExtensions({a:1}); o.b = 1; o.b"), + nxt_string("undefined") }, + { nxt_string("var d = new Date(''); d +' '+ d.getTime()"), nxt_string("Invalid Date NaN") }, From xeioex at nginx.com Mon Jun 19 11:49:32 2017 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Mon, 19 Jun 2017 11:49:32 +0000 Subject: [njs] Object.isFrozen() method. Message-ID: details: http://hg.nginx.org/njs/rev/b2ccd7673a5e branches: changeset: 368:b2ccd7673a5e user: Dmitry Volyntsev date: Mon Jun 19 14:41:03 2017 +0300 description: Object.isFrozen() method. diffstat: njs/njs_object.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++ njs/test/njs_unit_test.c | 51 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+), 0 deletions(-) diffs (134 lines): diff -r 4d5a5d618fca -r b2ccd7673a5e njs/njs_object.c --- a/njs/njs_object.c Mon Jun 19 14:40:14 2017 +0300 +++ b/njs/njs_object.c Mon Jun 19 14:41:03 2017 +0300 @@ -755,6 +755,54 @@ njs_object_freeze(njs_vm_t *vm, njs_valu static njs_ret_t +njs_object_is_frozen(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + nxt_lvlhsh_t *hash; + njs_object_t *object; + njs_object_prop_t *prop; + nxt_lvlhsh_each_t lhe; + const njs_value_t *retval; + + if (nargs < 2 || !njs_is_object(&args[1])) { + vm->exception = &njs_exception_type_error; + return NXT_ERROR; + } + + retval = &njs_string_false; + + object = args[1].data.u.object; + nxt_lvlhsh_each_init(&lhe, &njs_object_hash_proto); + + hash = &object->hash; + + if (object->extensible) { + goto done; + } + + for ( ;; ) { + prop = nxt_lvlhsh_each(hash, &lhe); + + if (prop == NULL) { + break; + } + + if (prop->writable || prop->configurable) { + goto done; + } + } + + retval = &njs_string_true; + +done: + + vm->retval = *retval; + + return NXT_OK; +} + + +static njs_ret_t njs_object_prevent_extensions(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, njs_index_t unused) { @@ -957,6 +1005,14 @@ static const njs_object_prop_t njs_obje NJS_SKIP_ARG, NJS_OBJECT_ARG), }, + /* Object.isFrozen(). */ + { + .type = NJS_METHOD, + .name = njs_string("isFrozen"), + .value = njs_native_function(njs_object_is_frozen, 0, + NJS_SKIP_ARG, NJS_OBJECT_ARG), + }, + /* Object.preventExtensions(). */ { .type = NJS_METHOD, diff -r 4d5a5d618fca -r b2ccd7673a5e njs/test/njs_unit_test.c --- a/njs/test/njs_unit_test.c Mon Jun 19 14:40:14 2017 +0300 +++ b/njs/test/njs_unit_test.c Mon Jun 19 14:41:03 2017 +0300 @@ -6164,6 +6164,57 @@ static njs_unit_test_t njs_test[] = { nxt_string("var r = Object.freeze(new RegExp('')); r.a = 1; r.a"), nxt_string("undefined") }, + { nxt_string("Object.isFrozen({a:1})"), + nxt_string("false") }, + + { nxt_string("Object.isFrozen([1,2])"), + nxt_string("false") }, + + { nxt_string("Object.isFrozen(function() {})"), + nxt_string("false") }, + + { nxt_string("Object.isFrozen(new Date(''))"), + nxt_string("false") }, + + { nxt_string("Object.isFrozen(new RegExp(''))"), + nxt_string("false") }, + + { nxt_string("Object.isFrozen(1)"), + nxt_string("TypeError") }, + + { nxt_string("Object.isFrozen('')"), + nxt_string("TypeError") }, + + { nxt_string("Object.isFrozen(Object.defineProperties({}, {a:{value:1}}))"), + nxt_string("false") }, + + { nxt_string("var o = Object.defineProperties({}, {a:{}, b:{}});" + "o = Object.preventExtensions(o);" + "Object.isFrozen(o)"), + nxt_string("true") }, + + { nxt_string("var o = Object.defineProperties({}, {a:{}, b:{writable:1}});" + "o = Object.preventExtensions(o);" + "Object.isFrozen(o)"), + nxt_string("false") }, + + { nxt_string("var o = Object.defineProperties({}, {a:{writable:1}});" + "o = Object.preventExtensions(o);" + "Object.isFrozen(o)"), + nxt_string("false") }, + + { nxt_string("var o = Object.defineProperties({}, {a:{configurable:1}});" + "o = Object.preventExtensions(o);" + "Object.isFrozen(o)"), + nxt_string("false") }, + + { nxt_string("var o = Object.preventExtensions({a:1});" + "Object.isFrozen(o)"), + nxt_string("false") }, + + { nxt_string("var o = Object.freeze({a:1}); Object.isFrozen(o)"), + nxt_string("true") }, + { nxt_string("var o = Object.preventExtensions({a:1});" "Object.defineProperty(o, 'b', {value:1})"), nxt_string("TypeError") }, From xeioex at nginx.com Mon Jun 19 11:49:33 2017 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Mon, 19 Jun 2017 11:49:33 +0000 Subject: [njs] Object.isExtensible() method. Message-ID: details: http://hg.nginx.org/njs/rev/bd6c05f66ea9 branches: changeset: 369:bd6c05f66ea9 user: Dmitry Volyntsev date: Mon Jun 19 14:46:34 2017 +0300 description: Object.isExtensible() method. diffstat: njs/njs_object.c | 28 ++++++++++++++++++++++++++++ njs/test/njs_unit_test.c | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 0 deletions(-) diffs (88 lines): diff -r b2ccd7673a5e -r bd6c05f66ea9 njs/njs_object.c --- a/njs/njs_object.c Mon Jun 19 14:41:03 2017 +0300 +++ b/njs/njs_object.c Mon Jun 19 14:46:34 2017 +0300 @@ -819,6 +819,26 @@ njs_object_prevent_extensions(njs_vm_t * } +static njs_ret_t +njs_object_is_extensible(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + const njs_value_t *retval; + + if (nargs < 2 || !njs_is_object(&args[1])) { + vm->exception = &njs_exception_type_error; + return NXT_ERROR; + } + + retval = args[1].data.u.object->extensible ? &njs_string_true : + &njs_string_false; + + vm->retval = *retval; + + return NXT_OK; +} + + /* * The __proto__ property of booleans, numbers and strings primitives, * of objects created by Boolean(), Number(), and String() constructors, @@ -1020,6 +1040,14 @@ static const njs_object_prop_t njs_obje .value = njs_native_function(njs_object_prevent_extensions, 0, NJS_SKIP_ARG, NJS_OBJECT_ARG), }, + + /* Object.isExtensible(). */ + { + .type = NJS_METHOD, + .name = njs_string("isExtensible"), + .value = njs_native_function(njs_object_is_extensible, 0, + NJS_SKIP_ARG, NJS_OBJECT_ARG), + }, }; diff -r b2ccd7673a5e -r bd6c05f66ea9 njs/test/njs_unit_test.c --- a/njs/test/njs_unit_test.c Mon Jun 19 14:41:03 2017 +0300 +++ b/njs/test/njs_unit_test.c Mon Jun 19 14:46:34 2017 +0300 @@ -6232,6 +6232,39 @@ static njs_unit_test_t njs_test[] = { nxt_string("var o = Object.preventExtensions({a:1}); o.b = 1; o.b"), nxt_string("undefined") }, + { nxt_string("Object.isExtensible({})"), + nxt_string("true") }, + + { nxt_string("Object.isExtensible([])"), + nxt_string("true") }, + + { nxt_string("Object.isExtensible(function() {})"), + nxt_string("true") }, + + { nxt_string("Object.isExtensible(new Date(''))"), + nxt_string("true") }, + + { nxt_string("Object.isExtensible(new RegExp(''))"), + nxt_string("true") }, + + { nxt_string("Object.isExtensible(1)"), + nxt_string("TypeError") }, + + { nxt_string("Object.isExtensible('')"), + nxt_string("TypeError") }, + + { nxt_string("Object.isExtensible(Object.preventExtensions({}))"), + nxt_string("false") }, + + { nxt_string("Object.isExtensible(Object.preventExtensions([]))"), + nxt_string("false") }, + + { nxt_string("Object.isExtensible(Object.freeze({}))"), + nxt_string("false") }, + + { nxt_string("Object.isExtensible(Object.freeze([]))"), + nxt_string("false") }, + { nxt_string("var d = new Date(''); d +' '+ d.getTime()"), nxt_string("Invalid Date NaN") }, From xeioex at nginx.com Mon Jun 19 11:49:35 2017 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Mon, 19 Jun 2017 11:49:35 +0000 Subject: [njs] Object.seal() method. Message-ID: details: http://hg.nginx.org/njs/rev/cc5ab912d455 branches: changeset: 370:cc5ab912d455 user: Dmitry Volyntsev date: Mon Jun 19 14:46:39 2017 +0300 description: Object.seal() method. diffstat: njs/njs_object.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ njs/test/njs_unit_test.c | 21 +++++++++++++++++++++ 2 files changed, 66 insertions(+), 0 deletions(-) diffs (93 lines): diff -r bd6c05f66ea9 -r cc5ab912d455 njs/njs_object.c --- a/njs/njs_object.c Mon Jun 19 14:46:34 2017 +0300 +++ b/njs/njs_object.c Mon Jun 19 14:46:39 2017 +0300 @@ -803,6 +803,43 @@ done: static njs_ret_t +njs_object_seal(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + nxt_lvlhsh_t *hash; + njs_object_t *object; + njs_object_prop_t *prop; + nxt_lvlhsh_each_t lhe; + + if (nargs < 2 || !njs_is_object(&args[1])) { + vm->exception = &njs_exception_type_error; + return NXT_ERROR; + } + + object = args[1].data.u.object; + object->extensible = 0; + + nxt_lvlhsh_each_init(&lhe, &njs_object_hash_proto); + + hash = &object->hash; + + for ( ;; ) { + prop = nxt_lvlhsh_each(hash, &lhe); + + if (prop == NULL) { + break; + } + + prop->configurable = 0; + } + + vm->retval = args[1]; + + return NXT_OK; +} + + +static njs_ret_t njs_object_prevent_extensions(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, njs_index_t unused) { @@ -1033,6 +1070,14 @@ static const njs_object_prop_t njs_obje NJS_SKIP_ARG, NJS_OBJECT_ARG), }, + /* Object.seal(). */ + { + .type = NJS_METHOD, + .name = njs_string("seal"), + .value = njs_native_function(njs_object_seal, 0, + NJS_SKIP_ARG, NJS_OBJECT_ARG), + }, + /* Object.preventExtensions(). */ { .type = NJS_METHOD, diff -r bd6c05f66ea9 -r cc5ab912d455 njs/test/njs_unit_test.c --- a/njs/test/njs_unit_test.c Mon Jun 19 14:46:34 2017 +0300 +++ b/njs/test/njs_unit_test.c Mon Jun 19 14:46:39 2017 +0300 @@ -6215,6 +6215,27 @@ static njs_unit_test_t njs_test[] = { nxt_string("var o = Object.freeze({a:1}); Object.isFrozen(o)"), nxt_string("true") }, + { nxt_string("var o = Object.seal({a:1}); o.a = 2; o.a"), + nxt_string("2") }, + + { nxt_string("var o = Object.seal({a:1}); delete o.a; o.a"), + nxt_string("1") }, + + { nxt_string("var o = Object.seal({a:1}); o.b = 1; o.b"), + nxt_string("undefined") }, + + { nxt_string("var o = Object.seal(Object.create({a:1})); o.a = 2; o.a"), + nxt_string("1") }, + + { nxt_string("var o = Object.seal({a:{b:1}}); o.a.b = 2; o.a.b"), + nxt_string("2") }, + + { nxt_string("Object.seal(1)"), + nxt_string("TypeError") }, + + { nxt_string("Object.seal('')"), + nxt_string("TypeError") }, + { nxt_string("var o = Object.preventExtensions({a:1});" "Object.defineProperty(o, 'b', {value:1})"), nxt_string("TypeError") }, From xeioex at nginx.com Mon Jun 19 11:49:36 2017 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Mon, 19 Jun 2017 11:49:36 +0000 Subject: [njs] Object.isSealed() method. Message-ID: details: http://hg.nginx.org/njs/rev/bcd7a7256805 branches: changeset: 371:bcd7a7256805 user: Dmitry Volyntsev date: Mon Jun 19 14:46:46 2017 +0300 description: Object.isSealed() method. diffstat: njs/njs_object.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++ njs/test/njs_unit_test.c | 51 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+), 0 deletions(-) diffs (134 lines): diff -r cc5ab912d455 -r bcd7a7256805 njs/njs_object.c --- a/njs/njs_object.c Mon Jun 19 14:46:39 2017 +0300 +++ b/njs/njs_object.c Mon Jun 19 14:46:46 2017 +0300 @@ -840,6 +840,54 @@ njs_object_seal(njs_vm_t *vm, njs_value_ static njs_ret_t +njs_object_is_sealed(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + nxt_lvlhsh_t *hash; + njs_object_t *object; + njs_object_prop_t *prop; + nxt_lvlhsh_each_t lhe; + const njs_value_t *retval; + + if (nargs < 2 || !njs_is_object(&args[1])) { + vm->exception = &njs_exception_type_error; + return NXT_ERROR; + } + + retval = &njs_string_false; + + object = args[1].data.u.object; + nxt_lvlhsh_each_init(&lhe, &njs_object_hash_proto); + + hash = &object->hash; + + if (object->extensible) { + goto done; + } + + for ( ;; ) { + prop = nxt_lvlhsh_each(hash, &lhe); + + if (prop == NULL) { + break; + } + + if (prop->writable) { + goto done; + } + } + + retval = &njs_string_true; + +done: + + vm->retval = *retval; + + return NXT_OK; +} + + +static njs_ret_t njs_object_prevent_extensions(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, njs_index_t unused) { @@ -1078,6 +1126,14 @@ static const njs_object_prop_t njs_obje NJS_SKIP_ARG, NJS_OBJECT_ARG), }, + /* Object.isSealed(). */ + { + .type = NJS_METHOD, + .name = njs_string("isSealed"), + .value = njs_native_function(njs_object_is_sealed, 0, + NJS_SKIP_ARG, NJS_OBJECT_ARG), + }, + /* Object.preventExtensions(). */ { .type = NJS_METHOD, diff -r cc5ab912d455 -r bcd7a7256805 njs/test/njs_unit_test.c --- a/njs/test/njs_unit_test.c Mon Jun 19 14:46:39 2017 +0300 +++ b/njs/test/njs_unit_test.c Mon Jun 19 14:46:46 2017 +0300 @@ -6236,6 +6236,57 @@ static njs_unit_test_t njs_test[] = { nxt_string("Object.seal('')"), nxt_string("TypeError") }, + { nxt_string("Object.isSealed({a:1})"), + nxt_string("false") }, + + { nxt_string("Object.isSealed([1,2])"), + nxt_string("false") }, + + { nxt_string("Object.isSealed(function() {})"), + nxt_string("false") }, + + { nxt_string("Object.isSealed(new Date(''))"), + nxt_string("false") }, + + { nxt_string("Object.isSealed(new RegExp(''))"), + nxt_string("false") }, + + { nxt_string("Object.isSealed(1)"), + nxt_string("TypeError") }, + + { nxt_string("Object.isSealed('')"), + nxt_string("TypeError") }, + + { nxt_string("Object.isSealed(Object.defineProperties({}, {a:{value:1}}))"), + nxt_string("false") }, + + { nxt_string("var o = Object.defineProperties({}, {a:{}, b:{}});" + "o = Object.preventExtensions(o);" + "Object.isSealed(o)"), + nxt_string("true") }, + + { nxt_string("var o = Object.defineProperties({}, {a:{}, b:{writable:1}});" + "o = Object.preventExtensions(o);" + "Object.isSealed(o)"), + nxt_string("false") }, + + { nxt_string("var o = Object.defineProperties({}, {a:{writable:1}});" + "o = Object.preventExtensions(o);" + "Object.isSealed(o)"), + nxt_string("false") }, + + { nxt_string("var o = Object.defineProperties({}, {a:{configurable:1}});" + "o = Object.preventExtensions(o);" + "Object.isSealed(o)"), + nxt_string("true") }, + + { nxt_string("var o = Object.preventExtensions({a:1});" + "Object.isFrozen(o)"), + nxt_string("false") }, + + { nxt_string("var o = Object.freeze({a:1}); Object.isFrozen(o)"), + nxt_string("true") }, + { nxt_string("var o = Object.preventExtensions({a:1});" "Object.defineProperty(o, 'b', {value:1})"), nxt_string("TypeError") }, From mdounin at mdounin.ru Mon Jun 19 13:47:53 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Mon, 19 Jun 2017 16:47:53 +0300 Subject: [PATCH 4 of 4] HTTP/2: reject HTTP/2 requests with connection-specific headers In-Reply-To: References: <10c3f4c37f96ef496eff.1497356385@piotrsikora.sfo.corp.google.com> <20170614190047.GU55433@mdounin.ru> Message-ID: <20170619134753.GJ55433@mdounin.ru> Hello! On Sat, Jun 17, 2017 at 01:57:38PM -0700, Piotr Sikora via nginx-devel wrote: [...] > > Unless there are practical reasons for these changes, I would > > rather reject the series. > > The practical reason is that other implementations (e.g. nghttp2) > reject requests with those headers, which leads to a weird behavior > where NGINX accepts requests and proxies them to a HTTP/2 upstream > which rejects them because they contain one of those headers. > > We could clear those headers in proxy module (I'm already doing that > for most of the headers, anyway), but it feels like a workaround for > broken clients. We anyway have to remove hop-by-hop headers from HTTP/1.x connections. I don't see how HTTP/2 can be different, specially if one side uses HTTP/1.x and another one uses HTTP/2. Accordingly, if an upstream server rejects a request, there are two possible reasons: - we've forgot to remove something we have to (that is, there is a bug in nginx); - a client sent something it shouldn't (that is, there is a bug in the client). In either case returing the error to the client, as it will naturally happen, looks fine to me. > Having said that, I'm fine with dropping the whole patchset. Yes, please. -- Maxim Dounin http://nginx.org/ From bartw at xs4all.nl Mon Jun 19 14:09:43 2017 From: bartw at xs4all.nl (Bart Warmerdam) Date: Mon, 19 Jun 2017 16:09:43 +0200 Subject: [PATCH] Avoid using the result of i2d_SSL_SESSION when the session is invalid In-Reply-To: <20170619101004.GA835@lo0.su> References: <20170619101004.GA835@lo0.su> Message-ID: <2a3d521156e2a330a7d1340946b995f9@xs4all.nl> According to the man-page of i2d_SSL_SESSION the result can be NULL or 0, but case the actual result can also be -1 in case of a failed CRYPTO_malloc. The call trace for this function is: Call chain: i2d_SSL_SESSION i2d_SSL_SESSION_ASN1 ASN1_item_i2d asn1_item_flags_i2d The preprocessor output generates the following code: static int asn1_item_flags_i2d(ASN1_VALUE *val, unsigned char **out, const ASN1_ITEM *it, int flags) { if (out && !*out) { unsigned char *p, *buf; int len; len = ASN1_item_ex_i2d(&val, # 60 "crypto/asn1/tasn_enc.c" 3 4 ((void *)0) # 60 "crypto/asn1/tasn_enc.c" , it, -1, flags); if (len <= 0) return len; buf = CRYPTO_malloc(len, "crypto/asn1/tasn_enc.c", 63); if (buf == # 64 "crypto/asn1/tasn_enc.c" 3 4 ((void *)0) # 64 "crypto/asn1/tasn_enc.c" ) return -1; p = buf; ASN1_item_ex_i2d(&val, &p, it, -1, flags); *out = buf; return len; } return ASN1_item_ex_i2d(&val, out, it, -1, flags); } As you can see around line 65 the -1 is returned once the allocation fails, which is then directly returned in all intermediate calls up to i2d_SSL_SESSION. So the -1 case is missed once the current "if statement" is kept without the "len < 1" or-statement. Regards, B. On 2017-06-19 12:10, Ruslan Ermilov wrote: > On Mon, Jun 19, 2017 at 08:08:38AM +0200, Bart Warmerdam wrote: >> # HG changeset patch >> # User Bart Warmerdam >> # Date 1497852211 -7200 >> # Mon Jun 19 08:03:31 2017 +0200 >> # Branch i2d_ssl_session_length >> # Node ID 079afb2cb4be3ef06d07e96d1a54cc359b971631 >> # Parent d1816a2696de8c2faa1cd913a151e5f62a8620f3 >> Make sure to also take into account the 'return 0' response of >> i2d_SSL_SESSION, which is possible when the session is not valid > > A case of invalid session is already caught by checking the return > value of SSL_get0_session() just prior to calling i2d_SSL_SESSION() > in ngx_http_upstream_save_round_robin_peer_session() and > ngx_stream_upstream_save_round_robin_peer_session(). > > The ngx_ssl_new_session() function is passed as an argument to > SSL_CTX_sess_set_new_cb() that "sets the callback function, which > is automatically called whenever a new session was negotiated." > > Do you think that it's possible for a session to be invalid in > either of these cases? > >> diff -r d1816a2696de -r 079afb2cb4be src/event/ngx_event_openssl.c >> --- a/src/event/ngx_event_openssl.c Fri Jun 16 18:15:58 2017 +0300 >> +++ b/src/event/ngx_event_openssl.c Mon Jun 19 08:03:31 2017 +0200 >> @@ -2458,9 +2458,9 @@ >> >> len = i2d_SSL_SESSION(sess, NULL); >> >> - /* do not cache too big session */ >> - >> - if (len > (int) NGX_SSL_MAX_SESSION_SIZE) { >> + /* do not cache too big or invalid session */ >> + >> + if (len > (int) NGX_SSL_MAX_SESSION_SIZE || len < 1) { >> return 0; >> } >> >> diff -r d1816a2696de -r 079afb2cb4be >> src/http/ngx_http_upstream_round_robin.c >> --- a/src/http/ngx_http_upstream_round_robin.c Fri Jun 16 18:15:58 >> 2017 >> +0300 >> +++ b/src/http/ngx_http_upstream_round_robin.c Mon Jun 19 08:03:31 >> 2017 >> +0200 >> @@ -755,9 +755,9 @@ >> >> len = i2d_SSL_SESSION(ssl_session, NULL); >> >> - /* do not cache too big session */ >> + /* do not cache too big or invalid session */ >> >> - if (len > NGX_SSL_MAX_SESSION_SIZE) { >> + if (len > NGX_SSL_MAX_SESSION_SIZE || len < 1) { >> return; >> } >> >> diff -r d1816a2696de -r 079afb2cb4be >> src/stream/ngx_stream_upstream_round_robin.c >> --- a/src/stream/ngx_stream_upstream_round_robin.c Fri Jun 16 >> 18:15:58 2017 +0300 >> +++ b/src/stream/ngx_stream_upstream_round_robin.c Mon Jun 19 >> 08:03:31 2017 +0200 >> @@ -787,9 +787,9 @@ >> >> len = i2d_SSL_SESSION(ssl_session, NULL); >> >> - /* do not cache too big session */ >> + /* do not cache too big or invalid session */ >> >> - if (len > NGX_SSL_MAX_SESSION_SIZE) { >> + if (len > NGX_SSL_MAX_SESSION_SIZE || len < 1) { >> return; >> } >> _______________________________________________ >> nginx-devel mailing list >> nginx-devel at nginx.org >> http://mailman.nginx.org/mailman/listinfo/nginx-devel >> From piotrsikora at google.com Mon Jun 19 14:28:29 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Mon, 19 Jun 2017 07:28:29 -0700 Subject: [PATCH] HTTP/2: add debug logging of control frames In-Reply-To: <6088549.Je3sRdAShv@vbart-workstation> References: <06d6418afe6e73604aea.1491275620@piotrsikora.sfo.corp.google.com> <6088549.Je3sRdAShv@vbart-workstation> Message-ID: Hey Valentin, > Ok, I've already resigned myself to multiline output, but don't let it > look like an another SETTINGS frame. > > IMHO, something like that will be good enough: > > http2 send SETTINGS frame > http2 SETTINGS param MAX_CONCURRENT_STREAMS: 100 > http2 SETTINGS param INITIAL_WINDOW_SIZE: 65536 > http2 SETTINGS param MAX_FRAME_SIZE: 16777215 Done, with retained "send " prefix to differentiate params that we send and receive. I've also re-added params counter to the "send SETTINGS frame", by setting "len" value a bit sooner, so that the number of params is calculated and cannot be forgotten in subsequent commits. Best regards, Piotr Sikora From piotrsikora at google.com Mon Jun 19 14:28:33 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Mon, 19 Jun 2017 07:28:33 -0700 Subject: [PATCH] HTTP/2: add debug logging of control frames In-Reply-To: <6088549.Je3sRdAShv@vbart-workstation> References: <6088549.Je3sRdAShv@vbart-workstation> Message-ID: <1f1549823fba355a0dd1.1497882513@piotrsikora.sfo.corp.google.com> # HG changeset patch # User Piotr Sikora # Date 1490516711 25200 # Sun Mar 26 01:25:11 2017 -0700 # Node ID 1f1549823fba355a0dd1af49108be4b4898bf331 # Parent d1816a2696de8c2faa1cd913a151e5f62a8620f3 HTTP/2: add debug logging of control frames. Signed-off-by: Piotr Sikora diff -r d1816a2696de -r 1f1549823fba src/http/v2/ngx_http_v2.c --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -41,9 +41,11 @@ /* settings fields */ #define NGX_HTTP_V2_HEADER_TABLE_SIZE_SETTING 0x1 +#define NGX_HTTP_V2_ENABLE_PUSH_SETTING 0x2 #define NGX_HTTP_V2_MAX_STREAMS_SETTING 0x3 #define NGX_HTTP_V2_INIT_WINDOW_SIZE_SETTING 0x4 #define NGX_HTTP_V2_MAX_FRAME_SIZE_SETTING 0x5 +#define NGX_HTTP_V2_HEADER_LIST_SIZE_SETTING 0x6 #define NGX_HTTP_V2_FRAME_BUFFER_SIZE 24 @@ -1946,6 +1948,9 @@ ngx_http_v2_state_settings(ngx_http_v2_c return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); } + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 SETTINGS frame ack:1"); + h2c->settings_ack = 1; return ngx_http_v2_state_complete(h2c, pos, end); @@ -1959,6 +1964,10 @@ ngx_http_v2_state_settings(ngx_http_v2_c return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); } + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 SETTINGS frame params:%uz", + h2c->state.length / NGX_HTTP_V2_SETTINGS_PARAM_SIZE); + return ngx_http_v2_state_settings_params(h2c, pos, end); } @@ -1986,6 +1995,27 @@ ngx_http_v2_state_settings_params(ngx_ht switch (id) { + case NGX_HTTP_V2_HEADER_TABLE_SIZE_SETTING: + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 SETTINGS param HEADER_TABLE_SIZE:%ui " + "(ignored)", value); + break; + + case NGX_HTTP_V2_ENABLE_PUSH_SETTING: + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 SETTINGS param ENABLE_PUSH:%ui " + "(ignored)", value); + break; + + case NGX_HTTP_V2_MAX_STREAMS_SETTING: + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 SETTINGS param MAX_CONCURRENT_STREAMS:%ui " + "(ignored)", value); + break; + case NGX_HTTP_V2_INIT_WINDOW_SIZE_SETTING: if (value > NGX_HTTP_V2_MAX_WINDOW) { @@ -1997,6 +2027,10 @@ ngx_http_v2_state_settings_params(ngx_ht NGX_HTTP_V2_FLOW_CTRL_ERROR); } + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 SETTINGS param INITIAL_WINDOW_SIZE:%ui", + value); + window_delta = value - h2c->init_window; h2c->init_window = value; @@ -2015,16 +2049,34 @@ ngx_http_v2_state_settings_params(ngx_ht NGX_HTTP_V2_PROTOCOL_ERROR); } + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 SETTINGS param MAX_FRAME_SIZE:%ui", + value); + h2c->frame_size = value; break; + case NGX_HTTP_V2_HEADER_LIST_SIZE_SETTING: + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 SETTINGS param MAX_HEADER_LIST_SIZE:%ui " + "(ignored)", value); + break; + default: + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 SETTINGS param 0x%Xi:%ui " + "(ignored)", id, value); break; } pos += NGX_HTTP_V2_SETTINGS_PARAM_SIZE; } + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 send SETTINGS frame ack:1"); + frame = ngx_http_v2_get_frame(h2c, NGX_HTTP_V2_SETTINGS_ACK_SIZE, NGX_HTTP_V2_SETTINGS_FRAME, NGX_HTTP_V2_ACK_FLAG, 0); @@ -2075,12 +2127,16 @@ ngx_http_v2_state_ping(ngx_http_v2_conne } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, - "http2 PING frame, flags: %ud", h2c->state.flags); + "http2 PING frame ack:%ud", + h2c->state.flags & NGX_HTTP_V2_ACK_FLAG ? 1 : 0); if (h2c->state.flags & NGX_HTTP_V2_ACK_FLAG) { return ngx_http_v2_state_skip(h2c, pos, end); } + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 send PING frame ack:1"); + frame = ngx_http_v2_get_frame(h2c, NGX_HTTP_V2_PING_SIZE, NGX_HTTP_V2_PING_FRAME, NGX_HTTP_V2_ACK_FLAG, 0); @@ -2492,8 +2548,11 @@ ngx_http_v2_send_settings(ngx_http_v2_co ngx_http_v2_srv_conf_t *h2scf; ngx_http_v2_out_frame_t *frame; - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, - "http2 send SETTINGS frame"); + len = NGX_HTTP_V2_SETTINGS_PARAM_SIZE * 3; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 send SETTINGS frame params:%uz", + len / NGX_HTTP_V2_SETTINGS_PARAM_SIZE); frame = ngx_palloc(h2c->pool, sizeof(ngx_http_v2_out_frame_t)); if (frame == NULL) { @@ -2505,8 +2564,6 @@ ngx_http_v2_send_settings(ngx_http_v2_co return NGX_ERROR; } - len = NGX_HTTP_V2_SETTINGS_PARAM_SIZE * 3; - buf = ngx_create_temp_buf(h2c->pool, NGX_HTTP_V2_FRAME_HEADER_SIZE + len); if (buf == NULL) { return NGX_ERROR; @@ -2536,15 +2593,27 @@ ngx_http_v2_send_settings(ngx_http_v2_co h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx, ngx_http_v2_module); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 send SETTINGS param MAX_CONCURRENT_STREAMS:%ui", + h2scf->concurrent_streams); + buf->last = ngx_http_v2_write_uint16(buf->last, NGX_HTTP_V2_MAX_STREAMS_SETTING); buf->last = ngx_http_v2_write_uint32(buf->last, h2scf->concurrent_streams); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 send SETTINGS param INITIAL_WINDOW_SIZE:%uz", + h2scf->preread_size); + buf->last = ngx_http_v2_write_uint16(buf->last, NGX_HTTP_V2_INIT_WINDOW_SIZE_SETTING); buf->last = ngx_http_v2_write_uint32(buf->last, h2scf->preread_size); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 send SETTINGS param MAX_FRAME_SIZE:%ud", + NGX_HTTP_V2_MAX_FRAME_SIZE); + buf->last = ngx_http_v2_write_uint16(buf->last, NGX_HTTP_V2_MAX_FRAME_SIZE_SETTING); buf->last = ngx_http_v2_write_uint32(buf->last, From piotrsikora at google.com Mon Jun 19 14:31:11 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Mon, 19 Jun 2017 07:31:11 -0700 Subject: [PATCH] Proxy: add "proxy_ssl_alpn" directive In-Reply-To: <20170608163207.GZ55433@mdounin.ru> References: <7733d946e2651a2486a5.1496545442@piotrsikora.sfo.corp.google.com> <20170608163207.GZ55433@mdounin.ru> Message-ID: Hey Maxim, > It doesn't look like this patch make sense by its own. > > If this patch is a part of a larger work, please consider > submitting a patch series with the whole feature instead. > Individual patches which doesn't make much sense > by its own are hard to review, and are likely to be rejected. Actually, it does, the only missing part (from HTTP/2 patchset) is: case NGX_HTTP_VERSION_11: ngx_str_set(&alpn, NGX_HTTP_11_ALPN_ADVERTISE); break; + +#if (NGX_HTTP_V2) + case NGX_HTTP_VERSION_20: + ngx_str_set(&alpn, NGX_HTTP_V2_ALPN_ADVERTISE); + break; +#endif } if (ngx_ssl_alpn_protos(cf, plcf->upstream.ssl, &alpn) != NGX_OK) { ...so this patch is perfectly reviewable on its own. In case you expected ALPN-negotiation - I didn't add it, since it would require rewrite of upstream logic, i.e. u->create_request() would need to be called after upstream is already connected, and possibly again in case of retries that negotiated different ALPN protocol, which would add complexity to the already big patchset. Also, I'm not sure how useful this is for upstream connections in reverse proxies. Let me know if that's a must-have, but IMHO we could always add "proxy_http_version alpn" in the future, without blocking this and HTTP/2 patchset, which effectively implement "prior knowledge". Best regards, Piotr Sikora From mdounin at mdounin.ru Mon Jun 19 14:59:39 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Mon, 19 Jun 2017 17:59:39 +0300 Subject: [PATCH] Avoid using the result of i2d_SSL_SESSION when the session is invalid In-Reply-To: <2a3d521156e2a330a7d1340946b995f9@xs4all.nl> References: <20170619101004.GA835@lo0.su> <2a3d521156e2a330a7d1340946b995f9@xs4all.nl> Message-ID: <20170619145939.GK55433@mdounin.ru> Hello! On Mon, Jun 19, 2017 at 04:09:43PM +0200, Bart Warmerdam wrote: > According to the man-page of i2d_SSL_SESSION the result can be NULL or > 0, but case the actual result can also be -1 in case of a failed > CRYPTO_malloc. The call trace for this function is: > > Call chain: > i2d_SSL_SESSION > i2d_SSL_SESSION_ASN1 > ASN1_item_i2d > asn1_item_flags_i2d > > > The preprocessor output generates the following code: > > static int asn1_item_flags_i2d(ASN1_VALUE *val, unsigned char **out, > const ASN1_ITEM *it, int flags) > { > if (out && !*out) { This condition cannot be true, as nginx uses preallocated buffer for i2d_SSL_SESSION(). (Moreover, using a preallocated buffer is this is the only approach documented in the i2d_SSL_SESSION() manual page, and the only one actually available before OpenSSL 1.1.0.) [...] -- Maxim Dounin http://nginx.org/ From fangpeng1986 at gmail.com Mon Jun 19 15:09:58 2017 From: fangpeng1986 at gmail.com (Peng Fang) Date: Mon, 19 Jun 2017 23:09:58 +0800 Subject: [PATCH] add reload_delay directive Message-ID: exporting patch: # HG changeset patch # User RocFang # Date 1497882783 0 # Node ID 8b9e416ef7f9f8e7f96eaa53b479062683464481 # Parent a39bc74873faf9e5bea616561b43f6ecc55229f9 Introduced reload_delay. Previously, the master process will sleep 100ms before sending a SHUTDOWN signal to old worker processes when reload. This patch make the sleep time configurable, because in some scenarios, the new workers may spend more than 100ms to get ready. For example, the init_prcess hook of some 3rd modules may be time-consuming. diff -r a39bc74873fa -r 8b9e416ef7f9 src/core/nginx.c --- a/src/core/nginx.c Mon Jun 19 14:25:42 2017 +0300 +++ b/src/core/nginx.c Mon Jun 19 14:33:03 2017 +0000 @@ -152,6 +152,13 @@ 0, NULL }, + { ngx_string("reload_delay"), + NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + 0, + offsetof(ngx_core_conf_t, reload_delay), + NULL }, + ngx_null_command }; @@ -1022,6 +1029,7 @@ ccf->master = NGX_CONF_UNSET; ccf->timer_resolution = NGX_CONF_UNSET_MSEC; ccf->shutdown_timeout = NGX_CONF_UNSET_MSEC; + ccf->reload_delay = NGX_CONF_UNSET_MSEC; ccf->worker_processes = NGX_CONF_UNSET; ccf->debug_points = NGX_CONF_UNSET; @@ -1051,6 +1059,7 @@ ngx_conf_init_value(ccf->master, 1); ngx_conf_init_msec_value(ccf->timer_resolution, 0); ngx_conf_init_msec_value(ccf->shutdown_timeout, 0); + ngx_conf_init_msec_value(ccf->reload_delay, 100); ngx_conf_init_value(ccf->worker_processes, 1); ngx_conf_init_value(ccf->debug_points, 0); diff -r a39bc74873fa -r 8b9e416ef7f9 src/core/ngx_cycle.h --- a/src/core/ngx_cycle.h Mon Jun 19 14:25:42 2017 +0300 +++ b/src/core/ngx_cycle.h Mon Jun 19 14:33:03 2017 +0000 @@ -89,6 +89,7 @@ ngx_msec_t timer_resolution; ngx_msec_t shutdown_timeout; + ngx_msec_t reload_delay; ngx_int_t worker_processes; ngx_int_t debug_points; diff -r a39bc74873fa -r 8b9e416ef7f9 src/os/unix/ngx_process_cycle.c --- a/src/os/unix/ngx_process_cycle.c Mon Jun 19 14:25:42 2017 +0300 +++ b/src/os/unix/ngx_process_cycle.c Mon Jun 19 14:33:03 2017 +0000 @@ -245,7 +245,7 @@ ngx_start_cache_manager_processes(cycle, 1); /* allow new processes to start */ - ngx_msleep(100); + ngx_msleep(ccf->reload_delay); live = 1; ngx_signal_worker_processes(cycle, -------------- next part -------------- An HTML attachment was scrubbed... URL: From mdounin at mdounin.ru Mon Jun 19 15:28:59 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Mon, 19 Jun 2017 18:28:59 +0300 Subject: [PATCH] add reload_delay directive In-Reply-To: References: Message-ID: <20170619152858.GL55433@mdounin.ru> Hello! On Mon, Jun 19, 2017 at 11:09:58PM +0800, Peng Fang wrote: > # HG changeset patch > # User RocFang > # Date 1497882783 0 > # Node ID 8b9e416ef7f9f8e7f96eaa53b479062683464481 > # Parent a39bc74873faf9e5bea616561b43f6ecc55229f9 > Introduced reload_delay. > > Previously, the master process will sleep 100ms before sending a > SHUTDOWN signal to old worker processes when reload. This patch > make the sleep time configurable, because in some scenarios, the > new workers may spend more than 100ms to get ready. For example, > the init_prcess hook of some 3rd modules may be time-consuming. The sleep in question is intended to let OS some time to actually start the process, and not intended to allow time-consuming work to happen on a worker process start. In general, no time-consuming operations are expected to be done during a worker process start. Instead, time-consuming preparatory work is expected to happen in the context of the master process during configuration parsing and init module hooks. If a module does something time-consuming in the init process hook, it might be a good idea to change the module logic. Unless there is something more specific than a "the init_prcess hook of some 3rd modules may be time-consuming", I would rather reject the patch, as it introduces unneeded user-level complexity by adding a directive, and encourages bad module writing practice. Nevertheless, thank you for the patch. -- Maxim Dounin http://nginx.org/ From fangpeng1986 at gmail.com Mon Jun 19 15:39:14 2017 From: fangpeng1986 at gmail.com (Peng Fang) Date: Mon, 19 Jun 2017 23:39:14 +0800 Subject: [PATCH] add reload_delay directive In-Reply-To: <20170619152858.GL55433@mdounin.ru> References: <20170619152858.GL55433@mdounin.ru> Message-ID: Hello, Maxim, Thanks for your detailed explanation and I truly agree with you. 2017-06-19 23:28 GMT+08:00 Maxim Dounin : > Hello! > > On Mon, Jun 19, 2017 at 11:09:58PM +0800, Peng Fang wrote: > > > # HG changeset patch > > # User RocFang > > # Date 1497882783 0 > > # Node ID 8b9e416ef7f9f8e7f96eaa53b479062683464481 > > # Parent a39bc74873faf9e5bea616561b43f6ecc55229f9 > > Introduced reload_delay. > > > > Previously, the master process will sleep 100ms before sending a > > SHUTDOWN signal to old worker processes when reload. This patch > > make the sleep time configurable, because in some scenarios, the > > new workers may spend more than 100ms to get ready. For example, > > the init_prcess hook of some 3rd modules may be time-consuming. > > The sleep in question is intended to let OS some time to actually > start the process, and not intended to allow time-consuming work > to happen on a worker process start. > > In general, no time-consuming operations are expected to be done > during a worker process start. Instead, time-consuming > preparatory work is expected to happen in the context of the > master process during configuration parsing and init module hooks. > If a module does something time-consuming in the init process > hook, it might be a good idea to change the module logic. > > Unless there is something more specific than a "the init_prcess > hook of some 3rd modules may be time-consuming", I would rather > reject the patch, as it introduces unneeded user-level complexity > by adding a directive, and encourages bad module writing practice. > > Nevertheless, thank you for the patch. > > -- > Maxim Dounin > http://nginx.org/ > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel > -------------- next part -------------- An HTML attachment was scrubbed... URL: From hucong.c at foxmail.com Mon Jun 19 15:43:55 2017 From: hucong.c at foxmail.com (=?utf-8?B?6IOh6IGqIChodWNjKQ==?=) Date: Mon, 19 Jun 2017 23:43:55 +0800 Subject: nginx development guide Message-ID: Hi, On Thursday, Jun 8, 2017 0:42 AM +0300, Vladimir Homutov wrote: >07.06.2017 17:00, ?? (hucc) ?????: >> Hello, >> >> There are two possible errors in >> http://nginx.org/en/docs/dev/development_guide.html#http_load_balancing >> >>> init(r, us) ? initializes per-request ngx_http_upstream_peer_t.peer (not to be confused with the >>> ngx_http_upstream_srv_conf_t.peer described above which is per-upstream) structure that is used >>> for load balancing. It will be passed as data argument to all callbacks that deal with server selection. >> >> Maybe is "initializes some fields (data, get, free, etc.) of per-request ngx_http_upstream_t.peer (...) " > >this details are described below in the text Please reconfirm this part, I firmly believe it is inconsistent with the meaning of the source code. Actually, I think this part should be rewritten?because it is confusing. Best wishes -hucc From hucong.c at foxmail.com Mon Jun 19 16:56:15 2017 From: hucong.c at foxmail.com (=?utf-8?B?6IOh6IGqIChodWNjKQ==?=) Date: Tue, 20 Jun 2017 00:56:15 +0800 Subject: [patch] Http image_filter: return 405 when method is HEAD and body is empty. Message-ID: Hi, Returning 415 does not conform to the HTTP protocol when image and proxy_pass configured in same location. # HG changeset patch # User hucongcong # Date 1497890354 -28800 # Tue Jun 20 00:39:14 2017 +0800 # Node ID af3a94de6a6549dec5e1205514eda1893313a14c # Parent d1816a2696de8c2faa1cd913a151e5f62a8620f3 Http image_filter: return 405 when method is HEAD and body is empty. diff -r d1816a2696de -r af3a94de6a65 src/http/modules/ngx_http_image_filter_module.c --- a/src/http/modules/ngx_http_image_filter_module.c Fri Jun 16 18:15:58 2017 +0300 +++ b/src/http/modules/ngx_http_image_filter_module.c Tue Jun 20 00:39:14 2017 +0800 @@ -330,6 +330,12 @@ ngx_http_image_body_filter(ngx_http_requ } } + if (r->method & NGX_HTTP_HEAD) { + return ngx_http_filter_finalize_request(r, + &ngx_http_image_filter_module, + NGX_HTTP_NOT_ALLOWED); + } + return ngx_http_filter_finalize_request(r, &ngx_http_image_filter_module, NGX_HTTP_UNSUPPORTED_MEDIA_TYPE); From mdounin at mdounin.ru Mon Jun 19 17:21:15 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Mon, 19 Jun 2017 20:21:15 +0300 Subject: [patch] Http image_filter: return 405 when method is HEAD and body is empty. In-Reply-To: References: Message-ID: <20170619172115.GN55433@mdounin.ru> Hello! On Tue, Jun 20, 2017 at 12:56:15AM +0800, ?? (hucc) wrote: > Returning 415 does not conform to the HTTP protocol when image and proxy_pass > configured in same location. > > # HG changeset patch > # User hucongcong > # Date 1497890354 -28800 > # Tue Jun 20 00:39:14 2017 +0800 > # Node ID af3a94de6a6549dec5e1205514eda1893313a14c > # Parent d1816a2696de8c2faa1cd913a151e5f62a8620f3 > Http image_filter: return 405 when method is HEAD and body is empty. > > diff -r d1816a2696de -r af3a94de6a65 src/http/modules/ngx_http_image_filter_module.c > --- a/src/http/modules/ngx_http_image_filter_module.c Fri Jun 16 18:15:58 2017 +0300 > +++ b/src/http/modules/ngx_http_image_filter_module.c Tue Jun 20 00:39:14 2017 +0800 > @@ -330,6 +330,12 @@ ngx_http_image_body_filter(ngx_http_requ > } > } > > + if (r->method & NGX_HTTP_HEAD) { > + return ngx_http_filter_finalize_request(r, > + &ngx_http_image_filter_module, > + NGX_HTTP_NOT_ALLOWED); > + } > + > return ngx_http_filter_finalize_request(r, > &ngx_http_image_filter_module, > NGX_HTTP_UNSUPPORTED_MEDIA_TYPE); Please clarify why you think that the current code is wrong. I don't see any problems with returning 415 to HEAD requests as long we are going to return 415 to GETs. -- Maxim Dounin http://nginx.org/ From mdounin at mdounin.ru Mon Jun 19 17:34:23 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Mon, 19 Jun 2017 20:34:23 +0300 Subject: [PATCH] Proxy: add "proxy_ssl_alpn" directive In-Reply-To: References: <7733d946e2651a2486a5.1496545442@piotrsikora.sfo.corp.google.com> <20170608163207.GZ55433@mdounin.ru> Message-ID: <20170619173423.GO55433@mdounin.ru> Hello! On Mon, Jun 19, 2017 at 07:31:11AM -0700, Piotr Sikora via nginx-devel wrote: > Hey Maxim, > > > It doesn't look like this patch make sense by its own. > > > > If this patch is a part of a larger work, please consider > > submitting a patch series with the whole feature instead. > > Individual patches which doesn't make much sense > > by its own are hard to review, and are likely to be rejected. > > Actually, it does, the only missing part (from HTTP/2 patchset) is: > > case NGX_HTTP_VERSION_11: > ngx_str_set(&alpn, NGX_HTTP_11_ALPN_ADVERTISE); > break; > + > +#if (NGX_HTTP_V2) > + case NGX_HTTP_VERSION_20: > + ngx_str_set(&alpn, NGX_HTTP_V2_ALPN_ADVERTISE); > + break; > +#endif > } > > if (ngx_ssl_alpn_protos(cf, plcf->upstream.ssl, &alpn) != NGX_OK) { > > ...so this patch is perfectly reviewable on its own. > > In case you expected ALPN-negotiation - I didn't add it, since it > would require rewrite of upstream logic, i.e. u->create_request() > would need to be called after upstream is already connected, and > possibly again in case of retries that negotiated different ALPN > protocol, which would add complexity to the already big patchset. > Also, I'm not sure how useful this is for upstream connections in > reverse proxies. > > Let me know if that's a must-have, but IMHO we could always add > "proxy_http_version alpn" in the future, without blocking this and > HTTP/2 patchset, which effectively implement "prior knowledge". I don't see how this patch is useful by its own, without additional HTTP/2 patchset, and it clearly adds additional user-visible complexity to the proxy module. And hence the suggestion is to include this patch into the HTTP/2 patchset as it is clearly a part of this patchset. Note well that at this point it might be a good idea to implement HTTP/2 to upstreams as a separate module, as it is done for other protocols like FastCGI, SCGI, uWSGI, and so on, instead of trying to built it into the proxy module which is intended to talk HTTP, not HTTP/2. It will better follow the generic concept we use now, "one protocol - one module", and will also imply much less blocking in general. -- Maxim Dounin http://nginx.org/ From hucong.c at foxmail.com Mon Jun 19 18:07:38 2017 From: hucong.c at foxmail.com (=?utf-8?B?6IOh6IGqIChodWNjKQ==?=) Date: Tue, 20 Jun 2017 02:07:38 +0800 Subject: [patch] Http image_filter: return 405 when method is HEAD andbody is empty. In-Reply-To: <20170619172115.GN55433@mdounin.ru> References: <20170619172115.GN55433@mdounin.ru> Message-ID: Hi, On Tuesday, Jun 20, 2017 1:21 AM +0300, Maxim Dounin wrote: >On Tue, Jun 20, 2017 at 12:56:15AM +0800, ?? (hucc) wrote: > >> Returning 415 does not conform to the HTTP protocol when image and proxy_pass >> configured in same location. >> >> # HG changeset patch >> # User hucongcong >> # Date 1497890354 -28800 >> # Tue Jun 20 00:39:14 2017 +0800 >> # Node ID af3a94de6a6549dec5e1205514eda1893313a14c >> # Parent d1816a2696de8c2faa1cd913a151e5f62a8620f3 >> Http image_filter: return 405 when method is HEAD and body is empty. >> >> diff -r d1816a2696de -r af3a94de6a65 src/http/modules/ngx_http_image_filter_module.c >> --- a/src/http/modules/ngx_http_image_filter_module.c Fri Jun 16 18:15:58 2017 +0300 >> +++ b/src/http/modules/ngx_http_image_filter_module.c Tue Jun 20 00:39:14 2017 +0800 >> @@ -330,6 +330,12 @@ ngx_http_image_body_filter(ngx_http_requ >> } >> } >> >> + if (r->method & NGX_HTTP_HEAD) { >> + return ngx_http_filter_finalize_request(r, >> + &ngx_http_image_filter_module, >> + NGX_HTTP_NOT_ALLOWED); >> + } >> + >> return ngx_http_filter_finalize_request(r, >> &ngx_http_image_filter_module, >> NGX_HTTP_UNSUPPORTED_MEDIA_TYPE); > >Please clarify why you think that the current code is wrong. I >don't see any problems with returning 415 to HEAD requests as long >we are going to return 415 to GETs. Ok, the problem is that nginx will return 200 to GET request and 415 to HEAD request. The configuration looks like: #proxy_method GET;#not configured location / { image resize 180 360; #... proxy_pass http://test_upstream$uri; } Best wishes -hucc From hongzhidao at gmail.com Mon Jun 19 18:25:14 2017 From: hongzhidao at gmail.com (=?UTF-8?B?5rSq5b+X6YGT?=) Date: Tue, 20 Jun 2017 02:25:14 +0800 Subject: [nginx] slice module issue Message-ID: Hi! Have a look at the following example first. server { listen 80; location / { slice 10; proxy_set_header Range $slice_range; proxy_pass http://127.0.0.1:81; } } server { listen 81; root html; } Then we start a request with curl. > curl http://my.test.com/ -x 127.1:80 -H "range: bytes=1-50, 2-51" We get a response of the whole file that differs from expectation (1-50, 2-51). It seems that slice module doesn't support multi-range (separated by commas), but it's confused $slice_range variable is valid. Please confirm this question and the following patch, thanks! diff -r 5e05118678af src/http/modules/ngx_http_slice_filter_module.c --- a/src/http/modules/ngx_http_slice_filter_module.c Mon May 29 23:33:38 2017 +0300 +++ b/src/http/modules/ngx_http_slice_filter_module.c Mon Jun 19 09:35:24 2017 -0400 @@ -389,6 +389,7 @@ ngx_http_variable_value_t *v, uintptr_t data) { u_char *p; + off_t start; ngx_http_slice_ctx_t *ctx; ngx_http_slice_loc_conf_t *slcf; @@ -407,6 +408,13 @@ return NGX_OK; } + start = ngx_http_slice_get_start(r); + + if (start == -1) { + v->not_found = 1; + return NGX_OK; + } + ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_slice_ctx_t)); if (ctx == NULL) { return NGX_ERROR; @@ -419,7 +427,7 @@ return NGX_ERROR; } - ctx->start = slcf->size * (ngx_http_slice_get_start(r) / slcf->size); + ctx->start = slcf->size * (start / slcf->size); ctx->range.data = p; ctx->range.len = ngx_sprintf(p, "bytes=%O-%O", ctx->start, @@ -460,7 +468,7 @@ p = h->value.data + 6; if (ngx_strchr(p, ',')) { - return 0; + return -1; } while (*p == ' ') { p++; } And this is a better conf. map $slice_range $x_slice_range { default $http_range; ~ $slice_range; } server { listen 80; location / { slice 10; proxy_set_header Range $x_slice_range; proxy_pass http://127.0.0.1:81; } } -------------- next part -------------- An HTML attachment was scrubbed... URL: From hucong.c at foxmail.com Mon Jun 19 18:25:45 2017 From: hucong.c at foxmail.com (=?utf-8?B?6IOh6IGqIChodWNjKQ==?=) Date: Tue, 20 Jun 2017 02:25:45 +0800 Subject: [patch] Slice filter: support for empty file. Message-ID: Hi, 416 will be returned when the request has no Range HEADER and the target file is empty. Apparently, it does not conform to the HTTP protocol. Empty file seems inevitable in the CDN service where Nginx is heavily used. # HG changeset patch # User hucongcong # Date 1497892764 -28800 # Tue Jun 20 01:19:24 2017 +0800 # Node ID 79d38b2d27d4eb92395cf1ff43bbfe23498bc69a # Parent d1816a2696de8c2faa1cd913a151e5f62a8620f3 Slice filter: support for empty file. diff -r d1816a2696de -r 79d38b2d27d4 src/http/modules/ngx_http_slice_filter_module.c --- a/src/http/modules/ngx_http_slice_filter_module.c Fri Jun 16 18:15:58 2017 +0300 +++ b/src/http/modules/ngx_http_slice_filter_module.c Tue Jun 20 01:19:24 2017 +0800 @@ -22,6 +22,7 @@ typedef struct { ngx_str_t etag; unsigned last:1; unsigned active:1; + unsigned no_range:1; ngx_http_request_t *sr; } ngx_http_slice_ctx_t; @@ -114,7 +115,21 @@ ngx_http_slice_header_filter(ngx_http_re } if (r->headers_out.status != NGX_HTTP_PARTIAL_CONTENT) { + if (r == r->main) { + if (ctx->no_range + && r->headers_out.status == NGX_HTTP_RANGE_NOT_SATISFIABLE) + { + r->header_only = 1; + ngx_str_null(&r->headers_out.content_type); + + r->headers_out.status = NGX_HTTP_OK; + r->headers_out.status_line.len = 0; + r->headers_out.content_length_n = 0; + r->headers_out.content_range->hash = 0; + r->headers_out.content_range = NULL; + } + ngx_http_set_ctx(r, NULL, ngx_http_slice_filter_module); return ngx_http_next_header_filter(r); } @@ -440,9 +455,10 @@ ngx_http_slice_range_variable(ngx_http_r static off_t ngx_http_slice_get_start(ngx_http_request_t *r) { - off_t start, cutoff, cutlim; - u_char *p; - ngx_table_elt_t *h; + off_t start, cutoff, cutlim; + u_char *p; + ngx_table_elt_t *h; + ngx_http_slice_ctx_t *ctx; if (r->headers_in.if_range) { return 0; @@ -454,6 +470,8 @@ ngx_http_slice_get_start(ngx_http_reques || h->value.len < 7 || ngx_strncasecmp(h->value.data, (u_char *) "bytes=", 6) != 0) { + ctx = ngx_http_get_module_ctx(r, ngx_http_slice_filter_module); + ctx->no_range = 1; return 0; } From hucong.c at foxmail.com Tue Jun 20 02:39:44 2017 From: hucong.c at foxmail.com (=?utf-8?B?6IOh6IGqIChodWNjKQ==?=) Date: Tue, 20 Jun 2017 10:39:44 +0800 Subject: [patch] Slice filter: support for empty file. In-Reply-To: References: Message-ID: Hi, >416 will be returned when the request has no Range HEADER and the target file is >empty. Apparently, it does not conform to the HTTP protocol. Empty file seems >inevitable in the CDN service where Nginx is heavily used. > ># HG changeset patch ># User hucongcong ># Date 1497892764 -28800 ># Tue Jun 20 01:19:24 2017 +0800 ># Node ID 79d38b2d27d4eb92395cf1ff43bbfe23498bc69a ># Parent d1816a2696de8c2faa1cd913a151e5f62a8620f3 >Slice filter: support for empty file. The following modifications may be more appropriate. # HG changeset patch # User hucongcong # Date 1497926137 -28800 # Tue Jun 20 10:35:37 2017 +0800 # Node ID e42abb7e28c4f0dd3d66cb81aa2623e7fae8b4da # Parent a39bc74873faf9e5bea616561b43f6ecc55229f9 Slice filter: support for empty file. diff -r a39bc74873fa -r e42abb7e28c4 src/http/modules/ngx_http_slice_filter_module.c --- a/src/http/modules/ngx_http_slice_filter_module.c Mon Jun 19 14:25:42 2017 +0300 +++ b/src/http/modules/ngx_http_slice_filter_module.c Tue Jun 20 10:35:37 2017 +0800 @@ -114,7 +114,21 @@ ngx_http_slice_header_filter(ngx_http_re } if (r->headers_out.status != NGX_HTTP_PARTIAL_CONTENT) { + if (r == r->main) { + if (r->headers_in.range == NULL + && r->headers_out.status == NGX_HTTP_RANGE_NOT_SATISFIABLE) + { + r->header_only = 1; + ngx_str_null(&r->headers_out.content_type); + + r->headers_out.status = NGX_HTTP_OK; + r->headers_out.status_line.len = 0; + r->headers_out.content_length_n = 0; + r->headers_out.content_range->hash = 0; + r->headers_out.content_range = NULL; + } + ngx_http_set_ctx(r, NULL, ngx_http_slice_filter_module); return ngx_http_next_header_filter(r); } From arut at nginx.com Tue Jun 20 12:09:53 2017 From: arut at nginx.com (Roman Arutyunyan) Date: Tue, 20 Jun 2017 15:09:53 +0300 Subject: [nginx] slice module issue In-Reply-To: References: Message-ID: <20170620120953.GD470@Romans-MacBook-Air.local> Hi, On Tue, Jun 20, 2017 at 02:25:14AM +0800, ??? wrote: > Hi! > > Have a look at the following example first. > > server { > listen 80; > > location / { > slice 10; > proxy_set_header Range $slice_range; > proxy_pass http://127.0.0.1:81; > } > } > > > server { > listen 81; > root html; > } > > Then we start a request with curl. > > curl http://my.test.com/ -x 127.1:80 -H "range: bytes=1-50, 2-51" > > We get a response of the whole file that differs from expectation (1-50, > 2-51). > > It seems that slice module doesn't support multi-range (separated by > commas), Yes, the slice module does not support multi-range. The entire file is proxied and processed by the standard range module, which has limited multi-range support too. Particularly, multi-range is supported only when returning an entire file from disk. > but it's confused $slice_range variable is valid. > > Please confirm this question and the following patch, thanks! > > > diff -r 5e05118678af src/http/modules/ngx_http_slice_filter_module.c > --- a/src/http/modules/ngx_http_slice_filter_module.c Mon May 29 23:33:38 > 2017 +0300 > +++ b/src/http/modules/ngx_http_slice_filter_module.c Mon Jun 19 09:35:24 > 2017 -0400 > @@ -389,6 +389,7 @@ > ngx_http_variable_value_t *v, uintptr_t data) > { > u_char *p; > + off_t start; > ngx_http_slice_ctx_t *ctx; > ngx_http_slice_loc_conf_t *slcf; > > @@ -407,6 +408,13 @@ > return NGX_OK; > } > > + start = ngx_http_slice_get_start(r); > + > + if (start == -1) { > + v->not_found = 1; > + return NGX_OK; > + } > + > ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_slice_ctx_t)); > if (ctx == NULL) { > return NGX_ERROR; > @@ -419,7 +427,7 @@ > return NGX_ERROR; > } > > - ctx->start = slcf->size * (ngx_http_slice_get_start(r) / > slcf->size); > + ctx->start = slcf->size * (start / slcf->size); > > ctx->range.data = p; > ctx->range.len = ngx_sprintf(p, "bytes=%O-%O", ctx->start, > @@ -460,7 +468,7 @@ > p = h->value.data + 6; > > if (ngx_strchr(p, ',')) { > - return 0; > + return -1; > } > > while (*p == ' ') { p++; } > > > And this is a better conf. > > map $slice_range $x_slice_range { > default $http_range; > ~ $slice_range; > } > > server { > listen 80; > > location / { > slice 10; > proxy_set_header Range $x_slice_range; > proxy_pass http://127.0.0.1:81; > } > } > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel -- Roman Arutyunyan From hongzhidao at gmail.com Tue Jun 20 12:42:30 2017 From: hongzhidao at gmail.com (=?UTF-8?B?5rSq5b+X6YGT?=) Date: Tue, 20 Jun 2017 12:42:30 +0000 Subject: [nginx] slice module issue In-Reply-To: <20170620120953.GD470@Romans-MacBook-Air.local> References: <20170620120953.GD470@Romans-MacBook-Air.local> Message-ID: Do you think it's better to set $slice_range not found as if multi-range request? Roman Arutyunyan ?2017?6?20? ??20:09??? > Hi, > > On Tue, Jun 20, 2017 at 02:25:14AM +0800, ??? wrote: > > Hi! > > > > Have a look at the following example first. > > > > server { > > listen 80; > > > > location / { > > slice 10; > > proxy_set_header Range $slice_range; > > proxy_pass http://127.0.0.1:81; > > } > > } > > > > > > server { > > listen 81; > > root html; > > } > > > > Then we start a request with curl. > > > curl http://my.test.com/ -x 127.1:80 -H "range: bytes=1-50, 2-51" > > > > We get a response of the whole file that differs from expectation (1-50, > > 2-51). > > > > It seems that slice module doesn't support multi-range (separated by > > commas), > > Yes, the slice module does not support multi-range. > The entire file is proxied and processed by the standard range module, > which has limited multi-range support too. Particularly, multi-range is > supported only when returning an entire file from disk. > > > but it's confused $slice_range variable is valid. > > > > Please confirm this question and the following patch, thanks! > > > > > > diff -r 5e05118678af src/http/modules/ngx_http_slice_filter_module.c > > --- a/src/http/modules/ngx_http_slice_filter_module.c Mon May 29 23:33:38 > > 2017 +0300 > > +++ b/src/http/modules/ngx_http_slice_filter_module.c Mon Jun 19 09:35:24 > > 2017 -0400 > > @@ -389,6 +389,7 @@ > > ngx_http_variable_value_t *v, uintptr_t data) > > { > > u_char *p; > > + off_t start; > > ngx_http_slice_ctx_t *ctx; > > ngx_http_slice_loc_conf_t *slcf; > > > > @@ -407,6 +408,13 @@ > > return NGX_OK; > > } > > > > + start = ngx_http_slice_get_start(r); > > + > > + if (start == -1) { > > + v->not_found = 1; > > + return NGX_OK; > > + } > > + > > ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_slice_ctx_t)); > > if (ctx == NULL) { > > return NGX_ERROR; > > @@ -419,7 +427,7 @@ > > return NGX_ERROR; > > } > > > > - ctx->start = slcf->size * (ngx_http_slice_get_start(r) / > > slcf->size); > > + ctx->start = slcf->size * (start / slcf->size); > > > > ctx->range.data = p; > > ctx->range.len = ngx_sprintf(p, "bytes=%O-%O", ctx->start, > > @@ -460,7 +468,7 @@ > > p = h->value.data + 6; > > > > if (ngx_strchr(p, ',')) { > > - return 0; > > + return -1; > > } > > > > while (*p == ' ') { p++; } > > > > > > And this is a better conf. > > > > map $slice_range $x_slice_range { > > default $http_range; > > ~ $slice_range; > > } > > > > server { > > listen 80; > > > > location / { > > slice 10; > > proxy_set_header Range $x_slice_range; > > proxy_pass http://127.0.0.1:81; > > } > > } > > > _______________________________________________ > > nginx-devel mailing list > > nginx-devel at nginx.org > > http://mailman.nginx.org/mailman/listinfo/nginx-devel > > > -- > Roman Arutyunyan > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel -------------- next part -------------- An HTML attachment was scrubbed... URL: From arut at nginx.com Tue Jun 20 13:34:49 2017 From: arut at nginx.com (Roman Arutyunyan) Date: Tue, 20 Jun 2017 16:34:49 +0300 Subject: [nginx] slice module issue In-Reply-To: References: <20170620120953.GD470@Romans-MacBook-Air.local> Message-ID: <20170620133449.GE470@Romans-MacBook-Air.local> That would disable slicing at all. On Tue, Jun 20, 2017 at 12:42:30PM +0000, ??? wrote: > Do you think it's better to set $slice_range not found as if multi-range > request? > > Roman Arutyunyan ?2017?6?20? ??20:09??? > > > Hi, > > > > On Tue, Jun 20, 2017 at 02:25:14AM +0800, ??? wrote: > > > Hi! > > > > > > Have a look at the following example first. > > > > > > server { > > > listen 80; > > > > > > location / { > > > slice 10; > > > proxy_set_header Range $slice_range; > > > proxy_pass http://127.0.0.1:81; > > > } > > > } > > > > > > > > > server { > > > listen 81; > > > root html; > > > } > > > > > > Then we start a request with curl. > > > > curl http://my.test.com/ -x 127.1:80 -H "range: bytes=1-50, 2-51" > > > > > > We get a response of the whole file that differs from expectation (1-50, > > > 2-51). > > > > > > It seems that slice module doesn't support multi-range (separated by > > > commas), > > > > Yes, the slice module does not support multi-range. > > The entire file is proxied and processed by the standard range module, > > which has limited multi-range support too. Particularly, multi-range is > > supported only when returning an entire file from disk. > > > > > but it's confused $slice_range variable is valid. > > > > > > Please confirm this question and the following patch, thanks! > > > > > > > > > diff -r 5e05118678af src/http/modules/ngx_http_slice_filter_module.c > > > --- a/src/http/modules/ngx_http_slice_filter_module.c Mon May 29 23:33:38 > > > 2017 +0300 > > > +++ b/src/http/modules/ngx_http_slice_filter_module.c Mon Jun 19 09:35:24 > > > 2017 -0400 > > > @@ -389,6 +389,7 @@ > > > ngx_http_variable_value_t *v, uintptr_t data) > > > { > > > u_char *p; > > > + off_t start; > > > ngx_http_slice_ctx_t *ctx; > > > ngx_http_slice_loc_conf_t *slcf; > > > > > > @@ -407,6 +408,13 @@ > > > return NGX_OK; > > > } > > > > > > + start = ngx_http_slice_get_start(r); > > > + > > > + if (start == -1) { > > > + v->not_found = 1; > > > + return NGX_OK; > > > + } > > > + > > > ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_slice_ctx_t)); > > > if (ctx == NULL) { > > > return NGX_ERROR; > > > @@ -419,7 +427,7 @@ > > > return NGX_ERROR; > > > } > > > > > > - ctx->start = slcf->size * (ngx_http_slice_get_start(r) / > > > slcf->size); > > > + ctx->start = slcf->size * (start / slcf->size); > > > > > > ctx->range.data = p; > > > ctx->range.len = ngx_sprintf(p, "bytes=%O-%O", ctx->start, > > > @@ -460,7 +468,7 @@ > > > p = h->value.data + 6; > > > > > > if (ngx_strchr(p, ',')) { > > > - return 0; > > > + return -1; > > > } > > > > > > while (*p == ' ') { p++; } > > > > > > > > > And this is a better conf. > > > > > > map $slice_range $x_slice_range { > > > default $http_range; > > > ~ $slice_range; > > > } > > > > > > server { > > > listen 80; > > > > > > location / { > > > slice 10; > > > proxy_set_header Range $x_slice_range; > > > proxy_pass http://127.0.0.1:81; > > > } > > > } > > > > > _______________________________________________ > > > nginx-devel mailing list > > > nginx-devel at nginx.org > > > http://mailman.nginx.org/mailman/listinfo/nginx-devel > > > > > > -- > > Roman Arutyunyan > > _______________________________________________ > > nginx-devel mailing list > > nginx-devel at nginx.org > > http://mailman.nginx.org/mailman/listinfo/nginx-devel > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel -- Roman Arutyunyan From xeioex at nginx.com Tue Jun 20 14:19:37 2017 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Tue, 20 Jun 2017 14:19:37 +0000 Subject: [njs] Fixed Object.prototype.isPrototypeOf() without arguments. Message-ID: details: http://hg.nginx.org/njs/rev/1c4d7281d44e branches: changeset: 372:1c4d7281d44e user: Dmitry Volyntsev date: Tue Jun 20 17:12:44 2017 +0300 description: Fixed Object.prototype.isPrototypeOf() without arguments. diffstat: njs/njs_object.c | 2 +- njs/test/njs_unit_test.c | 3 +++ 2 files changed, 4 insertions(+), 1 deletions(-) diffs (25 lines): diff -r bcd7a7256805 -r 1c4d7281d44e njs/njs_object.c --- a/njs/njs_object.c Mon Jun 19 14:46:46 2017 +0300 +++ b/njs/njs_object.c Tue Jun 20 17:12:44 2017 +0300 @@ -1413,7 +1413,7 @@ njs_object_prototype_is_prototype_of(njs retval = &njs_string_false; - if (njs_is_object(&args[0]) && njs_is_object(&args[1])) { + if (nargs > 1 && njs_is_object(&args[0]) && njs_is_object(&args[1])) { proto = args[0].data.u.object; object = args[1].data.u.object; diff -r bcd7a7256805 -r 1c4d7281d44e njs/test/njs_unit_test.c --- a/njs/test/njs_unit_test.c Mon Jun 19 14:46:46 2017 +0300 +++ b/njs/test/njs_unit_test.c Tue Jun 20 17:12:44 2017 +0300 @@ -6030,6 +6030,9 @@ static njs_unit_test_t njs_test[] = "o.isPrototypeOf()"), nxt_string("false") }, + { nxt_string("Object.valueOf.isPrototypeOf()"), + nxt_string("false") }, + { nxt_string("var p = {}; var o = Object.create(p);" "o.isPrototypeOf(1)"), nxt_string("false") }, From mdounin at mdounin.ru Tue Jun 20 15:01:17 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Tue, 20 Jun 2017 18:01:17 +0300 Subject: [patch] Slice filter: support for empty file. In-Reply-To: References: Message-ID: <20170620150117.GS55433@mdounin.ru> Hello! On Tue, Jun 20, 2017 at 10:39:44AM +0800, ?? (hucc) wrote: > Hi, > > >416 will be returned when the request has no Range HEADER and the target file is > >empty. Apparently, it does not conform to the HTTP protocol. Empty file seems > >inevitable in the CDN service where Nginx is heavily used. > > > ># HG changeset patch > ># User hucongcong > ># Date 1497892764 -28800 > ># Tue Jun 20 01:19:24 2017 +0800 > ># Node ID 79d38b2d27d4eb92395cf1ff43bbfe23498bc69a > ># Parent d1816a2696de8c2faa1cd913a151e5f62a8620f3 > >Slice filter: support for empty file. > > The following modifications may be more appropriate. > > # HG changeset patch > # User hucongcong > # Date 1497926137 -28800 > # Tue Jun 20 10:35:37 2017 +0800 > # Node ID e42abb7e28c4f0dd3d66cb81aa2623e7fae8b4da > # Parent a39bc74873faf9e5bea616561b43f6ecc55229f9 > Slice filter: support for empty file. > > diff -r a39bc74873fa -r e42abb7e28c4 src/http/modules/ngx_http_slice_filter_module.c > --- a/src/http/modules/ngx_http_slice_filter_module.c Mon Jun 19 14:25:42 2017 +0300 > +++ b/src/http/modules/ngx_http_slice_filter_module.c Tue Jun 20 10:35:37 2017 +0800 > @@ -114,7 +114,21 @@ ngx_http_slice_header_filter(ngx_http_re > } > > if (r->headers_out.status != NGX_HTTP_PARTIAL_CONTENT) { > + > if (r == r->main) { > + if (r->headers_in.range == NULL > + && r->headers_out.status == NGX_HTTP_RANGE_NOT_SATISFIABLE) > + { > + r->header_only = 1; > + ngx_str_null(&r->headers_out.content_type); > + > + r->headers_out.status = NGX_HTTP_OK; > + r->headers_out.status_line.len = 0; > + r->headers_out.content_length_n = 0; > + r->headers_out.content_range->hash = 0; > + r->headers_out.content_range = NULL; > + } > + > ngx_http_set_ctx(r, NULL, ngx_http_slice_filter_module); > return ngx_http_next_header_filter(r); > } Returning the 416 response with some headers deleted and status code changed to 200 doesn't look like a safe solution. On the other hand, changing nginx's range filter to allow range requests to 0-byte files should make this unneeded. I've reopened ticket #1031 which is about range requests to 0-byte files, see https://trac.nginx.org/nginx/ticket/1031. -- Maxim Dounin http://nginx.org/ From hongzhidao at gmail.com Tue Jun 20 15:21:50 2017 From: hongzhidao at gmail.com (=?UTF-8?B?5rSq5b+X6YGT?=) Date: Tue, 20 Jun 2017 15:21:50 +0000 Subject: [nginx] slice module issue In-Reply-To: <20170620133449.GE470@Romans-MacBook-Air.local> References: <20170620120953.GD470@Romans-MacBook-Air.local> <20170620133449.GE470@Romans-MacBook-Air.local> Message-ID: You said the module doesn't support multi-range, it means nothing to support slice feature through $slice_range. Anyway we can avoid it by access handle, but it's still unconvinient. Roman Arutyunyan ?2017?6?20? ??21:34??? > That would disable slicing at all. > > On Tue, Jun 20, 2017 at 12:42:30PM +0000, ??? wrote: > > Do you think it's better to set $slice_range not found as if multi-range > > request? > > > > Roman Arutyunyan ?2017?6?20? ??20:09??? > > > > > Hi, > > > > > > On Tue, Jun 20, 2017 at 02:25:14AM +0800, ??? wrote: > > > > Hi! > > > > > > > > Have a look at the following example first. > > > > > > > > server { > > > > listen 80; > > > > > > > > location / { > > > > slice 10; > > > > proxy_set_header Range $slice_range; > > > > proxy_pass http://127.0.0.1:81; > > > > } > > > > } > > > > > > > > > > > > server { > > > > listen 81; > > > > root html; > > > > } > > > > > > > > Then we start a request with curl. > > > > > curl http://my.test.com/ -x 127.1:80 -H "range: bytes=1-50, 2-51" > > > > > > > > We get a response of the whole file that differs from expectation > (1-50, > > > > 2-51). > > > > > > > > It seems that slice module doesn't support multi-range (separated by > > > > commas), > > > > > > Yes, the slice module does not support multi-range. > > > The entire file is proxied and processed by the standard range module, > > > which has limited multi-range support too. Particularly, multi-range > is > > > supported only when returning an entire file from disk. > > > > > > > but it's confused $slice_range variable is valid. > > > > > > > > Please confirm this question and the following patch, thanks! > > > > > > > > > > > > diff -r 5e05118678af src/http/modules/ngx_http_slice_filter_module.c > > > > --- a/src/http/modules/ngx_http_slice_filter_module.c Mon May 29 > 23:33:38 > > > > 2017 +0300 > > > > +++ b/src/http/modules/ngx_http_slice_filter_module.c Mon Jun 19 > 09:35:24 > > > > 2017 -0400 > > > > @@ -389,6 +389,7 @@ > > > > ngx_http_variable_value_t *v, uintptr_t data) > > > > { > > > > u_char *p; > > > > + off_t start; > > > > ngx_http_slice_ctx_t *ctx; > > > > ngx_http_slice_loc_conf_t *slcf; > > > > > > > > @@ -407,6 +408,13 @@ > > > > return NGX_OK; > > > > } > > > > > > > > + start = ngx_http_slice_get_start(r); > > > > + > > > > + if (start == -1) { > > > > + v->not_found = 1; > > > > + return NGX_OK; > > > > + } > > > > + > > > > ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_slice_ctx_t)); > > > > if (ctx == NULL) { > > > > return NGX_ERROR; > > > > @@ -419,7 +427,7 @@ > > > > return NGX_ERROR; > > > > } > > > > > > > > - ctx->start = slcf->size * (ngx_http_slice_get_start(r) / > > > > slcf->size); > > > > + ctx->start = slcf->size * (start / slcf->size); > > > > > > > > ctx->range.data = p; > > > > ctx->range.len = ngx_sprintf(p, "bytes=%O-%O", ctx->start, > > > > @@ -460,7 +468,7 @@ > > > > p = h->value.data + 6; > > > > > > > > if (ngx_strchr(p, ',')) { > > > > - return 0; > > > > + return -1; > > > > } > > > > > > > > while (*p == ' ') { p++; } > > > > > > > > > > > > And this is a better conf. > > > > > > > > map $slice_range $x_slice_range { > > > > default $http_range; > > > > ~ $slice_range; > > > > } > > > > > > > > server { > > > > listen 80; > > > > > > > > location / { > > > > slice 10; > > > > proxy_set_header Range $x_slice_range; > > > > proxy_pass http://127.0.0.1:81; > > > > } > > > > } > > > > > > > _______________________________________________ > > > > nginx-devel mailing list > > > > nginx-devel at nginx.org > > > > http://mailman.nginx.org/mailman/listinfo/nginx-devel > > > > > > > > > -- > > > Roman Arutyunyan > > > _______________________________________________ > > > nginx-devel mailing list > > > nginx-devel at nginx.org > > > http://mailman.nginx.org/mailman/listinfo/nginx-devel > > > _______________________________________________ > > nginx-devel mailing list > > nginx-devel at nginx.org > > http://mailman.nginx.org/mailman/listinfo/nginx-devel > > > -- > Roman Arutyunyan > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel -------------- next part -------------- An HTML attachment was scrubbed... URL: From hongzhidao at gmail.com Tue Jun 20 15:47:02 2017 From: hongzhidao at gmail.com (=?UTF-8?B?5rSq5b+X6YGT?=) Date: Tue, 20 Jun 2017 15:47:02 +0000 Subject: [nginx] slice module issue In-Reply-To: References: <20170620120953.GD470@Romans-MacBook-Air.local> <20170620133449.GE470@Romans-MacBook-Air.local> Message-ID: If we wan't to slice in the case of multi-page, how to achieve it? ??? ?2017?6?20? ??23:21??? > You said the module doesn't support multi-range, it means nothing to > support slice feature through $slice_range. > Anyway we can avoid it by access handle, but it's still unconvinient. > > Roman Arutyunyan ?2017?6?20? ??21:34??? > >> That would disable slicing at all. >> >> On Tue, Jun 20, 2017 at 12:42:30PM +0000, ??? wrote: >> > Do you think it's better to set $slice_range not found as if multi-range >> > request? >> > >> > Roman Arutyunyan ?2017?6?20? ??20:09??? >> > >> > > Hi, >> > > >> > > On Tue, Jun 20, 2017 at 02:25:14AM +0800, ??? wrote: >> > > > Hi! >> > > > >> > > > Have a look at the following example first. >> > > > >> > > > server { >> > > > listen 80; >> > > > >> > > > location / { >> > > > slice 10; >> > > > proxy_set_header Range $slice_range; >> > > > proxy_pass http://127.0.0.1:81; >> > > > } >> > > > } >> > > > >> > > > >> > > > server { >> > > > listen 81; >> > > > root html; >> > > > } >> > > > >> > > > Then we start a request with curl. >> > > > > curl http://my.test.com/ -x 127.1:80 -H "range: bytes=1-50, 2-51" >> > > > >> > > > We get a response of the whole file that differs from expectation >> (1-50, >> > > > 2-51). >> > > > >> > > > It seems that slice module doesn't support multi-range (separated by >> > > > commas), >> > > >> > > Yes, the slice module does not support multi-range. >> > > The entire file is proxied and processed by the standard range module, >> > > which has limited multi-range support too. Particularly, multi-range >> is >> > > supported only when returning an entire file from disk. >> > > >> > > > but it's confused $slice_range variable is valid. >> > > > >> > > > Please confirm this question and the following patch, thanks! >> > > > >> > > > >> > > > diff -r 5e05118678af src/http/modules/ngx_http_slice_filter_module.c >> > > > --- a/src/http/modules/ngx_http_slice_filter_module.c Mon May 29 >> 23:33:38 >> > > > 2017 +0300 >> > > > +++ b/src/http/modules/ngx_http_slice_filter_module.c Mon Jun 19 >> 09:35:24 >> > > > 2017 -0400 >> > > > @@ -389,6 +389,7 @@ >> > > > ngx_http_variable_value_t *v, uintptr_t data) >> > > > { >> > > > u_char *p; >> > > > + off_t start; >> > > > ngx_http_slice_ctx_t *ctx; >> > > > ngx_http_slice_loc_conf_t *slcf; >> > > > >> > > > @@ -407,6 +408,13 @@ >> > > > return NGX_OK; >> > > > } >> > > > >> > > > + start = ngx_http_slice_get_start(r); >> > > > + >> > > > + if (start == -1) { >> > > > + v->not_found = 1; >> > > > + return NGX_OK; >> > > > + } >> > > > + >> > > > ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_slice_ctx_t)); >> > > > if (ctx == NULL) { >> > > > return NGX_ERROR; >> > > > @@ -419,7 +427,7 @@ >> > > > return NGX_ERROR; >> > > > } >> > > > >> > > > - ctx->start = slcf->size * (ngx_http_slice_get_start(r) / >> > > > slcf->size); >> > > > + ctx->start = slcf->size * (start / slcf->size); >> > > > >> > > > ctx->range.data = p; >> > > > ctx->range.len = ngx_sprintf(p, "bytes=%O-%O", ctx->start, >> > > > @@ -460,7 +468,7 @@ >> > > > p = h->value.data + 6; >> > > > >> > > > if (ngx_strchr(p, ',')) { >> > > > - return 0; >> > > > + return -1; >> > > > } >> > > > >> > > > while (*p == ' ') { p++; } >> > > > >> > > > >> > > > And this is a better conf. >> > > > >> > > > map $slice_range $x_slice_range { >> > > > default $http_range; >> > > > ~ $slice_range; >> > > > } >> > > > >> > > > server { >> > > > listen 80; >> > > > >> > > > location / { >> > > > slice 10; >> > > > proxy_set_header Range $x_slice_range; >> > > > proxy_pass http://127.0.0.1:81; >> > > > } >> > > > } >> > > >> > > > _______________________________________________ >> > > > nginx-devel mailing list >> > > > nginx-devel at nginx.org >> > > > http://mailman.nginx.org/mailman/listinfo/nginx-devel >> > > >> > > >> > > -- >> > > Roman Arutyunyan >> > > _______________________________________________ >> > > nginx-devel mailing list >> > > nginx-devel at nginx.org >> > > http://mailman.nginx.org/mailman/listinfo/nginx-devel >> >> > _______________________________________________ >> > nginx-devel mailing list >> > nginx-devel at nginx.org >> > http://mailman.nginx.org/mailman/listinfo/nginx-devel >> >> >> -- >> Roman Arutyunyan >> _______________________________________________ >> nginx-devel mailing list >> nginx-devel at nginx.org >> http://mailman.nginx.org/mailman/listinfo/nginx-devel > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From mdounin at mdounin.ru Tue Jun 20 15:53:52 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Tue, 20 Jun 2017 18:53:52 +0300 Subject: [patch] Http image_filter: return 405 when method is HEAD andbody is empty. In-Reply-To: References: <20170619172115.GN55433@mdounin.ru> Message-ID: <20170620155352.GU55433@mdounin.ru> Hello! On Tue, Jun 20, 2017 at 02:07:38AM +0800, ?? (hucc) wrote: > Hi, > > On Tuesday, Jun 20, 2017 1:21 AM +0300, Maxim Dounin wrote: > > >On Tue, Jun 20, 2017 at 12:56:15AM +0800, ?? (hucc) wrote: > > > >> Returning 415 does not conform to the HTTP protocol when image and proxy_pass > >> configured in same location. > >> > >> # HG changeset patch > >> # User hucongcong > >> # Date 1497890354 -28800 > >> # Tue Jun 20 00:39:14 2017 +0800 > >> # Node ID af3a94de6a6549dec5e1205514eda1893313a14c > >> # Parent d1816a2696de8c2faa1cd913a151e5f62a8620f3 > >> Http image_filter: return 405 when method is HEAD and body is empty. > >> > >> diff -r d1816a2696de -r af3a94de6a65 src/http/modules/ngx_http_image_filter_module.c > >> --- a/src/http/modules/ngx_http_image_filter_module.c Fri Jun 16 18:15:58 2017 +0300 > >> +++ b/src/http/modules/ngx_http_image_filter_module.c Tue Jun 20 00:39:14 2017 +0800 > >> @@ -330,6 +330,12 @@ ngx_http_image_body_filter(ngx_http_requ > >> } > >> } > >> > >> + if (r->method & NGX_HTTP_HEAD) { > >> + return ngx_http_filter_finalize_request(r, > >> + &ngx_http_image_filter_module, > >> + NGX_HTTP_NOT_ALLOWED); > >> + } > >> + > >> return ngx_http_filter_finalize_request(r, > >> &ngx_http_image_filter_module, > >> NGX_HTTP_UNSUPPORTED_MEDIA_TYPE); > > > >Please clarify why you think that the current code is wrong. I > >don't see any problems with returning 415 to HEAD requests as long > >we are going to return 415 to GETs. > > Ok, the problem is that nginx will return 200 to GET request and > 415 to HEAD request. > > The configuration looks like: > #proxy_method GET;#not configured > location / { > image resize 180 360; > #... > proxy_pass http://test_upstream$uri; > } Ok, so the problem appears when using proxy_pass without additional configuration, image_filter, and a HEAD request. Unfortunately, the suggested change will also break the correct behaviour in other cases, for example, when serving static files. Using "proxy_method GET;" might be a better option. -- Maxim Dounin http://nginx.org/ From mdounin at mdounin.ru Tue Jun 20 15:56:34 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Tue, 20 Jun 2017 18:56:34 +0300 Subject: [PATCH] Output chain: propagate last_buf flag to c->send_chain() In-Reply-To: <3363bdf821c7110b5774.1497733184@piotrsikora.sfo.corp.google.com> References: <20170608162956.GY55433@mdounin.ru> <3363bdf821c7110b5774.1497733184@piotrsikora.sfo.corp.google.com> Message-ID: <20170620155634.GV55433@mdounin.ru> Hello! On Sat, Jun 17, 2017 at 01:59:44PM -0700, Piotr Sikora via nginx-devel wrote: > # HG changeset patch > # User Piotr Sikora > # Date 1491708381 25200 > # Sat Apr 08 20:26:21 2017 -0700 > # Node ID 3363bdf821c7110b577437bd59c962653f3a144f > # Parent d1816a2696de8c2faa1cd913a151e5f62a8620f3 > Output chain: propagate last_buf flag to c->send_chain(). > > Signed-off-by: Piotr Sikora [...] The patch itself looks fine, though I would prefer to consider it as a part of a series where relevant functionality is used. -- Maxim Dounin http://nginx.org/ From hucong.c at foxmail.com Tue Jun 20 16:28:23 2017 From: hucong.c at foxmail.com (=?utf-8?B?6IOh6IGqIChodWNjKQ==?=) Date: Wed, 21 Jun 2017 00:28:23 +0800 Subject: [patch] Http image_filter: return 405 when method is HEAD andbodyis empty. In-Reply-To: <20170620155352.GU55433@mdounin.ru> References: <20170619172115.GN55433@mdounin.ru> <20170620155352.GU55433@mdounin.ru> Message-ID: Hi, On Tuesday, Jun 20, 2017 11:53 PM +0300, Maxim Dounin wrote: >> >On Tue, Jun 20, 2017 at 12:56:15AM +0800, ?? (hucc) wrote: >> > >> >> Returning 415 does not conform to the HTTP protocol when image and proxy_pass >> >> configured in same location. >> >> >> >> # HG changeset patch >> >> # User hucongcong >> >> # Date 1497890354 -28800 >> >> # Tue Jun 20 00:39:14 2017 +0800 >> >> # Node ID af3a94de6a6549dec5e1205514eda1893313a14c >> >> # Parent d1816a2696de8c2faa1cd913a151e5f62a8620f3 >> >> Http image_filter: return 405 when method is HEAD and body is empty. >> >> >> >> diff -r d1816a2696de -r af3a94de6a65 src/http/modules/ngx_http_image_filter_module.c >> >> --- a/src/http/modules/ngx_http_image_filter_module.c Fri Jun 16 18:15:58 2017 +0300 >> >> +++ b/src/http/modules/ngx_http_image_filter_module.c Tue Jun 20 00:39:14 2017 +0800 >> >> @@ -330,6 +330,12 @@ ngx_http_image_body_filter(ngx_http_requ >> >> } >> >> } >> >> >> >> + if (r->method & NGX_HTTP_HEAD) { >> >> + return ngx_http_filter_finalize_request(r, >> >> + &ngx_http_image_filter_module, >> >> + NGX_HTTP_NOT_ALLOWED); >> >> + } >> >> + >> >> return ngx_http_filter_finalize_request(r, >> >> &ngx_http_image_filter_module, >> >> NGX_HTTP_UNSUPPORTED_MEDIA_TYPE); >> > >> >Please clarify why you think that the current code is wrong. I >> >don't see any problems with returning 415 to HEAD requests as long >> >we are going to return 415 to GETs. >> >> Ok, the problem is that nginx will return 200 to GET request and >> 415 to HEAD request. >> >> The configuration looks like: >> #proxy_method GET;#not configured >> location / { >> image resize 180 360; >> #... >> proxy_pass http://test_upstream$uri; >> } > >Ok, so the problem appears when using proxy_pass without >additional configuration, image_filter, and a HEAD request. > >Unfortunately, the suggested change will also break the correct >behaviour in other cases, for example, when serving static files. >Using "proxy_method GET;" might be a better option. Thanks for the reply. Which behaviour will be breaked? Is the following change right? + if (r->method & NGX_HTTP_HEAD && ngx_buf_size(in->buf) == 0) { + return ngx_http_filter_finalize_request(r, + &ngx_http_image_filter_module, + NGX_HTTP_NOT_ALLOWED); + } From arut at nginx.com Tue Jun 20 16:31:28 2017 From: arut at nginx.com (Roman Arutyunyan) Date: Tue, 20 Jun 2017 19:31:28 +0300 Subject: [nginx] slice module issue In-Reply-To: References: <20170620120953.GD470@Romans-MacBook-Air.local> <20170620133449.GE470@Romans-MacBook-Air.local> Message-ID: <20170620163128.GG470@Romans-MacBook-Air.local> You can pass a mapped variable to "proxy_set_header Range" which falls back to whatever you want for multi-range requests. On Tue, Jun 20, 2017 at 03:47:02PM +0000, ??? wrote: > If we wan't to slice in the case of multi-page, how to achieve it? > > ??? ?2017?6?20? ??23:21??? > > > You said the module doesn't support multi-range, it means nothing to > > support slice feature through $slice_range. > > Anyway we can avoid it by access handle, but it's still unconvinient. > > > > Roman Arutyunyan ?2017?6?20? ??21:34??? > > > >> That would disable slicing at all. > >> > >> On Tue, Jun 20, 2017 at 12:42:30PM +0000, ??? wrote: > >> > Do you think it's better to set $slice_range not found as if multi-range > >> > request? > >> > > >> > Roman Arutyunyan ?2017?6?20? ??20:09??? > >> > > >> > > Hi, > >> > > > >> > > On Tue, Jun 20, 2017 at 02:25:14AM +0800, ??? wrote: > >> > > > Hi! > >> > > > > >> > > > Have a look at the following example first. > >> > > > > >> > > > server { > >> > > > listen 80; > >> > > > > >> > > > location / { > >> > > > slice 10; > >> > > > proxy_set_header Range $slice_range; > >> > > > proxy_pass http://127.0.0.1:81; > >> > > > } > >> > > > } > >> > > > > >> > > > > >> > > > server { > >> > > > listen 81; > >> > > > root html; > >> > > > } > >> > > > > >> > > > Then we start a request with curl. > >> > > > > curl http://my.test.com/ -x 127.1:80 -H "range: bytes=1-50, 2-51" > >> > > > > >> > > > We get a response of the whole file that differs from expectation > >> (1-50, > >> > > > 2-51). > >> > > > > >> > > > It seems that slice module doesn't support multi-range (separated by > >> > > > commas), > >> > > > >> > > Yes, the slice module does not support multi-range. > >> > > The entire file is proxied and processed by the standard range module, > >> > > which has limited multi-range support too. Particularly, multi-range > >> is > >> > > supported only when returning an entire file from disk. > >> > > > >> > > > but it's confused $slice_range variable is valid. > >> > > > > >> > > > Please confirm this question and the following patch, thanks! > >> > > > > >> > > > > >> > > > diff -r 5e05118678af src/http/modules/ngx_http_slice_filter_module.c > >> > > > --- a/src/http/modules/ngx_http_slice_filter_module.c Mon May 29 > >> 23:33:38 > >> > > > 2017 +0300 > >> > > > +++ b/src/http/modules/ngx_http_slice_filter_module.c Mon Jun 19 > >> 09:35:24 > >> > > > 2017 -0400 > >> > > > @@ -389,6 +389,7 @@ > >> > > > ngx_http_variable_value_t *v, uintptr_t data) > >> > > > { > >> > > > u_char *p; > >> > > > + off_t start; > >> > > > ngx_http_slice_ctx_t *ctx; > >> > > > ngx_http_slice_loc_conf_t *slcf; > >> > > > > >> > > > @@ -407,6 +408,13 @@ > >> > > > return NGX_OK; > >> > > > } > >> > > > > >> > > > + start = ngx_http_slice_get_start(r); > >> > > > + > >> > > > + if (start == -1) { > >> > > > + v->not_found = 1; > >> > > > + return NGX_OK; > >> > > > + } > >> > > > + > >> > > > ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_slice_ctx_t)); > >> > > > if (ctx == NULL) { > >> > > > return NGX_ERROR; > >> > > > @@ -419,7 +427,7 @@ > >> > > > return NGX_ERROR; > >> > > > } > >> > > > > >> > > > - ctx->start = slcf->size * (ngx_http_slice_get_start(r) / > >> > > > slcf->size); > >> > > > + ctx->start = slcf->size * (start / slcf->size); > >> > > > > >> > > > ctx->range.data = p; > >> > > > ctx->range.len = ngx_sprintf(p, "bytes=%O-%O", ctx->start, > >> > > > @@ -460,7 +468,7 @@ > >> > > > p = h->value.data + 6; > >> > > > > >> > > > if (ngx_strchr(p, ',')) { > >> > > > - return 0; > >> > > > + return -1; > >> > > > } > >> > > > > >> > > > while (*p == ' ') { p++; } > >> > > > > >> > > > > >> > > > And this is a better conf. > >> > > > > >> > > > map $slice_range $x_slice_range { > >> > > > default $http_range; > >> > > > ~ $slice_range; > >> > > > } > >> > > > > >> > > > server { > >> > > > listen 80; > >> > > > > >> > > > location / { > >> > > > slice 10; > >> > > > proxy_set_header Range $x_slice_range; > >> > > > proxy_pass http://127.0.0.1:81; > >> > > > } > >> > > > } > >> > > > >> > > > _______________________________________________ > >> > > > nginx-devel mailing list > >> > > > nginx-devel at nginx.org > >> > > > http://mailman.nginx.org/mailman/listinfo/nginx-devel > >> > > > >> > > > >> > > -- > >> > > Roman Arutyunyan > >> > > _______________________________________________ > >> > > nginx-devel mailing list > >> > > nginx-devel at nginx.org > >> > > http://mailman.nginx.org/mailman/listinfo/nginx-devel > >> > >> > _______________________________________________ > >> > nginx-devel mailing list > >> > nginx-devel at nginx.org > >> > http://mailman.nginx.org/mailman/listinfo/nginx-devel > >> > >> > >> -- > >> Roman Arutyunyan > >> _______________________________________________ > >> nginx-devel mailing list > >> nginx-devel at nginx.org > >> http://mailman.nginx.org/mailman/listinfo/nginx-devel > > > > > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel -- Roman Arutyunyan From xeioex at nginx.com Tue Jun 20 16:33:26 2017 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Tue, 20 Jun 2017 16:33:26 +0000 Subject: [njs] Fixed Object.prototype.hasOwnProperty() without arguments. Message-ID: details: http://hg.nginx.org/njs/rev/36947c6cb8c6 branches: changeset: 373:36947c6cb8c6 user: Dmitry Volyntsev date: Tue Jun 20 18:06:19 2017 +0300 description: Fixed Object.prototype.hasOwnProperty() without arguments. diffstat: njs/njs_object.c | 2 +- njs/test/njs_unit_test.c | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletions(-) diffs (28 lines): diff -r 1c4d7281d44e -r 36947c6cb8c6 njs/njs_object.c --- a/njs/njs_object.c Tue Jun 20 17:12:44 2017 +0300 +++ b/njs/njs_object.c Tue Jun 20 18:06:19 2017 +0300 @@ -1373,7 +1373,7 @@ njs_object_prototype_has_own_property(nj retval = &njs_string_false; - if (njs_is_object(&args[0])) { + if (nargs > 1 && njs_is_object(&args[0])) { if (njs_is_array(&args[0])) { array = args[0].data.u.array; diff -r 1c4d7281d44e -r 36947c6cb8c6 njs/test/njs_unit_test.c --- a/njs/test/njs_unit_test.c Tue Jun 20 17:12:44 2017 +0300 +++ b/njs/test/njs_unit_test.c Tue Jun 20 18:06:19 2017 +0300 @@ -5989,6 +5989,12 @@ static njs_unit_test_t njs_test[] = { nxt_string("var o = {a:1}; o.hasOwnProperty()"), nxt_string("false") }, + { nxt_string("[,].hasOwnProperty()"), + nxt_string("false") }, + + { nxt_string("Object.valueOf.hasOwnProperty()"), + nxt_string("false") }, + { nxt_string("1..hasOwnProperty('b')"), nxt_string("false") }, From hongzhidao at gmail.com Tue Jun 20 16:45:07 2017 From: hongzhidao at gmail.com (=?UTF-8?B?5rSq5b+X6YGT?=) Date: Wed, 21 Jun 2017 00:45:07 +0800 Subject: [nginx] slice module issue In-Reply-To: <20170620163128.GG470@Romans-MacBook-Air.local> References: <20170620120953.GD470@Romans-MacBook-Air.local> <20170620133449.GE470@Romans-MacBook-Air.local> <20170620163128.GG470@Romans-MacBook-Air.local> Message-ID: Well, it's a good idea, but it's not satisfied yet. Now we assume users want to ignore the slice feature when the multi-range request is coming. How about let slice directive support if scope? Such as the following. map $http_range $need_slice { ... } map $slice_range $x_slice_range { ... } server { if ($need_slice) { slice 100; } proxy_set_header Range $x_slice_range; } --- a/src/http/modules/ngx_http_slice_filter_module.c Mon May 29 23:33:38 2017 +0300 +++ b/src/http/modules/ngx_http_slice_filter_module.c Tue Jun 20 12:37:34 2017 -0400 @@ -51,7 +51,8 @@ static ngx_command_t ngx_http_slice_filter_commands[] = { { ngx_string("slice"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF + NGX_CONF_TAKE1, ngx_conf_set_size_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_slice_loc_conf_t, size), On Wed, Jun 21, 2017 at 12:31 AM, Roman Arutyunyan wrote: > You can pass a mapped variable to "proxy_set_header Range" which falls back > to whatever you want for multi-range requests. > > On Tue, Jun 20, 2017 at 03:47:02PM +0000, ??? wrote: > > If we wan't to slice in the case of multi-page, how to achieve it? > > > > ??? ?2017?6?20? ??23:21??? > > > > > You said the module doesn't support multi-range, it means nothing to > > > support slice feature through $slice_range. > > > Anyway we can avoid it by access handle, but it's still unconvinient. > > > > > > Roman Arutyunyan ?2017?6?20? ??21:34??? > > > > > >> That would disable slicing at all. > > >> > > >> On Tue, Jun 20, 2017 at 12:42:30PM +0000, ??? wrote: > > >> > Do you think it's better to set $slice_range not found as if > multi-range > > >> > request? > > >> > > > >> > Roman Arutyunyan ?2017?6?20? ??20:09??? > > >> > > > >> > > Hi, > > >> > > > > >> > > On Tue, Jun 20, 2017 at 02:25:14AM +0800, ??? wrote: > > >> > > > Hi! > > >> > > > > > >> > > > Have a look at the following example first. > > >> > > > > > >> > > > server { > > >> > > > listen 80; > > >> > > > > > >> > > > location / { > > >> > > > slice 10; > > >> > > > proxy_set_header Range $slice_range; > > >> > > > proxy_pass http://127.0.0.1:81; > > >> > > > } > > >> > > > } > > >> > > > > > >> > > > > > >> > > > server { > > >> > > > listen 81; > > >> > > > root html; > > >> > > > } > > >> > > > > > >> > > > Then we start a request with curl. > > >> > > > > curl http://my.test.com/ -x 127.1:80 -H "range: bytes=1-50, > 2-51" > > >> > > > > > >> > > > We get a response of the whole file that differs from > expectation > > >> (1-50, > > >> > > > 2-51). > > >> > > > > > >> > > > It seems that slice module doesn't support multi-range > (separated by > > >> > > > commas), > > >> > > > > >> > > Yes, the slice module does not support multi-range. > > >> > > The entire file is proxied and processed by the standard range > module, > > >> > > which has limited multi-range support too. Particularly, > multi-range > > >> is > > >> > > supported only when returning an entire file from disk. > > >> > > > > >> > > > but it's confused $slice_range variable is valid. > > >> > > > > > >> > > > Please confirm this question and the following patch, thanks! > > >> > > > > > >> > > > > > >> > > > diff -r 5e05118678af src/http/modules/ngx_http_ > slice_filter_module.c > > >> > > > --- a/src/http/modules/ngx_http_slice_filter_module.c Mon May > 29 > > >> 23:33:38 > > >> > > > 2017 +0300 > > >> > > > +++ b/src/http/modules/ngx_http_slice_filter_module.c Mon Jun > 19 > > >> 09:35:24 > > >> > > > 2017 -0400 > > >> > > > @@ -389,6 +389,7 @@ > > >> > > > ngx_http_variable_value_t *v, uintptr_t data) > > >> > > > { > > >> > > > u_char *p; > > >> > > > + off_t start; > > >> > > > ngx_http_slice_ctx_t *ctx; > > >> > > > ngx_http_slice_loc_conf_t *slcf; > > >> > > > > > >> > > > @@ -407,6 +408,13 @@ > > >> > > > return NGX_OK; > > >> > > > } > > >> > > > > > >> > > > + start = ngx_http_slice_get_start(r); > > >> > > > + > > >> > > > + if (start == -1) { > > >> > > > + v->not_found = 1; > > >> > > > + return NGX_OK; > > >> > > > + } > > >> > > > + > > >> > > > ctx = ngx_pcalloc(r->pool, > sizeof(ngx_http_slice_ctx_t)); > > >> > > > if (ctx == NULL) { > > >> > > > return NGX_ERROR; > > >> > > > @@ -419,7 +427,7 @@ > > >> > > > return NGX_ERROR; > > >> > > > } > > >> > > > > > >> > > > - ctx->start = slcf->size * (ngx_http_slice_get_start(r) > / > > >> > > > slcf->size); > > >> > > > + ctx->start = slcf->size * (start / slcf->size); > > >> > > > > > >> > > > ctx->range.data = p; > > >> > > > ctx->range.len = ngx_sprintf(p, "bytes=%O-%O", > ctx->start, > > >> > > > @@ -460,7 +468,7 @@ > > >> > > > p = h->value.data + 6; > > >> > > > > > >> > > > if (ngx_strchr(p, ',')) { > > >> > > > - return 0; > > >> > > > + return -1; > > >> > > > } > > >> > > > > > >> > > > while (*p == ' ') { p++; } > > >> > > > > > >> > > > > > >> > > > And this is a better conf. > > >> > > > > > >> > > > map $slice_range $x_slice_range { > > >> > > > default $http_range; > > >> > > > ~ $slice_range; > > >> > > > } > > >> > > > > > >> > > > server { > > >> > > > listen 80; > > >> > > > > > >> > > > location / { > > >> > > > slice 10; > > >> > > > proxy_set_header Range $x_slice_range; > > >> > > > proxy_pass http://127.0.0.1:81; > > >> > > > } > > >> > > > } > > >> > > > > >> > > > _______________________________________________ > > >> > > > nginx-devel mailing list > > >> > > > nginx-devel at nginx.org > > >> > > > http://mailman.nginx.org/mailman/listinfo/nginx-devel > > >> > > > > >> > > > > >> > > -- > > >> > > Roman Arutyunyan > > >> > > _______________________________________________ > > >> > > nginx-devel mailing list > > >> > > nginx-devel at nginx.org > > >> > > http://mailman.nginx.org/mailman/listinfo/nginx-devel > > >> > > >> > _______________________________________________ > > >> > nginx-devel mailing list > > >> > nginx-devel at nginx.org > > >> > http://mailman.nginx.org/mailman/listinfo/nginx-devel > > >> > > >> > > >> -- > > >> Roman Arutyunyan > > >> _______________________________________________ > > >> nginx-devel mailing list > > >> nginx-devel at nginx.org > > >> http://mailman.nginx.org/mailman/listinfo/nginx-devel > > > > > > > > > _______________________________________________ > > nginx-devel mailing list > > nginx-devel at nginx.org > > http://mailman.nginx.org/mailman/listinfo/nginx-devel > > > -- > Roman Arutyunyan > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel > -------------- next part -------------- An HTML attachment was scrubbed... URL: From hongzhidao at gmail.com Tue Jun 20 17:22:09 2017 From: hongzhidao at gmail.com (=?UTF-8?B?5rSq5b+X6YGT?=) Date: Wed, 21 Jun 2017 01:22:09 +0800 Subject: [nginx] slice module issue In-Reply-To: References: <20170620120953.GD470@Romans-MacBook-Air.local> <20170620133449.GE470@Romans-MacBook-Air.local> <20170620163128.GG470@Romans-MacBook-Air.local> Message-ID: I hope slice module can satisfy customer requirements. Now the key point is users want to control the behavior of slice. And this is common. Here's the patch, take a look please! diff -r 5e05118678af src/http/modules/ngx_http_slice_filter_module.c --- a/src/http/modules/ngx_http_slice_filter_module.c Mon May 29 23:33:38 2017 +0300 +++ b/src/http/modules/ngx_http_slice_filter_module.c Tue Jun 20 13:17:17 2017 -0400 @@ -12,6 +12,7 @@ typedef struct { size_t size; + ngx_flag_t always; } ngx_http_slice_loc_conf_t; @@ -57,6 +58,13 @@ offsetof(ngx_http_slice_loc_conf_t, size), NULL }, + { ngx_string("slice_always"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_slice_loc_conf_t, always), + NULL }, + ngx_null_command }; @@ -389,6 +397,7 @@ ngx_http_variable_value_t *v, uintptr_t data) { u_char *p; + off_t start; ngx_http_slice_ctx_t *ctx; ngx_http_slice_loc_conf_t *slcf; @@ -407,6 +416,12 @@ return NGX_OK; } + start = ngx_http_slice_get_start(r); + if (start == -1 && slcf->always == 0) { + v->not_found = 1; + return NGX_OK; + } + ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_slice_ctx_t)); if (ctx == NULL) { return NGX_ERROR; @@ -419,7 +434,7 @@ return NGX_ERROR; } - ctx->start = slcf->size * (ngx_http_slice_get_start(r) / slcf->size); + ctx->start = slcf->size * (start / slcf->size); ctx->range.data = p; ctx->range.len = ngx_sprintf(p, "bytes=%O-%O", ctx->start, @@ -445,7 +460,7 @@ ngx_table_elt_t *h; if (r->headers_in.if_range) { - return 0; + return -1; } h = r->headers_in.range; @@ -454,13 +469,13 @@ || h->value.len < 7 || ngx_strncasecmp(h->value.data, (u_char *) "bytes=", 6) != 0) { - return 0; + return -1; } p = h->value.data + 6; if (ngx_strchr(p, ',')) { - return 0; + return -1; } while (*p == ' ') { p++; } @@ -476,7 +491,7 @@ while (*p >= '0' && *p <= '9') { if (start >= cutoff && (start > cutoff || *p - '0' > cutlim)) { - return 0; + return -1; } start = start * 10 + *p++ - '0'; @@ -497,6 +512,7 @@ } slcf->size = NGX_CONF_UNSET_SIZE; + slcf->always = NGX_CONF_UNSET; return slcf; } @@ -509,6 +525,7 @@ ngx_http_slice_loc_conf_t *conf = child; ngx_conf_merge_size_value(conf->size, prev->size, 0); + ngx_conf_merge_value(conf->always, prev->always, 1); return NGX_CONF_OK; } -------------- next part -------------- An HTML attachment was scrubbed... URL: From mdounin at mdounin.ru Tue Jun 20 17:38:27 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Tue, 20 Jun 2017 20:38:27 +0300 Subject: [patch] Http image_filter: return 405 when method is HEAD andbodyis empty. In-Reply-To: References: <20170619172115.GN55433@mdounin.ru> <20170620155352.GU55433@mdounin.ru> Message-ID: <20170620173827.GW55433@mdounin.ru> Hello! On Wed, Jun 21, 2017 at 12:28:23AM +0800, ?? (hucc) wrote: > Hi, > > On Tuesday, Jun 20, 2017 11:53 PM +0300, Maxim Dounin wrote: > > >> >On Tue, Jun 20, 2017 at 12:56:15AM +0800, ?? (hucc) wrote: > >> > > >> >> Returning 415 does not conform to the HTTP protocol when image and proxy_pass > >> >> configured in same location. > >> >> > >> >> # HG changeset patch > >> >> # User hucongcong > >> >> # Date 1497890354 -28800 > >> >> # Tue Jun 20 00:39:14 2017 +0800 > >> >> # Node ID af3a94de6a6549dec5e1205514eda1893313a14c > >> >> # Parent d1816a2696de8c2faa1cd913a151e5f62a8620f3 > >> >> Http image_filter: return 405 when method is HEAD and body is empty. > >> >> > >> >> diff -r d1816a2696de -r af3a94de6a65 src/http/modules/ngx_http_image_filter_module.c > >> >> --- a/src/http/modules/ngx_http_image_filter_module.c Fri Jun 16 18:15:58 2017 +0300 > >> >> +++ b/src/http/modules/ngx_http_image_filter_module.c Tue Jun 20 00:39:14 2017 +0800 > >> >> @@ -330,6 +330,12 @@ ngx_http_image_body_filter(ngx_http_requ > >> >> } > >> >> } > >> >> > >> >> + if (r->method & NGX_HTTP_HEAD) { > >> >> + return ngx_http_filter_finalize_request(r, > >> >> + &ngx_http_image_filter_module, > >> >> + NGX_HTTP_NOT_ALLOWED); > >> >> + } > >> >> + > >> >> return ngx_http_filter_finalize_request(r, > >> >> &ngx_http_image_filter_module, > >> >> NGX_HTTP_UNSUPPORTED_MEDIA_TYPE); > >> > > >> >Please clarify why you think that the current code is wrong. I > >> >don't see any problems with returning 415 to HEAD requests as long > >> >we are going to return 415 to GETs. > >> > >> Ok, the problem is that nginx will return 200 to GET request and > >> 415 to HEAD request. > >> > >> The configuration looks like: > >> #proxy_method GET;#not configured > >> location / { > >> image resize 180 360; > >> #... > >> proxy_pass http://test_upstream$uri; > >> } > > > >Ok, so the problem appears when using proxy_pass without > >additional configuration, image_filter, and a HEAD request. > > > >Unfortunately, the suggested change will also break the correct > >behaviour in other cases, for example, when serving static files. > >Using "proxy_method GET;" might be a better option. > > Thanks for the reply. Which behaviour will be breaked? Is the following change right? When the response is read from a static file, not proxied, and/or proxied with "proxy_method GET;", an empty response means exacly that: an empty response, and returning 415 is perfectly correct. The ngx_buf_size() test will limit the incorrect behaviour to indeed empty responses (previous version of your patch affected all non-image responses), but it won't eliminate incorrect behaviour for empty responses. The root cause of the problem is that in the configuration you've provided proxy don't know about image filter, and uses the HEAD method in the request to upstream filter despite the fact image filter needs a body to return proper response. This doesn't look like something easy to fix, as proper fix implies some knowledge to be passed between image filter and proxy. Most trivial solution, as suggested above, would be to use "proxy_method GET" explicitly in the configuration. It might be actually a good enough solution, as image filter is a special module and it requires proper configuration anyway. -- Maxim Dounin http://nginx.org/ From arut at nginx.com Tue Jun 20 18:43:56 2017 From: arut at nginx.com (Roman Arutyunyan) Date: Tue, 20 Jun 2017 21:43:56 +0300 Subject: [nginx] slice module issue In-Reply-To: References: <20170620120953.GD470@Romans-MacBook-Air.local> <20170620133449.GE470@Romans-MacBook-Air.local> <20170620163128.GG470@Romans-MacBook-Air.local> Message-ID: <20170620184356.GH470@Romans-MacBook-Air.local> Hi, Here's a simple configuration for your case. map $http_range $proxy_range { volatile; ~, $http_range; default $slice_range; } server { listen 8000; location / { slice 100; proxy_set_header Range $proxy_range; proxy_pass http://127.0.0.1:9000; } } Note that at request level the slice module is enabled by evaluating the $slice_range variable. On Wed, Jun 21, 2017 at 12:45:07AM +0800, ??? wrote: > Well, it's a good idea, but it's not satisfied yet. > > Now we assume users want to ignore the slice feature > when the multi-range request is coming. > > How about let slice directive support if scope? > > Such as the following. > > map $http_range $need_slice { > ... > } > > map $slice_range $x_slice_range { > ... > } > > server { > if ($need_slice) { > slice 100; > } > > proxy_set_header Range $x_slice_range; > } > > --- a/src/http/modules/ngx_http_slice_filter_module.c Mon May 29 23:33:38 > 2017 +0300 > +++ b/src/http/modules/ngx_http_slice_filter_module.c Tue Jun 20 12:37:34 > 2017 -0400 > @@ -51,7 +51,8 @@ > static ngx_command_t ngx_http_slice_filter_commands[] = { > > { ngx_string("slice"), > - > NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, > + > NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF > + NGX_CONF_TAKE1, > ngx_conf_set_size_slot, > NGX_HTTP_LOC_CONF_OFFSET, > offsetof(ngx_http_slice_loc_conf_t, size), > > On Wed, Jun 21, 2017 at 12:31 AM, Roman Arutyunyan wrote: > > > You can pass a mapped variable to "proxy_set_header Range" which falls back > > to whatever you want for multi-range requests. > > > > On Tue, Jun 20, 2017 at 03:47:02PM +0000, ??? wrote: > > > If we wan't to slice in the case of multi-page, how to achieve it? > > > > > > ??? ?2017?6?20? ??23:21??? > > > > > > > You said the module doesn't support multi-range, it means nothing to > > > > support slice feature through $slice_range. > > > > Anyway we can avoid it by access handle, but it's still unconvinient. > > > > > > > > Roman Arutyunyan ?2017?6?20? ??21:34??? > > > > > > > >> That would disable slicing at all. > > > >> > > > >> On Tue, Jun 20, 2017 at 12:42:30PM +0000, ??? wrote: > > > >> > Do you think it's better to set $slice_range not found as if > > multi-range > > > >> > request? > > > >> > > > > >> > Roman Arutyunyan ?2017?6?20? ??20:09??? > > > >> > > > > >> > > Hi, > > > >> > > > > > >> > > On Tue, Jun 20, 2017 at 02:25:14AM +0800, ??? wrote: > > > >> > > > Hi! > > > >> > > > > > > >> > > > Have a look at the following example first. > > > >> > > > > > > >> > > > server { > > > >> > > > listen 80; > > > >> > > > > > > >> > > > location / { > > > >> > > > slice 10; > > > >> > > > proxy_set_header Range $slice_range; > > > >> > > > proxy_pass http://127.0.0.1:81; > > > >> > > > } > > > >> > > > } > > > >> > > > > > > >> > > > > > > >> > > > server { > > > >> > > > listen 81; > > > >> > > > root html; > > > >> > > > } > > > >> > > > > > > >> > > > Then we start a request with curl. > > > >> > > > > curl http://my.test.com/ -x 127.1:80 -H "range: bytes=1-50, > > 2-51" > > > >> > > > > > > >> > > > We get a response of the whole file that differs from > > expectation > > > >> (1-50, > > > >> > > > 2-51). > > > >> > > > > > > >> > > > It seems that slice module doesn't support multi-range > > (separated by > > > >> > > > commas), > > > >> > > > > > >> > > Yes, the slice module does not support multi-range. > > > >> > > The entire file is proxied and processed by the standard range > > module, > > > >> > > which has limited multi-range support too. Particularly, > > multi-range > > > >> is > > > >> > > supported only when returning an entire file from disk. > > > >> > > > > > >> > > > but it's confused $slice_range variable is valid. > > > >> > > > > > > >> > > > Please confirm this question and the following patch, thanks! > > > >> > > > > > > >> > > > > > > >> > > > diff -r 5e05118678af src/http/modules/ngx_http_ > > slice_filter_module.c > > > >> > > > --- a/src/http/modules/ngx_http_slice_filter_module.c Mon May > > 29 > > > >> 23:33:38 > > > >> > > > 2017 +0300 > > > >> > > > +++ b/src/http/modules/ngx_http_slice_filter_module.c Mon Jun > > 19 > > > >> 09:35:24 > > > >> > > > 2017 -0400 > > > >> > > > @@ -389,6 +389,7 @@ > > > >> > > > ngx_http_variable_value_t *v, uintptr_t data) > > > >> > > > { > > > >> > > > u_char *p; > > > >> > > > + off_t start; > > > >> > > > ngx_http_slice_ctx_t *ctx; > > > >> > > > ngx_http_slice_loc_conf_t *slcf; > > > >> > > > > > > >> > > > @@ -407,6 +408,13 @@ > > > >> > > > return NGX_OK; > > > >> > > > } > > > >> > > > > > > >> > > > + start = ngx_http_slice_get_start(r); > > > >> > > > + > > > >> > > > + if (start == -1) { > > > >> > > > + v->not_found = 1; > > > >> > > > + return NGX_OK; > > > >> > > > + } > > > >> > > > + > > > >> > > > ctx = ngx_pcalloc(r->pool, > > sizeof(ngx_http_slice_ctx_t)); > > > >> > > > if (ctx == NULL) { > > > >> > > > return NGX_ERROR; > > > >> > > > @@ -419,7 +427,7 @@ > > > >> > > > return NGX_ERROR; > > > >> > > > } > > > >> > > > > > > >> > > > - ctx->start = slcf->size * (ngx_http_slice_get_start(r) > > / > > > >> > > > slcf->size); > > > >> > > > + ctx->start = slcf->size * (start / slcf->size); > > > >> > > > > > > >> > > > ctx->range.data = p; > > > >> > > > ctx->range.len = ngx_sprintf(p, "bytes=%O-%O", > > ctx->start, > > > >> > > > @@ -460,7 +468,7 @@ > > > >> > > > p = h->value.data + 6; > > > >> > > > > > > >> > > > if (ngx_strchr(p, ',')) { > > > >> > > > - return 0; > > > >> > > > + return -1; > > > >> > > > } > > > >> > > > > > > >> > > > while (*p == ' ') { p++; } > > > >> > > > > > > >> > > > > > > >> > > > And this is a better conf. > > > >> > > > > > > >> > > > map $slice_range $x_slice_range { > > > >> > > > default $http_range; > > > >> > > > ~ $slice_range; > > > >> > > > } > > > >> > > > > > > >> > > > server { > > > >> > > > listen 80; > > > >> > > > > > > >> > > > location / { > > > >> > > > slice 10; > > > >> > > > proxy_set_header Range $x_slice_range; > > > >> > > > proxy_pass http://127.0.0.1:81; > > > >> > > > } > > > >> > > > } > > > >> > > > > > >> > > > _______________________________________________ > > > >> > > > nginx-devel mailing list > > > >> > > > nginx-devel at nginx.org > > > >> > > > http://mailman.nginx.org/mailman/listinfo/nginx-devel > > > >> > > > > > >> > > > > > >> > > -- > > > >> > > Roman Arutyunyan > > > >> > > _______________________________________________ > > > >> > > nginx-devel mailing list > > > >> > > nginx-devel at nginx.org > > > >> > > http://mailman.nginx.org/mailman/listinfo/nginx-devel > > > >> > > > >> > _______________________________________________ > > > >> > nginx-devel mailing list > > > >> > nginx-devel at nginx.org > > > >> > http://mailman.nginx.org/mailman/listinfo/nginx-devel > > > >> > > > >> > > > >> -- > > > >> Roman Arutyunyan > > > >> _______________________________________________ > > > >> nginx-devel mailing list > > > >> nginx-devel at nginx.org > > > >> http://mailman.nginx.org/mailman/listinfo/nginx-devel > > > > > > > > > > > > > _______________________________________________ > > > nginx-devel mailing list > > > nginx-devel at nginx.org > > > http://mailman.nginx.org/mailman/listinfo/nginx-devel > > > > > > -- > > Roman Arutyunyan > > _______________________________________________ > > nginx-devel mailing list > > nginx-devel at nginx.org > > http://mailman.nginx.org/mailman/listinfo/nginx-devel > > > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel -- Roman Arutyunyan From hongzhidao at gmail.com Tue Jun 20 19:11:22 2017 From: hongzhidao at gmail.com (=?UTF-8?B?5rSq5b+X6YGT?=) Date: Wed, 21 Jun 2017 03:11:22 +0800 Subject: [nginx] slice module issue In-Reply-To: <20170620184356.GH470@Romans-MacBook-Air.local> References: <20170620120953.GD470@Romans-MacBook-Air.local> <20170620133449.GE470@Romans-MacBook-Air.local> <20170620163128.GG470@Romans-MacBook-Air.local> <20170620184356.GH470@Romans-MacBook-Air.local> Message-ID: Wow, thank you for your patience. Perfect!!! Btw, The ctx of slice module is really a good skill. On Wed, Jun 21, 2017 at 2:43 AM, Roman Arutyunyan wrote: > Hi, > > Here's a simple configuration for your case. > > map $http_range $proxy_range { > volatile; > ~, $http_range; > default $slice_range; > } > > server { > listen 8000; > location / { > slice 100; > proxy_set_header Range $proxy_range; > proxy_pass http://127.0.0.1:9000; > } > } > > > Note that at request level the slice module is enabled by evaluating the > $slice_range variable. > > > On Wed, Jun 21, 2017 at 12:45:07AM +0800, ??? wrote: > > Well, it's a good idea, but it's not satisfied yet. > > > > Now we assume users want to ignore the slice feature > > when the multi-range request is coming. > > > > How about let slice directive support if scope? > > > > Such as the following. > > > > map $http_range $need_slice { > > ... > > } > > > > map $slice_range $x_slice_range { > > ... > > } > > > > server { > > if ($need_slice) { > > slice 100; > > } > > > > proxy_set_header Range $x_slice_range; > > } > > > > --- a/src/http/modules/ngx_http_slice_filter_module.c Mon May 29 > 23:33:38 > > 2017 +0300 > > +++ b/src/http/modules/ngx_http_slice_filter_module.c Tue Jun 20 > 12:37:34 > > 2017 -0400 > > @@ -51,7 +51,8 @@ > > static ngx_command_t ngx_http_slice_filter_commands[] = { > > > > { ngx_string("slice"), > > - > > NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, > > + > > NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF| > NGX_HTTP_LIF_CONF > > + NGX_CONF_TAKE1, > > ngx_conf_set_size_slot, > > NGX_HTTP_LOC_CONF_OFFSET, > > offsetof(ngx_http_slice_loc_conf_t, size), > > > > On Wed, Jun 21, 2017 at 12:31 AM, Roman Arutyunyan > wrote: > > > > > You can pass a mapped variable to "proxy_set_header Range" which falls > back > > > to whatever you want for multi-range requests. > > > > > > On Tue, Jun 20, 2017 at 03:47:02PM +0000, ??? wrote: > > > > If we wan't to slice in the case of multi-page, how to achieve it? > > > > > > > > ??? ?2017?6?20? ??23:21??? > > > > > > > > > You said the module doesn't support multi-range, it means nothing > to > > > > > support slice feature through $slice_range. > > > > > Anyway we can avoid it by access handle, but it's still > unconvinient. > > > > > > > > > > Roman Arutyunyan ?2017?6?20? ??21:34??? > > > > > > > > > >> That would disable slicing at all. > > > > >> > > > > >> On Tue, Jun 20, 2017 at 12:42:30PM +0000, ??? wrote: > > > > >> > Do you think it's better to set $slice_range not found as if > > > multi-range > > > > >> > request? > > > > >> > > > > > >> > Roman Arutyunyan ?2017?6?20? ??20:09??? > > > > >> > > > > > >> > > Hi, > > > > >> > > > > > > >> > > On Tue, Jun 20, 2017 at 02:25:14AM +0800, ??? wrote: > > > > >> > > > Hi! > > > > >> > > > > > > > >> > > > Have a look at the following example first. > > > > >> > > > > > > > >> > > > server { > > > > >> > > > listen 80; > > > > >> > > > > > > > >> > > > location / { > > > > >> > > > slice 10; > > > > >> > > > proxy_set_header Range $slice_range; > > > > >> > > > proxy_pass http://127.0.0.1:81; > > > > >> > > > } > > > > >> > > > } > > > > >> > > > > > > > >> > > > > > > > >> > > > server { > > > > >> > > > listen 81; > > > > >> > > > root html; > > > > >> > > > } > > > > >> > > > > > > > >> > > > Then we start a request with curl. > > > > >> > > > > curl http://my.test.com/ -x 127.1:80 -H "range: > bytes=1-50, > > > 2-51" > > > > >> > > > > > > > >> > > > We get a response of the whole file that differs from > > > expectation > > > > >> (1-50, > > > > >> > > > 2-51). > > > > >> > > > > > > > >> > > > It seems that slice module doesn't support multi-range > > > (separated by > > > > >> > > > commas), > > > > >> > > > > > > >> > > Yes, the slice module does not support multi-range. > > > > >> > > The entire file is proxied and processed by the standard range > > > module, > > > > >> > > which has limited multi-range support too. Particularly, > > > multi-range > > > > >> is > > > > >> > > supported only when returning an entire file from disk. > > > > >> > > > > > > >> > > > but it's confused $slice_range variable is valid. > > > > >> > > > > > > > >> > > > Please confirm this question and the following patch, > thanks! > > > > >> > > > > > > > >> > > > > > > > >> > > > diff -r 5e05118678af src/http/modules/ngx_http_ > > > slice_filter_module.c > > > > >> > > > --- a/src/http/modules/ngx_http_slice_filter_module.c Mon > May > > > 29 > > > > >> 23:33:38 > > > > >> > > > 2017 +0300 > > > > >> > > > +++ b/src/http/modules/ngx_http_slice_filter_module.c Mon > Jun > > > 19 > > > > >> 09:35:24 > > > > >> > > > 2017 -0400 > > > > >> > > > @@ -389,6 +389,7 @@ > > > > >> > > > ngx_http_variable_value_t *v, uintptr_t data) > > > > >> > > > { > > > > >> > > > u_char *p; > > > > >> > > > + off_t start; > > > > >> > > > ngx_http_slice_ctx_t *ctx; > > > > >> > > > ngx_http_slice_loc_conf_t *slcf; > > > > >> > > > > > > > >> > > > @@ -407,6 +408,13 @@ > > > > >> > > > return NGX_OK; > > > > >> > > > } > > > > >> > > > > > > > >> > > > + start = ngx_http_slice_get_start(r); > > > > >> > > > + > > > > >> > > > + if (start == -1) { > > > > >> > > > + v->not_found = 1; > > > > >> > > > + return NGX_OK; > > > > >> > > > + } > > > > >> > > > + > > > > >> > > > ctx = ngx_pcalloc(r->pool, > > > sizeof(ngx_http_slice_ctx_t)); > > > > >> > > > if (ctx == NULL) { > > > > >> > > > return NGX_ERROR; > > > > >> > > > @@ -419,7 +427,7 @@ > > > > >> > > > return NGX_ERROR; > > > > >> > > > } > > > > >> > > > > > > > >> > > > - ctx->start = slcf->size * > (ngx_http_slice_get_start(r) > > > / > > > > >> > > > slcf->size); > > > > >> > > > + ctx->start = slcf->size * (start / slcf->size); > > > > >> > > > > > > > >> > > > ctx->range.data = p; > > > > >> > > > ctx->range.len = ngx_sprintf(p, "bytes=%O-%O", > > > ctx->start, > > > > >> > > > @@ -460,7 +468,7 @@ > > > > >> > > > p = h->value.data + 6; > > > > >> > > > > > > > >> > > > if (ngx_strchr(p, ',')) { > > > > >> > > > - return 0; > > > > >> > > > + return -1; > > > > >> > > > } > > > > >> > > > > > > > >> > > > while (*p == ' ') { p++; } > > > > >> > > > > > > > >> > > > > > > > >> > > > And this is a better conf. > > > > >> > > > > > > > >> > > > map $slice_range $x_slice_range { > > > > >> > > > default $http_range; > > > > >> > > > ~ $slice_range; > > > > >> > > > } > > > > >> > > > > > > > >> > > > server { > > > > >> > > > listen 80; > > > > >> > > > > > > > >> > > > location / { > > > > >> > > > slice 10; > > > > >> > > > proxy_set_header Range $x_slice_range; > > > > >> > > > proxy_pass http://127.0.0.1:81; > > > > >> > > > } > > > > >> > > > } > > > > >> > > > > > > >> > > > _______________________________________________ > > > > >> > > > nginx-devel mailing list > > > > >> > > > nginx-devel at nginx.org > > > > >> > > > http://mailman.nginx.org/mailman/listinfo/nginx-devel > > > > >> > > > > > > >> > > > > > > >> > > -- > > > > >> > > Roman Arutyunyan > > > > >> > > _______________________________________________ > > > > >> > > nginx-devel mailing list > > > > >> > > nginx-devel at nginx.org > > > > >> > > http://mailman.nginx.org/mailman/listinfo/nginx-devel > > > > >> > > > > >> > _______________________________________________ > > > > >> > nginx-devel mailing list > > > > >> > nginx-devel at nginx.org > > > > >> > http://mailman.nginx.org/mailman/listinfo/nginx-devel > > > > >> > > > > >> > > > > >> -- > > > > >> Roman Arutyunyan > > > > >> _______________________________________________ > > > > >> nginx-devel mailing list > > > > >> nginx-devel at nginx.org > > > > >> http://mailman.nginx.org/mailman/listinfo/nginx-devel > > > > > > > > > > > > > > > > > _______________________________________________ > > > > nginx-devel mailing list > > > > nginx-devel at nginx.org > > > > http://mailman.nginx.org/mailman/listinfo/nginx-devel > > > > > > > > > -- > > > Roman Arutyunyan > > > _______________________________________________ > > > nginx-devel mailing list > > > nginx-devel at nginx.org > > > http://mailman.nginx.org/mailman/listinfo/nginx-devel > > > > > > _______________________________________________ > > nginx-devel mailing list > > nginx-devel at nginx.org > > http://mailman.nginx.org/mailman/listinfo/nginx-devel > > > -- > Roman Arutyunyan > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel > -------------- next part -------------- An HTML attachment was scrubbed... URL: From hucong.c at foxmail.com Wed Jun 21 17:05:37 2017 From: hucong.c at foxmail.com (=?utf-8?B?6IOh6IGqIChodWNjKQ==?=) Date: Thu, 22 Jun 2017 01:05:37 +0800 Subject: [patch] Http image_filter: return 405 when method is HEADandbodyis empty. In-Reply-To: <20170620173827.GW55433@mdounin.ru> References: <20170619172115.GN55433@mdounin.ru> <20170620155352.GU55433@mdounin.ru> <20170620173827.GW55433@mdounin.ru> Message-ID: Hi, On Wednesday, Jun 21, 2017 1:38 AM +0300, Maxim Dounin wrote: >When the response is read from a static file, not proxied, and/or >proxied with "proxy_method GET;", an empty response means exacly >that: an empty response, and returning 415 is perfectly correct. > >The ngx_buf_size() test will limit the incorrect behaviour to >indeed empty responses (previous version of your patch affected >all non-image responses), but it won't eliminate incorrect behaviour >for empty responses. > >The root cause of the problem is that in the configuration you've >provided proxy don't know about image filter, and uses the HEAD >method in the request to upstream filter despite the fact image >filter needs a body to return proper response. This doesn't look >like something easy to fix, as proper fix implies some knowledge >to be passed between image filter and proxy. > >Most trivial solution, as suggested above, would be to use >"proxy_method GET" explicitly in the configuration. It might be >actually a good enough solution, as image filter is a special >module and it requires proper configuration anyway. Thank you for the detailed explanation. After reading the code again, I still have two questions. First, is the following change appropriate ? --- a/ngx_http_image_filter_module.c +++ b/ngx_http_image_filter_module.c @@ -330,6 +330,15 @@ ngx_http_image_body_filter(ngx_http_request_t *r, ngx_chain_t *in) } } + if (r->method & NGX_HTTP_HEAD + && ctx->length + && ngx_buf_size(in->buf) == 0) + { + return ngx_http_filter_finalize_request(r, + &ngx_http_image_filter_module, + NGX_HTTP_NOT_ALLOWED); + } + return ngx_http_filter_finalize_request(r, &ngx_http_image_filter_module, NGX_HTTP_UNSUPPORTED_MEDIA_TYPE); Second, Why not restrict the use of the image filter in the following way ? --- a/ngx_http_image_filter_module.c +++ b/ngx_http_image_filter_module.c @@ -224,7 +224,9 @@ ngx_http_image_header_filter(ngx_http_request_t *r) ngx_http_image_filter_ctx_t *ctx; ngx_http_image_filter_conf_t *conf; - if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED) { + if (r->headers_out.status != NGX_HTTP_OK + && r->headers_out.status != NGX_HTTP_PARTIAL_CONTENT) + { return ngx_http_next_header_filter(r); } Best wishes -hucc From mdounin at mdounin.ru Wed Jun 21 17:24:17 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Wed, 21 Jun 2017 20:24:17 +0300 Subject: [patch] Http image_filter: return 405 when method is HEADandbodyis empty. In-Reply-To: References: <20170619172115.GN55433@mdounin.ru> <20170620155352.GU55433@mdounin.ru> <20170620173827.GW55433@mdounin.ru> Message-ID: <20170621172417.GC55433@mdounin.ru> Hello! On Thu, Jun 22, 2017 at 01:05:37AM +0800, ?? (hucc) wrote: > Hi, > > On Wednesday, Jun 21, 2017 1:38 AM +0300, Maxim Dounin wrote: > > >When the response is read from a static file, not proxied, and/or > >proxied with "proxy_method GET;", an empty response means exacly > >that: an empty response, and returning 415 is perfectly correct. > > > >The ngx_buf_size() test will limit the incorrect behaviour to > >indeed empty responses (previous version of your patch affected > >all non-image responses), but it won't eliminate incorrect behaviour > >for empty responses. > > > >The root cause of the problem is that in the configuration you've > >provided proxy don't know about image filter, and uses the HEAD > >method in the request to upstream filter despite the fact image > >filter needs a body to return proper response. This doesn't look > >like something easy to fix, as proper fix implies some knowledge > >to be passed between image filter and proxy. > > > >Most trivial solution, as suggested above, would be to use > >"proxy_method GET" explicitly in the configuration. It might be > >actually a good enough solution, as image filter is a special > >module and it requires proper configuration anyway. > > Thank you for the detailed explanation. After reading the code again, > I still have two questions. First, is the following change appropriate ? > > --- a/ngx_http_image_filter_module.c > +++ b/ngx_http_image_filter_module.c > @@ -330,6 +330,15 @@ ngx_http_image_body_filter(ngx_http_request_t *r, ngx_chain_t *in) > } > } > > + if (r->method & NGX_HTTP_HEAD > + && ctx->length > + && ngx_buf_size(in->buf) == 0) > + { > + return ngx_http_filter_finalize_request(r, > + &ngx_http_image_filter_module, > + NGX_HTTP_NOT_ALLOWED); > + } > + > return ngx_http_filter_finalize_request(r, > &ngx_http_image_filter_module, > NGX_HTTP_UNSUPPORTED_MEDIA_TYPE); I don't think it is. > Second, Why not restrict the use of the image filter in the following way ? > > --- a/ngx_http_image_filter_module.c > +++ b/ngx_http_image_filter_module.c > @@ -224,7 +224,9 @@ ngx_http_image_header_filter(ngx_http_request_t *r) > ngx_http_image_filter_ctx_t *ctx; > ngx_http_image_filter_conf_t *conf; > > - if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED) { > + if (r->headers_out.status != NGX_HTTP_OK > + && r->headers_out.status != NGX_HTTP_PARTIAL_CONTENT) > + { > return ngx_http_next_header_filter(r); > } The goal of the image filter module is to process all resources returned, including ones returned with error codes. Such a change will make it useless at least for the use case the module was written for. -- Maxim Dounin http://nginx.org/ From hucong.c at foxmail.com Wed Jun 21 17:24:38 2017 From: hucong.c at foxmail.com (=?utf-8?B?6IOh6IGqIChodWNjKQ==?=) Date: Thu, 22 Jun 2017 01:24:38 +0800 Subject: [patch] Slice filter: support for empty file. In-Reply-To: <20170620150117.GS55433@mdounin.ru> References: <20170620150117.GS55433@mdounin.ru> Message-ID: Hi, On Tuesday, Jun 20, 2017 11:01 PM +0300, Maxim Dounin wrote: >On Tue, Jun 20, 2017 at 10:39:44AM +0800, ?? (hucc) wrote: > >> >416 will be returned when the request has no Range HEADER and the target file is >> >empty. Apparently, it does not conform to the HTTP protocol. Empty file seems >> >inevitable in the CDN service where Nginx is heavily used. > >Returning the 416 response with some headers deleted and status >code changed to 200 doesn't look like a safe solution. > >On the other hand, changing nginx's range filter to allow range >requests to 0-byte files should make this unneeded. I've reopened >ticket #1031 which is about range requests to 0-byte files, see >https://trac.nginx.org/nginx/ticket/1031. Looking forward to this feature! I've tried to implement this feature, and it's a bit hard for me now. From hucong.c at foxmail.com Wed Jun 21 17:38:28 2017 From: hucong.c at foxmail.com (=?utf-8?B?6IOh6IGqIChodWNjKQ==?=) Date: Thu, 22 Jun 2017 01:38:28 +0800 Subject: [patch] Http image_filter: return 405 when method isHEADandbodyis empty. In-Reply-To: <20170621172417.GC55433@mdounin.ru> References: <20170619172115.GN55433@mdounin.ru> <20170620155352.GU55433@mdounin.ru> <20170620173827.GW55433@mdounin.ru> <20170621172417.GC55433@mdounin.ru> Message-ID: So, the user will never know what happened on the backend (just get 415). Get it, thank you very much! ------------------ Original ------------------ From: "Maxim Dounin";; Send time: Thursday, Jun 22, 2017 1:24 AM To: "nginx-devel"; Subject: Re: [patch] Http image_filter: return 405 when method isHEADandbodyis empty. Hello! On Thu, Jun 22, 2017 at 01:05:37AM +0800, ?? (hucc) wrote: > Hi, > > On Wednesday, Jun 21, 2017 1:38 AM +0300, Maxim Dounin wrote: > > >When the response is read from a static file, not proxied, and/or > >proxied with "proxy_method GET;", an empty response means exacly > >that: an empty response, and returning 415 is perfectly correct. > > > >The ngx_buf_size() test will limit the incorrect behaviour to > >indeed empty responses (previous version of your patch affected > >all non-image responses), but it won't eliminate incorrect behaviour > >for empty responses. > > > >The root cause of the problem is that in the configuration you've > >provided proxy don't know about image filter, and uses the HEAD > >method in the request to upstream filter despite the fact image > >filter needs a body to return proper response. This doesn't look > >like something easy to fix, as proper fix implies some knowledge > >to be passed between image filter and proxy. > > > >Most trivial solution, as suggested above, would be to use > >"proxy_method GET" explicitly in the configuration. It might be > >actually a good enough solution, as image filter is a special > >module and it requires proper configuration anyway. > > Thank you for the detailed explanation. After reading the code again, > I still have two questions. First, is the following change appropriate ? > > --- a/ngx_http_image_filter_module.c > +++ b/ngx_http_image_filter_module.c > @@ -330,6 +330,15 @@ ngx_http_image_body_filter(ngx_http_request_t *r, ngx_chain_t *in) > } > } > > + if (r->method & NGX_HTTP_HEAD > + && ctx->length > + && ngx_buf_size(in->buf) == 0) > + { > + return ngx_http_filter_finalize_request(r, > + &ngx_http_image_filter_module, > + NGX_HTTP_NOT_ALLOWED); > + } > + > return ngx_http_filter_finalize_request(r, > &ngx_http_image_filter_module, > NGX_HTTP_UNSUPPORTED_MEDIA_TYPE); I don't think it is. > Second, Why not restrict the use of the image filter in the following way ? > > --- a/ngx_http_image_filter_module.c > +++ b/ngx_http_image_filter_module.c > @@ -224,7 +224,9 @@ ngx_http_image_header_filter(ngx_http_request_t *r) > ngx_http_image_filter_ctx_t *ctx; > ngx_http_image_filter_conf_t *conf; > > - if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED) { > + if (r->headers_out.status != NGX_HTTP_OK > + && r->headers_out.status != NGX_HTTP_PARTIAL_CONTENT) > + { > return ngx_http_next_header_filter(r); > } The goal of the image filter module is to process all resources returned, including ones returned with error codes. Such a change will make it useless at least for the use case the module was written for. -- Maxim Dounin http://nginx.org/ _______________________________________________ nginx-devel mailing list nginx-devel at nginx.org http://mailman.nginx.org/mailman/listinfo/nginx-devel From arut at nginx.com Thu Jun 22 08:53:35 2017 From: arut at nginx.com (Roman Arutyunyan) Date: Thu, 22 Jun 2017 08:53:35 +0000 Subject: [nginx] Resolver: added the "async" flag to resolver context. Message-ID: details: http://hg.nginx.org/nginx/rev/d49b74a683b1 branches: changeset: 7040:d49b74a683b1 user: Roman Arutyunyan date: Wed Jun 14 18:13:31 2017 +0300 description: Resolver: added the "async" flag to resolver context. The flag indicates that the resolve handler is called asynchronously after the resolve function ngx_resolve_name()/ngx_resolve_addr() exited. diffstat: src/core/ngx_resolver.c | 5 +++++ src/core/ngx_resolver.h | 3 ++- 2 files changed, 7 insertions(+), 1 deletions(-) diffs (56 lines): diff -r a39bc74873fa -r d49b74a683b1 src/core/ngx_resolver.c --- a/src/core/ngx_resolver.c Mon Jun 19 14:25:42 2017 +0300 +++ b/src/core/ngx_resolver.c Wed Jun 14 18:13:31 2017 +0300 @@ -746,6 +746,7 @@ ngx_resolve_name_locked(ngx_resolver_t * last->next = rn->waiting; rn->waiting = ctx; ctx->state = NGX_AGAIN; + ctx->async = 1; do { ctx->node = rn; @@ -892,6 +893,7 @@ ngx_resolve_name_locked(ngx_resolver_t * rn->waiting = ctx; ctx->state = NGX_AGAIN; + ctx->async = 1; do { ctx->node = rn; @@ -1023,6 +1025,7 @@ ngx_resolve_addr(ngx_resolver_ctx_t *ctx ctx->next = rn->waiting; rn->waiting = ctx; ctx->state = NGX_AGAIN; + ctx->async = 1; ctx->node = rn; /* unlock addr mutex */ @@ -1119,6 +1122,7 @@ ngx_resolve_addr(ngx_resolver_ctx_t *ctx /* unlock addr mutex */ ctx->state = NGX_AGAIN; + ctx->async = 1; ctx->node = rn; return NGX_OK; @@ -3019,6 +3023,7 @@ ngx_resolver_srv_names_handler(ngx_resol srv = cctx->srvs; ctx->count--; + ctx->async |= cctx->async; srv->ctx = NULL; srv->state = cctx->state; diff -r a39bc74873fa -r d49b74a683b1 src/core/ngx_resolver.h --- a/src/core/ngx_resolver.h Mon Jun 19 14:25:42 2017 +0300 +++ b/src/core/ngx_resolver.h Wed Jun 14 18:13:31 2017 +0300 @@ -218,7 +218,8 @@ struct ngx_resolver_ctx_s { void *data; ngx_msec_t timeout; - ngx_uint_t quick; /* unsigned quick:1; */ + unsigned quick:1; + unsigned async:1; ngx_uint_t recursion; ngx_event_t *event; }; From arut at nginx.com Thu Jun 22 08:53:38 2017 From: arut at nginx.com (Roman Arutyunyan) Date: Thu, 22 Jun 2017 08:53:38 +0000 Subject: [nginx] Upstream: fixed running posted requests (ticket #788). Message-ID: details: http://hg.nginx.org/nginx/rev/6169dbad37d8 branches: changeset: 7041:6169dbad37d8 user: Roman Arutyunyan date: Wed Jun 14 20:13:41 2017 +0300 description: Upstream: fixed running posted requests (ticket #788). Previously, the upstream resolve handler always called ngx_http_run_posted_requests() to run posted requests after processing the resolver response. However, if the handler was called directly from the ngx_resolve_name() function (for example, if the resolver response was cached), running posted requests from the handler could lead to the following errors: - If the request was scheduled for termination, it could actually be terminated in the resolve handler. Upper stack frames could reference the freed request object in this case. - If a significant number of requests were posted, and for each of them the resolve handler was called directly from the ngx_resolve_name() function, posted requests could be run recursively and lead to stack overflow. Now ngx_http_run_posted_requests() is only called from asynchronously invoked resolve handlers. diffstat: src/http/ngx_http_upstream.c | 7 ++++++- 1 files changed, 6 insertions(+), 1 deletions(-) diffs (29 lines): diff -r d49b74a683b1 -r 6169dbad37d8 src/http/ngx_http_upstream.c --- a/src/http/ngx_http_upstream.c Wed Jun 14 18:13:31 2017 +0300 +++ b/src/http/ngx_http_upstream.c Wed Jun 14 20:13:41 2017 +0300 @@ -1143,11 +1143,14 @@ ngx_http_upstream_cache_check_range(ngx_ static void ngx_http_upstream_resolve_handler(ngx_resolver_ctx_t *ctx) { + ngx_uint_t run_posted; ngx_connection_t *c; ngx_http_request_t *r; ngx_http_upstream_t *u; ngx_http_upstream_resolved_t *ur; + run_posted = ctx->async; + r = ctx->data; c = r->connection; @@ -1211,7 +1214,9 @@ ngx_http_upstream_resolve_handler(ngx_re failed: - ngx_http_run_posted_requests(c); + if (run_posted) { + ngx_http_run_posted_requests(c); + } } From Nate.Karstens at garmin.com Thu Jun 22 13:24:57 2017 From: Nate.Karstens at garmin.com (Karstens, Nate) Date: Thu, 22 Jun 2017 13:24:57 +0000 Subject: [PATCH 2 of 3] PSK: add PSK identity variable Message-ID: <145451D4E6785E4DA4DFA353A58CB7B2015E16FAB4@OLAWPA-EXMB03.ad.garmin.com> # HG changeset patch # User Nate Karstens # Date 1498137207 18000 # Thu Jun 22 08:13:27 2017 -0500 # Node ID a4635fa4a0cabf5312cda617b8010ea14279ab1c # Parent 3fb3c4928d06029ca1d57853a163c9f56fa90bca PSK: add PSK identity variable Adds the variable $ssl_psk_identity to get the PSK identity used in a connnection secured with a PSK cipher suite. Signed-off-by: Nate Karstens diff -r 3fb3c4928d06 -r a4635fa4a0ca src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c Thu Jun 22 08:13:00 2017 -0500 +++ b/src/event/ngx_event_openssl.c Thu Jun 22 08:13:27 2017 -0500 @@ -4147,6 +4147,33 @@ } +ngx_int_t +ngx_ssl_get_psk_identity(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s) +{ + const char *identity; + size_t len; + + identity = SSL_get_psk_identity(c->ssl->connection); + + if (identity == NULL) { + s->len = 0; + return NGX_OK; + } + + len = ngx_strlen(identity); + + s->data = ngx_pnalloc(pool, len); + if (s->data == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(s->data, identity, len); + s->len = len; + + return NGX_OK; +} + + static time_t ngx_ssl_parse_time( #if OPENSSL_VERSION_NUMBER > 0x10100000L diff -r 3fb3c4928d06 -r a4635fa4a0ca src/event/ngx_event_openssl.h --- a/src/event/ngx_event_openssl.h Thu Jun 22 08:13:00 2017 -0500 +++ b/src/event/ngx_event_openssl.h Thu Jun 22 08:13:27 2017 -0500 @@ -233,6 +233,8 @@ ngx_str_t *s); ngx_int_t ngx_ssl_get_client_v_remain(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s); +ngx_int_t ngx_ssl_get_psk_identity(ngx_connection_t *c, ngx_pool_t *pool, + ngx_str_t *s); ngx_int_t ngx_ssl_handshake(ngx_connection_t *c); diff -r 3fb3c4928d06 -r a4635fa4a0ca src/http/modules/ngx_http_ssl_module.c --- a/src/http/modules/ngx_http_ssl_module.c Thu Jun 22 08:13:00 2017 -0500 +++ b/src/http/modules/ngx_http_ssl_module.c Thu Jun 22 08:13:27 2017 -0500 @@ -336,6 +336,9 @@ { ngx_string("ssl_client_v_remain"), NULL, ngx_http_ssl_variable, (uintptr_t) ngx_ssl_get_client_v_remain, NGX_HTTP_VAR_CHANGEABLE, 0 }, + { ngx_string("ssl_psk_identity"), NULL, ngx_http_ssl_variable, + (uintptr_t) ngx_ssl_get_psk_identity, NGX_HTTP_VAR_CHANGEABLE, 0 }, + { ngx_null_string, NULL, NULL, 0, 0, 0 } }; ________________________________ CONFIDENTIALITY NOTICE: This email and any attachments are for the sole use of the intended recipient(s) and contain information that may be Garmin confidential and/or Garmin legally privileged. If you have received this email in error, please notify the sender by reply email and delete the message. Any disclosure, copying, distribution or use of this communication (including attachments) by someone other than the intended recipient is prohibited. Thank you. From Nate.Karstens at garmin.com Thu Jun 22 13:24:54 2017 From: Nate.Karstens at garmin.com (Karstens, Nate) Date: Thu, 22 Jun 2017 13:24:54 +0000 Subject: [PATCH 1 of 3] PSK: connection support Message-ID: <145451D4E6785E4DA4DFA353A58CB7B2015E16FAA8@OLAWPA-EXMB03.ad.garmin.com> # HG changeset patch # User Nate Karstens # Date 1498137180 18000 # Thu Jun 22 08:13:00 2017 -0500 # Node ID 3fb3c4928d06029ca1d57853a163c9f56fa90bca # Parent 6169dbad37d85fa8642b9d0a51f0f0f6c19dd3d1 PSK: connection support Adds support for TLS connections using PSK cipher suites. A new configuration directive, ssl_psk_file, specifies the file that contains a list of identities and associated PSKs. Each line of the file begins with the identity, followed by a colon character (':'), and ending with the PSK. As required by RFC 4279 section 5.4, PSKs may be entered either as plain text or using hexadecimal encoding. Hexadecimal PSKs must begin with "{HEX}". PSKs without this prefix are assumed to be plain text, but they may optionally begin with "{PLAIN}" to denote this. Some examples: gary:plain_text_password min:{PLAIN}another_text_password cliff:{HEX}ab0123CD The format of the given PSK file is checked at server startup. PSK functionality can be easily tested with the OpenSSL s_client using the "-psk" and "-psk_identity" options. Signed-off-by: Nate Karstens diff -r 6169dbad37d8 -r 3fb3c4928d06 contrib/vim/syntax/nginx.vim --- a/contrib/vim/syntax/nginx.vim Wed Jun 14 20:13:41 2017 +0300 +++ b/contrib/vim/syntax/nginx.vim Thu Jun 22 08:13:00 2017 -0500 @@ -550,6 +550,7 @@ syn keyword ngxDirective contained ssl_prefer_server_ciphers syn keyword ngxDirective contained ssl_preread syn keyword ngxDirective contained ssl_protocols +syn keyword ngxDirective contained ssl_psk_file syn keyword ngxDirective contained ssl_session_cache syn keyword ngxDirective contained ssl_session_ticket_key syn keyword ngxDirective contained ssl_session_tickets diff -r 6169dbad37d8 -r 3fb3c4928d06 src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c Wed Jun 14 20:13:41 2017 +0300 +++ b/src/event/ngx_event_openssl.c Thu Jun 22 08:13:00 2017 -0500 @@ -11,6 +11,7 @@ #define NGX_SSL_PASSWORD_BUFFER_SIZE 4096 +#define NGX_SSL_PSK_BUFFER_SIZE 4096 typedef struct { @@ -23,6 +24,8 @@ static int ngx_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store); static void ngx_ssl_info_callback(const ngx_ssl_conn_t *ssl_conn, int where, int ret); +static unsigned int ngx_ssl_psk_callback(SSL *ssl, const char *identity, + unsigned char *psk, unsigned int max_psk_len); static void ngx_ssl_passwords_cleanup(void *data); static void ngx_ssl_handshake_handler(ngx_event_t *ev); static ngx_int_t ngx_ssl_handle_recv(ngx_connection_t *c, int n); @@ -65,6 +68,11 @@ #endif ASN1_TIME *asn1time); +static ngx_int_t ngx_ssl_psk_convert_hex(u_char **buf, const u_char *psk, + const u_char *last, ngx_uint_t line); +static ngx_int_t ngx_ssl_psk_read(ngx_str_t *file, const char *identity, + unsigned char *psk, int max_psk_len); + static void *ngx_openssl_create_conf(ngx_cycle_t *cycle); static char *ngx_openssl_engine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static void ngx_openssl_exit(ngx_cycle_t *cycle); @@ -114,6 +122,7 @@ int ngx_ssl_next_certificate_index; int ngx_ssl_certificate_name_index; int ngx_ssl_stapling_index; +int ngx_ssl_psk_index; ngx_int_t @@ -225,6 +234,13 @@ return NGX_ERROR; } + ngx_ssl_psk_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); + + if (ngx_ssl_psk_index == -1) { + ngx_ssl_error(NGX_LOG_ALERT, log, 0, "SSL_get_ex_new_index() failed"); + return NGX_ERROR; + } + return NGX_OK; } @@ -345,6 +361,7 @@ SSL_CTX_set_read_ahead(ssl->ctx, 1); SSL_CTX_set_info_callback(ssl->ctx, ngx_ssl_info_callback); + SSL_CTX_set_psk_server_callback(ssl->ctx, ngx_ssl_psk_callback); return NGX_OK; } @@ -875,6 +892,29 @@ } +static unsigned int ngx_ssl_psk_callback(SSL *ssl, const char *identity, + unsigned char *psk, unsigned int max_psk_len) +{ + SSL_CTX *ssl_ctx; + ngx_str_t *psk_file; + ngx_int_t psk_len; + + ssl_ctx = SSL_get_SSL_CTX(ssl); + + psk_file = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_psk_index); + if (psk_file == NULL) { + return 0; + } + + psk_len = ngx_ssl_psk_read(psk_file, identity, psk, max_psk_len); + if (psk_len < 0) { + return 0; + } + + return psk_len; +} + + RSA * ngx_ssl_rsa512_key_callback(ngx_ssl_conn_t *ssl_conn, int is_export, int key_length) @@ -3137,6 +3177,24 @@ #endif +ngx_int_t +ngx_ssl_psk_file(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file) + +{ + ngx_int_t rc; + + if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_psk_index, file) == 0) { + ngx_ssl_error(NGX_LOG_ALERT, ssl->log, 0, + "SSL_CTX_set_ex_data() failed"); + return NGX_ERROR; + } + + rc = ngx_ssl_psk_read(file, NULL, NULL, 0); + + return rc == 0 ? NGX_OK : NGX_ERROR; +} + + void ngx_ssl_cleanup_ctx(void *data) { @@ -4127,6 +4185,223 @@ } +static ngx_int_t +ngx_ssl_psk_convert_hex(u_char **buf, const u_char *psk, const u_char *last, + ngx_uint_t line) +{ + ngx_int_t len; + u_char *out = NULL; + u_char val; + + if (buf != NULL) { + *buf = NULL; + } + + if ((last - psk) & 1) { + len = NGX_ERROR; + goto error; + } + + len = (last - psk) / 2; + + if (buf != NULL) { + out = ngx_alloc(len, ngx_cycle->log); + if (out == NULL) { + return NGX_ERROR; + } + *buf = out; + } + + while (psk < last) { + if (*psk >= '0' && *psk <= '9') { + val = *psk - '0'; + } else if (*psk >= 'a' && *psk <= 'f') { + val = *psk - 'a' + 10; + } else if (*psk >= 'A' && *psk <= 'F') { + val = *psk - 'A' + 10; + } else { + len = NGX_ERROR; + goto error; + } + + val <<= 4; + psk++; + + if (*psk >= '0' && *psk <= '9') { + val += *psk - '0'; + } else if (*psk >= 'a' && *psk <= 'f') { + val += *psk - 'a' + 10; + } else if (*psk >= 'A' && *psk <= 'F') { + val += *psk - 'A' + 10; + } else { + len = NGX_ERROR; + goto error; + } + + psk++; + + if (out != NULL) { + *out = val; + out++; + } + } + +error: + + if (len == NGX_ERROR) { + ngx_ssl_error(NGX_LOG_EMERG, ngx_cycle->log, 0, + "The PSK on line %ui is not valid hex", line); + if (buf != NULL) { + ngx_free(*buf); + *buf = NULL; + } + } + + return len; +} + + +static ngx_int_t +ngx_ssl_psk_read(ngx_str_t *file, const char *identity, unsigned char *psk, + int max_psk_len) +{ + ngx_int_t rc = 0; + ngx_fd_t fd; + ngx_uint_t line; + size_t len; + ssize_t n; + u_char *p, *last, *end; + u_char *psk_ptr, *hex_buf = NULL; + u_char buf[NGX_SSL_PSK_BUFFER_SIZE]; + + static const char plain_tag[7] = "{PLAIN}"; + static const char hex_tag[5] = "{HEX}"; + + fd = ngx_open_file(file->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0); + if (fd == NGX_INVALID_FILE) { + ngx_ssl_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno, + ngx_open_file_n " \"%V\" failed", file); + return NGX_ERROR; + } + + len = 0; + last = buf; + + do { + n = ngx_read_fd(fd, last, NGX_SSL_PSK_BUFFER_SIZE - len); + + if (n == -1) { + ngx_ssl_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno, + ngx_read_fd_n " \"%V\" failed", file); + rc = NGX_ERROR; + goto done; + } + + end = last + n; + + if (len && n == 0) { + *end++ = LF; + } + + for (p = buf, line = 1; ; p = last, line++) { + last = ngx_strlchr(last, end, LF); + + if (last == NULL) { + break; + } + + len = last++ - p; + + if (len && p[len - 1] == CR) { + len--; + } + + if (len == 0) { + continue; + } + + psk_ptr = (u_char *) ngx_strlchr(p, p + len, ':'); + if (psk_ptr == NULL) { + ngx_ssl_error(NGX_LOG_EMERG, ngx_cycle->log, 0, + "Bad format on line %ui of PSK file \"%V\"", + line, file); + rc = NGX_ERROR; + goto done; + } + + *psk_ptr = '\0'; + psk_ptr++; + + if (identity == NULL) { + if (ngx_memcmp(psk_ptr, hex_tag, sizeof(hex_tag)) == 0) { + psk_ptr += sizeof(hex_tag); + if (ngx_ssl_psk_convert_hex(NULL, psk_ptr, p + len, line) + == NGX_ERROR) + { + rc = NGX_ERROR; + goto done; + } + } + + continue; + } + + if (ngx_strcmp(p, identity) != 0) { + continue; + } + + if (ngx_memcmp(psk_ptr, plain_tag, sizeof(plain_tag)) == 0) { + psk_ptr += sizeof(plain_tag); + rc = last - 1 - psk_ptr; + } else if (ngx_memcmp(psk_ptr, hex_tag, sizeof(hex_tag)) == 0) { + psk_ptr += sizeof(hex_tag); + rc = ngx_ssl_psk_convert_hex(&hex_buf, psk_ptr, p + len, line); + if (rc == NGX_ERROR) { + goto done; + } + psk_ptr = hex_buf; + } else { + rc = last - 1 - psk_ptr; + } + + if (rc > max_psk_len) { + rc = 0; + goto done; + } + + ngx_memcpy(psk, psk_ptr, rc); + goto done; + } + + len = end - p; + + if (len == NGX_SSL_PSK_BUFFER_SIZE) { + ngx_ssl_error(NGX_LOG_EMERG, ngx_cycle->log, 0, + "too long line in \"%V\"", file); + rc = NGX_ERROR; + goto done; + } + + ngx_memmove(buf, p, len); + last = buf + len; + + } while (n != 0); + +done: + + if (ngx_close_file(fd) == NGX_FILE_ERROR) { + ngx_ssl_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno, + ngx_close_file_n " %V failed", file); + rc = NGX_ERROR; + } + + ngx_memzero(buf, NGX_SSL_PSK_BUFFER_SIZE); + ngx_free(hex_buf); + + return rc; +} + + static void * ngx_openssl_create_conf(ngx_cycle_t *cycle) { diff -r 6169dbad37d8 -r 3fb3c4928d06 src/event/ngx_event_openssl.h --- a/src/event/ngx_event_openssl.h Wed Jun 14 20:13:41 2017 +0300 +++ b/src/event/ngx_event_openssl.h Thu Jun 22 08:13:00 2017 -0500 @@ -171,6 +171,7 @@ ssize_t builtin_session_cache, ngx_shm_zone_t *shm_zone, time_t timeout); ngx_int_t ngx_ssl_session_ticket_keys(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_array_t *paths); +ngx_int_t ngx_ssl_psk_file(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file); ngx_int_t ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, void *data); ngx_int_t ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c, ngx_uint_t flags); @@ -255,6 +256,7 @@ extern int ngx_ssl_next_certificate_index; extern int ngx_ssl_certificate_name_index; extern int ngx_ssl_stapling_index; +extern int ngx_ssl_psk_index; #endif /* _NGX_EVENT_OPENSSL_H_INCLUDED_ */ diff -r 6169dbad37d8 -r 3fb3c4928d06 src/http/modules/ngx_http_ssl_module.c --- a/src/http/modules/ngx_http_ssl_module.c Wed Jun 14 20:13:41 2017 +0300 +++ b/src/http/modules/ngx_http_ssl_module.c Thu Jun 22 08:13:00 2017 -0500 @@ -234,6 +234,13 @@ offsetof(ngx_http_ssl_srv_conf_t, stapling_verify), NULL }, + { ngx_string("ssl_psk_file"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_ssl_srv_conf_t, psk_file), + NULL }, + ngx_null_command }; @@ -539,6 +546,7 @@ * sscf->shm_zone = NULL; * sscf->stapling_file = { 0, NULL }; * sscf->stapling_responder = { 0, NULL }; + * sscf->psk_file = { 0, NULL }; */ sscf->enable = NGX_CONF_UNSET; @@ -620,6 +628,8 @@ ngx_conf_merge_str_value(conf->stapling_responder, prev->stapling_responder, ""); + ngx_conf_merge_str_value(conf->psk_file, prev->psk_file, ""); + conf->ssl.log = cf->log; if (conf->enable) { @@ -800,6 +810,12 @@ } + if (ngx_ssl_psk_file(cf, &conf->ssl, &conf->psk_file) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + return NGX_CONF_OK; } diff -r 6169dbad37d8 -r 3fb3c4928d06 src/http/modules/ngx_http_ssl_module.h --- a/src/http/modules/ngx_http_ssl_module.h Wed Jun 14 20:13:41 2017 +0300 +++ b/src/http/modules/ngx_http_ssl_module.h Thu Jun 22 08:13:00 2017 -0500 @@ -55,6 +55,8 @@ ngx_str_t stapling_file; ngx_str_t stapling_responder; + ngx_str_t psk_file; + u_char *file; ngx_uint_t line; } ngx_http_ssl_srv_conf_t; ________________________________ CONFIDENTIALITY NOTICE: This email and any attachments are for the sole use of the intended recipient(s) and contain information that may be Garmin confidential and/or Garmin legally privileged. If you have received this email in error, please notify the sender by reply email and delete the message. Any disclosure, copying, distribution or use of this communication (including attachments) by someone other than the intended recipient is prohibited. Thank you. From Nate.Karstens at garmin.com Thu Jun 22 13:24:59 2017 From: Nate.Karstens at garmin.com (Karstens, Nate) Date: Thu, 22 Jun 2017 13:24:59 +0000 Subject: [PATCH 3 of 3] PSK: add identity hint config directive Message-ID: <145451D4E6785E4DA4DFA353A58CB7B2015E16FABB@OLAWPA-EXMB03.ad.garmin.com> # HG changeset patch # User Nate Karstens # Date 1498137243 18000 # Thu Jun 22 08:14:03 2017 -0500 # Node ID b706695658216c88716904519467a36c1aac7ac9 # Parent a4635fa4a0cabf5312cda617b8010ea14279ab1c PSK: add identity hint config directive Adds the directive "ssl_psk_identity_hint" to the ngx_http_ssl_module. This allows the user to specify the PSK identity hint given to the connecting client. Signed-off-by: Nate Karstens diff -r a4635fa4a0ca -r b70669565821 contrib/vim/syntax/nginx.vim --- a/contrib/vim/syntax/nginx.vim Thu Jun 22 08:13:27 2017 -0500 +++ b/contrib/vim/syntax/nginx.vim Thu Jun 22 08:14:03 2017 -0500 @@ -551,6 +551,7 @@ syn keyword ngxDirective contained ssl_preread syn keyword ngxDirective contained ssl_protocols syn keyword ngxDirective contained ssl_psk_file +syn keyword ngxDirective contained ssl_psk_identity_hint syn keyword ngxDirective contained ssl_session_cache syn keyword ngxDirective contained ssl_session_ticket_key syn keyword ngxDirective contained ssl_session_tickets diff -r a4635fa4a0ca -r b70669565821 src/http/modules/ngx_http_ssl_module.c --- a/src/http/modules/ngx_http_ssl_module.c Thu Jun 22 08:13:27 2017 -0500 +++ b/src/http/modules/ngx_http_ssl_module.c Thu Jun 22 08:14:03 2017 -0500 @@ -241,6 +241,13 @@ offsetof(ngx_http_ssl_srv_conf_t, psk_file), NULL }, + { ngx_string("ssl_psk_identity_hint"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_ssl_srv_conf_t, psk_identity_hint), + NULL }, + ngx_null_command }; @@ -550,6 +557,7 @@ * sscf->stapling_file = { 0, NULL }; * sscf->stapling_responder = { 0, NULL }; * sscf->psk_file = { 0, NULL }; + * sscf->psk_identity_hint = { 0, NULL }; */ sscf->enable = NGX_CONF_UNSET; @@ -632,6 +640,7 @@ prev->stapling_responder, ""); ngx_conf_merge_str_value(conf->psk_file, prev->psk_file, ""); + ngx_conf_merge_str_value(conf->psk_identity_hint, prev->psk_identity_hint, ""); conf->ssl.log = cf->log; @@ -819,6 +828,15 @@ return NGX_CONF_ERROR; } + if (conf->psk_identity_hint.len != 0) { + if (SSL_CTX_use_psk_identity_hint(conf->ssl.ctx, + (char *) conf->psk_identity_hint.data) + != 1) + { + return NGX_CONF_ERROR; + } + } + return NGX_CONF_OK; } diff -r a4635fa4a0ca -r b70669565821 src/http/modules/ngx_http_ssl_module.h --- a/src/http/modules/ngx_http_ssl_module.h Thu Jun 22 08:13:27 2017 -0500 +++ b/src/http/modules/ngx_http_ssl_module.h Thu Jun 22 08:14:03 2017 -0500 @@ -56,6 +56,7 @@ ngx_str_t stapling_responder; ngx_str_t psk_file; + ngx_str_t psk_identity_hint; u_char *file; ngx_uint_t line; ________________________________ CONFIDENTIALITY NOTICE: This email and any attachments are for the sole use of the intended recipient(s) and contain information that may be Garmin confidential and/or Garmin legally privileged. If you have received this email in error, please notify the sender by reply email and delete the message. Any disclosure, copying, distribution or use of this communication (including attachments) by someone other than the intended recipient is prohibited. Thank you. From xeioex at nginx.com Thu Jun 22 16:28:41 2017 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Thu, 22 Jun 2017 16:28:41 +0000 Subject: [njs] Log error message if VM creation failed. Message-ID: details: http://hg.nginx.org/njs/rev/61feac0beeb3 branches: changeset: 374:61feac0beeb3 user: Dmitry Volyntsev date: Thu Jun 22 18:52:47 2017 +0300 description: Log error message if VM creation failed. diffstat: nginx/ngx_http_js_module.c | 1 + nginx/ngx_stream_js_module.c | 1 + 2 files changed, 2 insertions(+), 0 deletions(-) diffs (22 lines): diff -r 36947c6cb8c6 -r 61feac0beeb3 nginx/ngx_http_js_module.c --- a/nginx/ngx_http_js_module.c Tue Jun 20 18:06:19 2017 +0300 +++ b/nginx/ngx_http_js_module.c Thu Jun 22 18:52:47 2017 +0300 @@ -1323,6 +1323,7 @@ ngx_http_js_include(ngx_conf_t *cf, ngx_ jlcf->vm = njs_vm_create(mcp, &shared, &externals); if (jlcf->vm == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "failed to create JS VM"); return NGX_CONF_ERROR; } diff -r 36947c6cb8c6 -r 61feac0beeb3 nginx/ngx_stream_js_module.c --- a/nginx/ngx_stream_js_module.c Tue Jun 20 18:06:19 2017 +0300 +++ b/nginx/ngx_stream_js_module.c Thu Jun 22 18:52:47 2017 +0300 @@ -1033,6 +1033,7 @@ ngx_stream_js_include(ngx_conf_t *cf, ng jscf->vm = njs_vm_create(mcp, &shared, &externals); if (jscf->vm == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "failed to create JS VM"); return NGX_CONF_ERROR; } From xeioex at nginx.com Thu Jun 22 16:28:42 2017 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Thu, 22 Jun 2017 16:28:42 +0000 Subject: [njs] Fixed typo in js_include handler. Message-ID: details: http://hg.nginx.org/njs/rev/44ca33e6afdb branches: changeset: 375:44ca33e6afdb user: Dmitry Volyntsev date: Thu Jun 22 18:56:26 2017 +0300 description: Fixed typo in js_include handler. diffstat: nginx/ngx_http_js_module.c | 2 +- nginx/ngx_stream_js_module.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diffs (24 lines): diff -r 61feac0beeb3 -r 44ca33e6afdb nginx/ngx_http_js_module.c --- a/nginx/ngx_http_js_module.c Thu Jun 22 18:52:47 2017 +0300 +++ b/nginx/ngx_http_js_module.c Thu Jun 22 18:56:26 2017 +0300 @@ -1303,7 +1303,7 @@ ngx_http_js_include(ngx_conf_t *cf, ngx_ cln = ngx_pool_cleanup_add(cf->pool, 0); if (cln == NULL) { - return NULL; + return NGX_CONF_ERROR; } cln->handler = ngx_http_js_cleanup_mem_cache_pool; diff -r 61feac0beeb3 -r 44ca33e6afdb nginx/ngx_stream_js_module.c --- a/nginx/ngx_stream_js_module.c Thu Jun 22 18:52:47 2017 +0300 +++ b/nginx/ngx_stream_js_module.c Thu Jun 22 18:56:26 2017 +0300 @@ -1013,7 +1013,7 @@ ngx_stream_js_include(ngx_conf_t *cf, ng cln = ngx_pool_cleanup_add(cf->pool, 0); if (cln == NULL) { - return NULL; + return NGX_CONF_ERROR; } cln->handler = ngx_stream_js_cleanup_mem_cache_pool; From mdounin at mdounin.ru Thu Jun 22 20:02:45 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Thu, 22 Jun 2017 20:02:45 +0000 Subject: [nginx] Upstream: introduced ngx_http_upstream_ssl_handshake_handler(). Message-ID: details: http://hg.nginx.org/nginx/rev/bd2f97a3aecc branches: changeset: 7042:bd2f97a3aecc user: Maxim Dounin date: Thu Jun 22 21:09:06 2017 +0300 description: Upstream: introduced ngx_http_upstream_ssl_handshake_handler(). This change reworks 13a5f4765887 to only run posted requests once, with nothing on stack. Running posted requests with other request functions on stack may result in use-after-free in case of errors, similar to the one reported in #788. To only run posted request once, a separate function was introduced to be used as ssl handshake handler in c->ssl->handler, ngx_http_upstream_ssl_handshake_handler(). The ngx_http_run_posted_requests() is only called in this function, and not in ngx_http_upstream_ssl_handshake() which may be called directly on stack. Additionaly, ngx_http_upstream_ssl_handshake_handler() now does appropriate debug logging of the current subrequest, similar to what is done in other event handlers. diffstat: src/http/ngx_http_upstream.c | 38 ++++++++++++++++++++++++-------------- 1 files changed, 24 insertions(+), 14 deletions(-) diffs (91 lines): diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c --- a/src/http/ngx_http_upstream.c +++ b/src/http/ngx_http_upstream.c @@ -182,7 +182,9 @@ static char *ngx_http_upstream_init_main #if (NGX_HTTP_SSL) static void ngx_http_upstream_ssl_init_connection(ngx_http_request_t *, ngx_http_upstream_t *u, ngx_connection_t *c); -static void ngx_http_upstream_ssl_handshake(ngx_connection_t *c); +static void ngx_http_upstream_ssl_handshake_handler(ngx_connection_t *c); +static void ngx_http_upstream_ssl_handshake(ngx_http_request_t *, + ngx_http_upstream_t *u, ngx_connection_t *c); static ngx_int_t ngx_http_upstream_ssl_name(ngx_http_request_t *r, ngx_http_upstream_t *u, ngx_connection_t *c); #endif @@ -1667,26 +1669,43 @@ ngx_http_upstream_ssl_init_connection(ng ngx_add_timer(c->write, u->conf->connect_timeout); } - c->ssl->handler = ngx_http_upstream_ssl_handshake; + c->ssl->handler = ngx_http_upstream_ssl_handshake_handler; return; } - ngx_http_upstream_ssl_handshake(c); + ngx_http_upstream_ssl_handshake(r, u, c); } static void -ngx_http_upstream_ssl_handshake(ngx_connection_t *c) +ngx_http_upstream_ssl_handshake_handler(ngx_connection_t *c) { - long rc; ngx_http_request_t *r; ngx_http_upstream_t *u; r = c->data; + u = r->upstream; + c = r->connection; ngx_http_set_log_request(c->log, r); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http upstream ssl handshake: \"%V?%V\"", + &r->uri, &r->args); + + ngx_http_upstream_ssl_handshake(r, u, u->peer.connection); + + ngx_http_run_posted_requests(c); +} + + +static void +ngx_http_upstream_ssl_handshake(ngx_http_request_t *r, ngx_http_upstream_t *u, + ngx_connection_t *c) +{ + long rc; + if (c->ssl->handshaked) { if (u->conf->ssl_verify) { @@ -1714,28 +1733,19 @@ ngx_http_upstream_ssl_handshake(ngx_conn c->write->handler = ngx_http_upstream_handler; c->read->handler = ngx_http_upstream_handler; - c = r->connection; - ngx_http_upstream_send_request(r, u, 1); - ngx_http_run_posted_requests(c); return; } if (c->write->timedout) { - c = r->connection; ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_TIMEOUT); - ngx_http_run_posted_requests(c); return; } failed: - c = r->connection; - ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR); - - ngx_http_run_posted_requests(c); } From piotrsikora at google.com Thu Jun 22 20:32:32 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Thu, 22 Jun 2017 13:32:32 -0700 Subject: [PATCH] Proxy: split configured header names and values In-Reply-To: <20170613215302.GL55433@mdounin.ru> References: <20170613215302.GL55433@mdounin.ru> Message-ID: Hey Maxim, > Full patch with the above comments below: Applied, thanks! Best regards, Piotr Sikora From piotrsikora at google.com Thu Jun 22 20:33:05 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Thu, 22 Jun 2017 13:33:05 -0700 Subject: [PATCH 01 of 14] Output chain: propagate last_buf flag to c->send_chain() Message-ID: <5f5d70428655db0889a2.1498163585@piotrsikora.sfo.corp.google.com> # HG changeset patch # User Piotr Sikora # Date 1491708381 25200 # Sat Apr 08 20:26:21 2017 -0700 # Node ID 5f5d70428655db0889a2111d17d912a7383df152 # Parent a39bc74873faf9e5bea616561b43f6ecc55229f9 Output chain: propagate last_buf flag to c->send_chain(). Signed-off-by: Piotr Sikora diff -r a39bc74873fa -r 5f5d70428655 src/core/ngx_output_chain.c --- a/src/core/ngx_output_chain.c +++ b/src/core/ngx_output_chain.c @@ -658,6 +658,7 @@ ngx_chain_writer(void *data, ngx_chain_t ngx_chain_writer_ctx_t *ctx = data; off_t size; + ngx_uint_t last; ngx_chain_t *cl, *ln, *chain; ngx_connection_t *c; @@ -689,9 +690,10 @@ ngx_chain_writer(void *data, ngx_chain_t size += ngx_buf_size(in->buf); - ngx_log_debug2(NGX_LOG_DEBUG_CORE, c->log, 0, - "chain writer buf fl:%d s:%uO", - in->buf->flush, ngx_buf_size(in->buf)); + ngx_log_debug3(NGX_LOG_DEBUG_CORE, c->log, 0, + "chain writer buf fl:%d l:%d s:%uO", + in->buf->flush, in->buf->last_buf, + ngx_buf_size(in->buf)); cl = ngx_alloc_chain_link(ctx->pool); if (cl == NULL) { @@ -707,6 +709,8 @@ ngx_chain_writer(void *data, ngx_chain_t ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0, "chain writer in: %p", ctx->out); + last = 0; + for (cl = ctx->out; cl; cl = cl->next) { #if 1 @@ -732,9 +736,16 @@ ngx_chain_writer(void *data, ngx_chain_t #endif size += ngx_buf_size(cl->buf); + + if (cl->buf->last_buf) { + last = 1; + } } - if (size == 0 && !c->buffered) { + if (size == 0 + && !c->buffered + && !(last && c->need_last_buf)) + { return NGX_OK; } From piotrsikora at google.com Thu Jun 22 20:33:06 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Thu, 22 Jun 2017 13:33:06 -0700 Subject: [PATCH 02 of 14] Upstream keepalive: preserve c->data In-Reply-To: <5f5d70428655db0889a2.1498163585@piotrsikora.sfo.corp.google.com> References: <5f5d70428655db0889a2.1498163585@piotrsikora.sfo.corp.google.com> Message-ID: # HG changeset patch # User Piotr Sikora # Date 1491886301 25200 # Mon Apr 10 21:51:41 2017 -0700 # Node ID a147dd50ee3fb8628b79f4482c552c7c2852a732 # Parent 5f5d70428655db0889a2111d17d912a7383df152 Upstream keepalive: preserve c->data. Signed-off-by: Piotr Sikora diff -r 5f5d70428655 -r a147dd50ee3f src/http/modules/ngx_http_upstream_keepalive_module.c --- a/src/http/modules/ngx_http_upstream_keepalive_module.c +++ b/src/http/modules/ngx_http_upstream_keepalive_module.c @@ -27,6 +27,7 @@ typedef struct { ngx_queue_t queue; ngx_connection_t *connection; + void *data; socklen_t socklen; ngx_sockaddr_t sockaddr; @@ -254,6 +255,7 @@ found: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, "get keepalive peer: using connection %p", c); + c->data = item->data; c->idle = 0; c->sent = 0; c->log = pc->log; @@ -336,6 +338,7 @@ ngx_http_upstream_free_keepalive_peer(ng ngx_queue_insert_head(&kp->conf->cache, q); item->connection = c; + item->data = c->data; pc->connection = NULL; From piotrsikora at google.com Thu Jun 22 20:33:07 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Thu, 22 Jun 2017 13:33:07 -0700 Subject: [PATCH 03 of 14] HTTP/2: add debug logging of control frames In-Reply-To: <5f5d70428655db0889a2.1498163585@piotrsikora.sfo.corp.google.com> References: <5f5d70428655db0889a2.1498163585@piotrsikora.sfo.corp.google.com> Message-ID: <22d178a11e30c4a8576c.1498163587@piotrsikora.sfo.corp.google.com> # HG changeset patch # User Piotr Sikora # Date 1490516711 25200 # Sun Mar 26 01:25:11 2017 -0700 # Node ID 22d178a11e30c4a8576c3ce28859dfe1cc8adec0 # Parent a147dd50ee3fb8628b79f4482c552c7c2852a732 HTTP/2: add debug logging of control frames. Signed-off-by: Piotr Sikora diff -r a147dd50ee3f -r 22d178a11e30 src/http/v2/ngx_http_v2.c --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -41,9 +41,11 @@ /* settings fields */ #define NGX_HTTP_V2_HEADER_TABLE_SIZE_SETTING 0x1 +#define NGX_HTTP_V2_ENABLE_PUSH_SETTING 0x2 #define NGX_HTTP_V2_MAX_STREAMS_SETTING 0x3 #define NGX_HTTP_V2_INIT_WINDOW_SIZE_SETTING 0x4 #define NGX_HTTP_V2_MAX_FRAME_SIZE_SETTING 0x5 +#define NGX_HTTP_V2_HEADER_LIST_SIZE_SETTING 0x6 #define NGX_HTTP_V2_FRAME_BUFFER_SIZE 24 @@ -1946,6 +1948,9 @@ ngx_http_v2_state_settings(ngx_http_v2_c return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); } + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 SETTINGS frame ack:1"); + h2c->settings_ack = 1; return ngx_http_v2_state_complete(h2c, pos, end); @@ -1959,6 +1964,10 @@ ngx_http_v2_state_settings(ngx_http_v2_c return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); } + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 SETTINGS frame params:%uz", + h2c->state.length / NGX_HTTP_V2_SETTINGS_PARAM_SIZE); + return ngx_http_v2_state_settings_params(h2c, pos, end); } @@ -1986,6 +1995,27 @@ ngx_http_v2_state_settings_params(ngx_ht switch (id) { + case NGX_HTTP_V2_HEADER_TABLE_SIZE_SETTING: + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 SETTINGS param HEADER_TABLE_SIZE:%ui " + "(ignored)", value); + break; + + case NGX_HTTP_V2_ENABLE_PUSH_SETTING: + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 SETTINGS param ENABLE_PUSH:%ui " + "(ignored)", value); + break; + + case NGX_HTTP_V2_MAX_STREAMS_SETTING: + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 SETTINGS param MAX_CONCURRENT_STREAMS:%ui " + "(ignored)", value); + break; + case NGX_HTTP_V2_INIT_WINDOW_SIZE_SETTING: if (value > NGX_HTTP_V2_MAX_WINDOW) { @@ -1997,6 +2027,10 @@ ngx_http_v2_state_settings_params(ngx_ht NGX_HTTP_V2_FLOW_CTRL_ERROR); } + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 SETTINGS param INITIAL_WINDOW_SIZE:%ui", + value); + window_delta = value - h2c->init_window; h2c->init_window = value; @@ -2015,16 +2049,34 @@ ngx_http_v2_state_settings_params(ngx_ht NGX_HTTP_V2_PROTOCOL_ERROR); } + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 SETTINGS param MAX_FRAME_SIZE:%ui", + value); + h2c->frame_size = value; break; + case NGX_HTTP_V2_HEADER_LIST_SIZE_SETTING: + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 SETTINGS param MAX_HEADER_LIST_SIZE:%ui " + "(ignored)", value); + break; + default: + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 SETTINGS param 0x%Xi:%ui " + "(ignored)", id, value); break; } pos += NGX_HTTP_V2_SETTINGS_PARAM_SIZE; } + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 send SETTINGS frame ack:1"); + frame = ngx_http_v2_get_frame(h2c, NGX_HTTP_V2_SETTINGS_ACK_SIZE, NGX_HTTP_V2_SETTINGS_FRAME, NGX_HTTP_V2_ACK_FLAG, 0); @@ -2075,12 +2127,16 @@ ngx_http_v2_state_ping(ngx_http_v2_conne } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, - "http2 PING frame, flags: %ud", h2c->state.flags); + "http2 PING frame ack:%ud", + h2c->state.flags & NGX_HTTP_V2_ACK_FLAG ? 1 : 0); if (h2c->state.flags & NGX_HTTP_V2_ACK_FLAG) { return ngx_http_v2_state_skip(h2c, pos, end); } + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 send PING frame ack:1"); + frame = ngx_http_v2_get_frame(h2c, NGX_HTTP_V2_PING_SIZE, NGX_HTTP_V2_PING_FRAME, NGX_HTTP_V2_ACK_FLAG, 0); @@ -2492,8 +2548,11 @@ ngx_http_v2_send_settings(ngx_http_v2_co ngx_http_v2_srv_conf_t *h2scf; ngx_http_v2_out_frame_t *frame; - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, - "http2 send SETTINGS frame"); + len = NGX_HTTP_V2_SETTINGS_PARAM_SIZE * 3; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 send SETTINGS frame params:%uz", + len / NGX_HTTP_V2_SETTINGS_PARAM_SIZE); frame = ngx_palloc(h2c->pool, sizeof(ngx_http_v2_out_frame_t)); if (frame == NULL) { @@ -2505,8 +2564,6 @@ ngx_http_v2_send_settings(ngx_http_v2_co return NGX_ERROR; } - len = NGX_HTTP_V2_SETTINGS_PARAM_SIZE * 3; - buf = ngx_create_temp_buf(h2c->pool, NGX_HTTP_V2_FRAME_HEADER_SIZE + len); if (buf == NULL) { return NGX_ERROR; @@ -2536,15 +2593,27 @@ ngx_http_v2_send_settings(ngx_http_v2_co h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx, ngx_http_v2_module); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 send SETTINGS param MAX_CONCURRENT_STREAMS:%ui", + h2scf->concurrent_streams); + buf->last = ngx_http_v2_write_uint16(buf->last, NGX_HTTP_V2_MAX_STREAMS_SETTING); buf->last = ngx_http_v2_write_uint32(buf->last, h2scf->concurrent_streams); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 send SETTINGS param INITIAL_WINDOW_SIZE:%uz", + h2scf->preread_size); + buf->last = ngx_http_v2_write_uint16(buf->last, NGX_HTTP_V2_INIT_WINDOW_SIZE_SETTING); buf->last = ngx_http_v2_write_uint32(buf->last, h2scf->preread_size); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 send SETTINGS param MAX_FRAME_SIZE:%ud", + NGX_HTTP_V2_MAX_FRAME_SIZE); + buf->last = ngx_http_v2_write_uint16(buf->last, NGX_HTTP_V2_MAX_FRAME_SIZE_SETTING); buf->last = ngx_http_v2_write_uint32(buf->last, From piotrsikora at google.com Thu Jun 22 20:33:08 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Thu, 22 Jun 2017 13:33:08 -0700 Subject: [PATCH 04 of 14] HTTP/2: s/client/peer/ In-Reply-To: <5f5d70428655db0889a2.1498163585@piotrsikora.sfo.corp.google.com> References: <5f5d70428655db0889a2.1498163585@piotrsikora.sfo.corp.google.com> Message-ID: <912d9cf36783146e61a6.1498163588@piotrsikora.sfo.corp.google.com> # HG changeset patch # User Piotr Sikora # Date 1485917964 28800 # Tue Jan 31 18:59:24 2017 -0800 # Node ID 912d9cf36783146e61a68d554253e70956ea9125 # Parent 22d178a11e30c4a8576c3ce28859dfe1cc8adec0 HTTP/2: s/client/peer/. No functional changes. Signed-off-by: Piotr Sikora diff -r 22d178a11e30 -r 912d9cf36783 src/http/v2/ngx_http_v2.c --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -316,7 +316,7 @@ ngx_http_v2_read_handler(ngx_event_t *re h2c = c->data; if (rev->timedout) { - ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "peer timed out"); ngx_http_v2_finalize_connection(h2c, NGX_HTTP_V2_PROTOCOL_ERROR); return; } @@ -368,7 +368,7 @@ ngx_http_v2_read_handler(ngx_event_t *re if (n == 0 && (h2c->state.incomplete || h2c->processing)) { ngx_log_error(NGX_LOG_INFO, c->log, 0, - "client prematurely closed connection"); + "peer prematurely closed connection"); } if (n == 0 || n == NGX_ERROR) { @@ -774,7 +774,7 @@ ngx_http_v2_state_data(ngx_http_v2_conne if (h2c->state.length == 0) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, - "client sent padded DATA frame " + "peer sent padded DATA frame " "with incorrect length: 0"); return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); @@ -789,7 +789,7 @@ ngx_http_v2_state_data(ngx_http_v2_conne if (h2c->state.padding >= size) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, - "client sent padded DATA frame " + "peer sent padded DATA frame " "with incorrect length: %uz, padding: %uz", size, h2c->state.padding); @@ -805,7 +805,7 @@ ngx_http_v2_state_data(ngx_http_v2_conne if (size > h2c->recv_window) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, - "client violated connection flow control: " + "peer violated connection flow control: " "received DATA frame length %uz, available window %uz", size, h2c->recv_window); @@ -840,7 +840,7 @@ ngx_http_v2_state_data(ngx_http_v2_conne if (size > stream->recv_window) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, - "client violated flow control for stream %ui: " + "peer violated flow control for stream %ui: " "received DATA frame length %uz, available window %uz", node->id, size, stream->recv_window); @@ -874,7 +874,7 @@ ngx_http_v2_state_data(ngx_http_v2_conne if (stream->in_closed) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, - "client sent DATA frame for half-closed stream %ui", + "peer sent DATA frame for half-closed stream %ui", node->id); if (ngx_http_v2_terminate_stream(h2c, stream, @@ -1002,7 +1002,7 @@ ngx_http_v2_state_headers(ngx_http_v2_co if (h2c->state.length < size) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, - "client sent HEADERS frame with incorrect length %uz", + "peer sent HEADERS frame with incorrect length %uz", h2c->state.length); return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); @@ -1010,7 +1010,7 @@ ngx_http_v2_state_headers(ngx_http_v2_co if (h2c->state.length == size) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, - "client sent HEADERS frame with empty header block"); + "peer sent HEADERS frame with empty header block"); return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); } @@ -1033,7 +1033,7 @@ ngx_http_v2_state_headers(ngx_http_v2_co if (h2c->state.padding > h2c->state.length) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, - "client sent padded HEADERS frame " + "peer sent padded HEADERS frame " "with incorrect length: %uz, padding: %uz", h2c->state.length, h2c->state.padding); @@ -1064,8 +1064,8 @@ ngx_http_v2_state_headers(ngx_http_v2_co if (h2c->state.sid % 2 == 0 || h2c->state.sid <= h2c->last_sid) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, - "client sent HEADERS frame with incorrect identifier " - "%ui, the last was %ui", h2c->state.sid, h2c->last_sid); + "peer sent HEADERS frame with incorrect identifier %ui, " + "the last was %ui", h2c->state.sid, h2c->last_sid); return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR); } @@ -1079,7 +1079,7 @@ ngx_http_v2_state_headers(ngx_http_v2_co if (depend == h2c->state.sid) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, - "client sent HEADERS frame for stream %ui " + "peer sent HEADERS frame for stream %ui " "with incorrect dependency", h2c->state.sid); status = NGX_HTTP_V2_PROTOCOL_ERROR; @@ -1104,7 +1104,7 @@ ngx_http_v2_state_headers(ngx_http_v2_co && h2scf->preread_size < NGX_HTTP_V2_DEFAULT_WINDOW) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, - "client sent stream with data " + "peer sent stream with data " "before settings were acknowledged"); status = NGX_HTTP_V2_REFUSED_STREAM; @@ -1224,14 +1224,14 @@ ngx_http_v2_state_header_block(ngx_http_ if (value == NGX_DECLINED) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, - "client sent header block with too long %s value", + "peer sent header block with too long %s value", size_update ? "size update" : "header index"); return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_COMP_ERROR); } ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, - "client sent header block with incorrect length"); + "peer sent header block with incorrect length"); return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); } @@ -1283,7 +1283,7 @@ ngx_http_v2_state_field_len(ngx_http_v2_ if (h2c->state.length < 1) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, - "client sent header block with incorrect length"); + "peer sent header block with incorrect length"); return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); } @@ -1304,13 +1304,13 @@ ngx_http_v2_state_field_len(ngx_http_v2_ if (len == NGX_DECLINED) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, - "client sent header field with too long length value"); + "peer sent header field with too long length value"); return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_COMP_ERROR); } ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, - "client sent header block with incorrect length"); + "peer sent header block with incorrect length"); return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); } @@ -1324,7 +1324,7 @@ ngx_http_v2_state_field_len(ngx_http_v2_ if ((size_t) len > h2scf->max_field_size) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, - "client exceeded http2_max_field_size limit"); + "peer exceeded http2_max_field_size limit"); return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_ENHANCE_YOUR_CALM); } @@ -1378,7 +1378,7 @@ ngx_http_v2_state_field_huff(ngx_http_v2 != NGX_OK) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, - "client sent invalid encoded header field"); + "peer sent invalid encoded header field"); return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_COMP_ERROR); } @@ -1397,7 +1397,7 @@ ngx_http_v2_state_field_huff(ngx_http_v2 if (h2c->state.flags & NGX_HTTP_V2_END_HEADERS_FLAG) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, - "client sent header field with incorrect length"); + "peer sent header field with incorrect length"); return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); } @@ -1442,7 +1442,7 @@ ngx_http_v2_state_field_raw(ngx_http_v2_ if (h2c->state.flags & NGX_HTTP_V2_END_HEADERS_FLAG) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, - "client sent header field with incorrect length"); + "peer sent header field with incorrect length"); return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); } @@ -1484,7 +1484,7 @@ ngx_http_v2_state_field_skip(ngx_http_v2 if (h2c->state.flags & NGX_HTTP_V2_END_HEADERS_FLAG) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, - "client sent header field with incorrect length"); + "peer sent header field with incorrect length"); return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); } @@ -1531,7 +1531,7 @@ ngx_http_v2_state_process_header(ngx_htt if (len > h2c->state.header_limit) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, - "client exceeded http2_max_header_size limit"); + "peer exceeded http2_max_header_size limit"); return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_ENHANCE_YOUR_CALM); } @@ -1601,7 +1601,7 @@ ngx_http_v2_state_process_header(ngx_htt if (cscf->ignore_invalid_headers) { ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, - "client sent invalid header: \"%V\"", &header->name); + "peer sent invalid header: \"%V\"", &header->name); return ngx_http_v2_state_header_complete(h2c, pos, end); } @@ -1727,7 +1727,7 @@ ngx_http_v2_handle_continuation(ngx_http if (ngx_http_v2_parse_type(head) != NGX_HTTP_V2_CONTINUATION_FRAME) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, - "client sent inappropriate frame while CONTINUATION was expected"); + "peer sent inappropriate frame while CONTINUATION was expected"); return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR); } @@ -1736,7 +1736,7 @@ ngx_http_v2_handle_continuation(ngx_http if (h2c->state.sid != ngx_http_v2_parse_sid(&p[5])) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, - "client sent CONTINUATION frame with incorrect identifier"); + "peer sent CONTINUATION frame with incorrect identifier"); return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR); } @@ -1768,7 +1768,7 @@ ngx_http_v2_state_priority(ngx_http_v2_c if (h2c->state.length != NGX_HTTP_V2_PRIORITY_SIZE) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, - "client sent PRIORITY frame with incorrect length %uz", + "peer sent PRIORITY frame with incorrect length %uz", h2c->state.length); return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); @@ -1793,14 +1793,14 @@ ngx_http_v2_state_priority(ngx_http_v2_c if (h2c->state.sid == 0) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, - "client sent PRIORITY frame with incorrect identifier"); + "peer sent PRIORITY frame with incorrect identifier"); return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR); } if (depend == h2c->state.sid) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, - "client sent PRIORITY frame for stream %ui " + "peer sent PRIORITY frame for stream %ui " "with incorrect dependency", h2c->state.sid); node = ngx_http_v2_get_node_by_id(h2c, h2c->state.sid, 0); @@ -1864,7 +1864,7 @@ ngx_http_v2_state_rst_stream(ngx_http_v2 if (h2c->state.length != NGX_HTTP_V2_RST_STREAM_SIZE) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, - "client sent RST_STREAM frame with incorrect length %uz", + "peer sent RST_STREAM frame with incorrect length %uz", h2c->state.length); return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); @@ -1885,7 +1885,7 @@ ngx_http_v2_state_rst_stream(ngx_http_v2 if (h2c->state.sid == 0) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, - "client sent RST_STREAM frame with incorrect identifier"); + "peer sent RST_STREAM frame with incorrect identifier"); return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR); } @@ -1911,18 +1911,18 @@ ngx_http_v2_state_rst_stream(ngx_http_v2 case NGX_HTTP_V2_CANCEL: ngx_log_error(NGX_LOG_INFO, fc->log, 0, - "client canceled stream %ui", h2c->state.sid); + "peer canceled stream %ui", h2c->state.sid); break; case NGX_HTTP_V2_INTERNAL_ERROR: ngx_log_error(NGX_LOG_INFO, fc->log, 0, - "client terminated stream %ui due to internal error", + "peer terminated stream %ui due to internal error", h2c->state.sid); break; default: ngx_log_error(NGX_LOG_INFO, fc->log, 0, - "client terminated stream %ui with status %ui", + "peer terminated stream %ui with status %ui", h2c->state.sid, status); break; } @@ -1942,7 +1942,7 @@ ngx_http_v2_state_settings(ngx_http_v2_c if (h2c->state.length != 0) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, - "client sent SETTINGS frame with the ACK flag " + "peer sent SETTINGS frame with the ACK flag " "and nonzero length"); return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); @@ -1958,7 +1958,7 @@ ngx_http_v2_state_settings(ngx_http_v2_c if (h2c->state.length % NGX_HTTP_V2_SETTINGS_PARAM_SIZE) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, - "client sent SETTINGS frame with incorrect length %uz", + "peer sent SETTINGS frame with incorrect length %uz", h2c->state.length); return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); @@ -2020,7 +2020,7 @@ ngx_http_v2_state_settings_params(ngx_ht if (value > NGX_HTTP_V2_MAX_WINDOW) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, - "client sent SETTINGS frame with incorrect " + "peer sent SETTINGS frame with incorrect " "INITIAL_WINDOW_SIZE value %ui", value); return ngx_http_v2_connection_error(h2c, @@ -2042,7 +2042,7 @@ ngx_http_v2_state_settings_params(ngx_ht || value < NGX_HTTP_V2_DEFAULT_FRAME_SIZE) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, - "client sent SETTINGS frame with incorrect " + "peer sent SETTINGS frame with incorrect " "MAX_FRAME_SIZE value %ui", value); return ngx_http_v2_connection_error(h2c, @@ -2102,7 +2102,7 @@ ngx_http_v2_state_push_promise(ngx_http_ u_char *end) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, - "client sent PUSH_PROMISE frame"); + "peer sent PUSH_PROMISE frame"); return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR); } @@ -2116,7 +2116,7 @@ ngx_http_v2_state_ping(ngx_http_v2_conne if (h2c->state.length != NGX_HTTP_V2_PING_SIZE) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, - "client sent PING frame with incorrect length %uz", + "peer sent PING frame with incorrect length %uz", h2c->state.length); return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); @@ -2164,7 +2164,7 @@ ngx_http_v2_state_goaway(ngx_http_v2_con if (h2c->state.length < NGX_HTTP_V2_GOAWAY_SIZE) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, - "client sent GOAWAY frame " + "peer sent GOAWAY frame " "with incorrect length %uz", h2c->state.length); return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); @@ -2203,7 +2203,7 @@ ngx_http_v2_state_window_update(ngx_http if (h2c->state.length != NGX_HTTP_V2_WINDOW_UPDATE_SIZE) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, - "client sent WINDOW_UPDATE frame " + "peer sent WINDOW_UPDATE frame " "with incorrect length %uz", h2c->state.length); return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); @@ -2275,7 +2275,7 @@ ngx_http_v2_state_window_update(ngx_http if (window > (size_t) (NGX_HTTP_V2_MAX_WINDOW - stream->send_window)) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, - "client violated flow control for stream %ui: " + "peer violated flow control for stream %ui: " "received WINDOW_UPDATE frame " "with window increment %uz " "not allowed for window %z", @@ -2312,7 +2312,7 @@ ngx_http_v2_state_window_update(ngx_http if (window > NGX_HTTP_V2_MAX_WINDOW - h2c->send_window) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, - "client violated connection flow control: " + "peer violated connection flow control: " "received WINDOW_UPDATE frame " "with window increment %uz " "not allowed for window %uz", @@ -2355,7 +2355,7 @@ ngx_http_v2_state_continuation(ngx_http_ u_char *end) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, - "client sent unexpected CONTINUATION frame"); + "peer sent unexpected CONTINUATION frame"); return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR); } @@ -3103,7 +3103,7 @@ ngx_http_v2_validate_header(ngx_http_req case CR: case ':': ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, - "client sent invalid header name: \"%V\"", + "peer sent invalid header name: \"%V\"", &header->name); return NGX_ERROR; @@ -3111,7 +3111,7 @@ ngx_http_v2_validate_header(ngx_http_req if (ch >= 'A' && ch <= 'Z') { ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, - "client sent invalid header name: \"%V\"", + "peer sent invalid header name: \"%V\"", &header->name); return NGX_ERROR; @@ -3128,7 +3128,7 @@ ngx_http_v2_validate_header(ngx_http_req case LF: case CR: ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, - "client sent header \"%V\" with " + "peer sent header \"%V\" with " "invalid value: \"%V\"", &header->name, &header->value); @@ -3182,7 +3182,7 @@ ngx_http_v2_pseudo_header(ngx_http_reque } ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, - "client sent unknown pseudo-header \":%V\"", + "peer sent unknown pseudo-header \":%V\"", &header->name); return NGX_DECLINED; @@ -4202,7 +4202,7 @@ ngx_http_v2_close_stream_handler(ngx_eve "http2 close stream handler"); if (ev->timedout) { - ngx_log_error(NGX_LOG_INFO, fc->log, NGX_ETIMEDOUT, "client timed out"); + ngx_log_error(NGX_LOG_INFO, fc->log, NGX_ETIMEDOUT, "peer timed out"); fc->timedout = 1; @@ -4270,7 +4270,7 @@ ngx_http_v2_idle_handler(ngx_event_t *re if (rev->pending_eof) { c->log->handler = NULL; ngx_log_error(NGX_LOG_INFO, c->log, rev->kq_errno, - "kevent() reported that client %V closed " + "kevent() reported that peer %V closed " "idle connection", &c->addr_text); #if (NGX_HTTP_SSL) if (c->ssl) { diff -r 22d178a11e30 -r 912d9cf36783 src/http/v2/ngx_http_v2_table.c --- a/src/http/v2/ngx_http_v2_table.c +++ b/src/http/v2/ngx_http_v2_table.c @@ -96,7 +96,7 @@ ngx_http_v2_get_indexed_header(ngx_http_ if (index == 0) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, - "client sent invalid hpack table index 0"); + "peer sent invalid hpack table index 0"); return NGX_ERROR; } @@ -165,7 +165,7 @@ ngx_http_v2_get_indexed_header(ngx_http_ } ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, - "client sent out of bound hpack table index: %ui", index); + "peer sent out of bound hpack table index: %ui", index); return NGX_ERROR; } @@ -326,7 +326,7 @@ ngx_http_v2_table_size(ngx_http_v2_conne if (size > NGX_HTTP_V2_TABLE_SIZE) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, - "client sent invalid table size update: %uz", size); + "peer sent invalid table size update: %uz", size); return NGX_ERROR; } From piotrsikora at google.com Thu Jun 22 20:33:09 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Thu, 22 Jun 2017 13:33:09 -0700 Subject: [PATCH 05 of 14] HTTP/2: introduce h2c->conf_ctx In-Reply-To: <5f5d70428655db0889a2.1498163585@piotrsikora.sfo.corp.google.com> References: <5f5d70428655db0889a2.1498163585@piotrsikora.sfo.corp.google.com> Message-ID: <24b0f9f4ebfa560edd98.1498163589@piotrsikora.sfo.corp.google.com> # HG changeset patch # User Piotr Sikora # Date 1489106035 25200 # Thu Mar 09 17:33:55 2017 -0700 # Node ID 24b0f9f4ebfa560edd984146548ab07925dba73f # Parent 912d9cf36783146e61a68d554253e70956ea9125 HTTP/2: introduce h2c->conf_ctx. No functional changes. Signed-off-by: Piotr Sikora diff -r 912d9cf36783 -r 24b0f9f4ebfa src/http/v2/ngx_http_v2.c --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -239,6 +239,7 @@ ngx_http_v2_init(ngx_event_t *rev) h2c->connection = c; h2c->http_connection = hc; + h2c->conf_ctx = hc->conf_ctx; h2c->send_window = NGX_HTTP_V2_DEFAULT_WINDOW; h2c->recv_window = NGX_HTTP_V2_MAX_WINDOW; @@ -247,7 +248,7 @@ ngx_http_v2_init(ngx_event_t *rev) h2c->frame_size = NGX_HTTP_V2_DEFAULT_FRAME_SIZE; - h2scf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v2_module); + h2scf = ngx_http_get_module_srv_conf(h2c->conf_ctx, ngx_http_v2_module); h2c->pool = ngx_create_pool(h2scf->pool_size, h2c->connection->log); if (h2c->pool == NULL) { @@ -349,8 +350,7 @@ ngx_http_v2_read_handler(ngx_event_t *re return; } - h2mcf = ngx_http_get_module_main_conf(h2c->http_connection->conf_ctx, - ngx_http_v2_module); + h2mcf = ngx_http_get_module_main_conf(h2c->conf_ctx, ngx_http_v2_module); available = h2mcf->recv_buffer_size - 2 * NGX_HTTP_V2_STATE_BUFFER_SIZE; @@ -511,8 +511,7 @@ ngx_http_v2_send_output_queue(ngx_http_v goto error; } - clcf = ngx_http_get_module_loc_conf(h2c->http_connection->conf_ctx, - ngx_http_core_module); + clcf = ngx_http_get_module_loc_conf(h2c->conf_ctx, ngx_http_core_module); if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) { goto error; @@ -624,8 +623,7 @@ ngx_http_v2_handle_connection(ngx_http_v return; } - h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx, - ngx_http_v2_module); + h2scf = ngx_http_get_module_srv_conf(h2c->conf_ctx, ngx_http_v2_module); if (h2c->state.incomplete) { ngx_add_timer(c->read, h2scf->recv_timeout); return; @@ -1086,8 +1084,7 @@ ngx_http_v2_state_headers(ngx_http_v2_co goto rst_stream; } - h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx, - ngx_http_v2_module); + h2scf = ngx_http_get_module_srv_conf(h2c->conf_ctx, ngx_http_v2_module); h2c->state.header_limit = h2scf->max_header_size; @@ -1319,8 +1316,7 @@ ngx_http_v2_state_field_len(ngx_http_v2_ "http2 hpack %s string length: %i", huff ? "encoded" : "raw", len); - h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx, - ngx_http_v2_module); + h2scf = ngx_http_get_module_srv_conf(h2c->conf_ctx, ngx_http_v2_module); if ((size_t) len > h2scf->max_field_size) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, @@ -2590,8 +2586,7 @@ ngx_http_v2_send_settings(ngx_http_v2_co buf->last = ngx_http_v2_write_sid(buf->last, 0); - h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx, - ngx_http_v2_module); + h2scf = ngx_http_get_module_srv_conf(h2c->conf_ctx, ngx_http_v2_module); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, "http2 send SETTINGS param MAX_CONCURRENT_STREAMS:%ui", @@ -2953,8 +2948,7 @@ ngx_http_v2_get_node_by_id(ngx_http_v2_c ngx_http_v2_node_t *node; ngx_http_v2_srv_conf_t *h2scf; - h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx, - ngx_http_v2_module); + h2scf = ngx_http_get_module_srv_conf(h2c->conf_ctx, ngx_http_v2_module); index = ngx_http_v2_index(h2scf, sid); @@ -2998,8 +2992,7 @@ ngx_http_v2_get_closed_node(ngx_http_v2_ ngx_http_v2_node_t *node, **next, *n, *parent, *child; ngx_http_v2_srv_conf_t *h2scf; - h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx, - ngx_http_v2_module); + h2scf = ngx_http_get_module_srv_conf(h2c->conf_ctx, ngx_http_v2_module); h2c->closed_nodes--; @@ -4287,8 +4280,7 @@ ngx_http_v2_idle_handler(ngx_event_t *re c->destroyed = 0; ngx_reusable_connection(c, 0); - h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx, - ngx_http_v2_module); + h2scf = ngx_http_get_module_srv_conf(h2c->conf_ctx, ngx_http_v2_module); h2c->pool = ngx_create_pool(h2scf->pool_size, h2c->connection->log); if (h2c->pool == NULL) { @@ -4337,8 +4329,7 @@ ngx_http_v2_finalize_connection(ngx_http h2c->last_out = NULL; - h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx, - ngx_http_v2_module); + h2scf = ngx_http_get_module_srv_conf(h2c->conf_ctx, ngx_http_v2_module); size = ngx_http_v2_index_size(h2scf); @@ -4393,8 +4384,7 @@ ngx_http_v2_adjust_windows(ngx_http_v2_c ngx_http_v2_stream_t *stream; ngx_http_v2_srv_conf_t *h2scf; - h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx, - ngx_http_v2_module); + h2scf = ngx_http_get_module_srv_conf(h2c->conf_ctx, ngx_http_v2_module); size = ngx_http_v2_index_size(h2scf); diff -r 912d9cf36783 -r 24b0f9f4ebfa src/http/v2/ngx_http_v2.h --- a/src/http/v2/ngx_http_v2.h +++ b/src/http/v2/ngx_http_v2.h @@ -113,6 +113,7 @@ typedef struct { struct ngx_http_v2_connection_s { ngx_connection_t *connection; ngx_http_connection_t *http_connection; + ngx_http_conf_ctx_t *conf_ctx; ngx_uint_t processing; From piotrsikora at google.com Thu Jun 22 20:33:10 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Thu, 22 Jun 2017 13:33:10 -0700 Subject: [PATCH 06 of 14] HTTP/2: introduce stream->fake_connection In-Reply-To: <5f5d70428655db0889a2.1498163585@piotrsikora.sfo.corp.google.com> References: <5f5d70428655db0889a2.1498163585@piotrsikora.sfo.corp.google.com> Message-ID: <64d12a65309eca385905.1498163590@piotrsikora.sfo.corp.google.com> # HG changeset patch # User Piotr Sikora # Date 1489111358 28800 # Thu Mar 09 18:02:38 2017 -0800 # Node ID 64d12a65309eca3859055a04eb02cc14f3b3168d # Parent 24b0f9f4ebfa560edd984146548ab07925dba73f HTTP/2: introduce stream->fake_connection. No functional changes. Signed-off-by: Piotr Sikora diff -r 24b0f9f4ebfa -r 64d12a65309e src/http/v2/ngx_http_v2.c --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -1900,7 +1900,7 @@ ngx_http_v2_state_rst_stream(ngx_http_v2 stream->in_closed = 1; stream->out_closed = 1; - fc = stream->request->connection; + fc = stream->fake_connection; fc->error = 1; switch (status) { @@ -2293,7 +2293,7 @@ ngx_http_v2_state_window_update(ngx_http if (stream->exhausted) { stream->exhausted = 0; - wev = stream->request->connection->write; + wev = stream->fake_connection->write; wev->active = 0; wev->ready = 1; @@ -2328,7 +2328,7 @@ ngx_http_v2_state_window_update(ngx_http stream->waiting = 0; - wev = stream->request->connection->write; + wev = stream->fake_connection->write; wev->active = 0; wev->ready = 1; @@ -2444,17 +2444,18 @@ static u_char * ngx_http_v2_state_headers_save(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end, ngx_http_v2_handler_pt handler) { - ngx_event_t *rev; - ngx_http_request_t *r; + ngx_connection_t *fc; + ngx_http_v2_stream_t *stream; ngx_http_core_srv_conf_t *cscf; if (h2c->state.stream) { - r = h2c->state.stream->request; - rev = r->connection->read; - - if (!rev->timer_set) { - cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); - ngx_add_timer(rev, cscf->client_header_timeout); + stream = h2c->state.stream; + fc = stream->fake_connection; + + if (!fc->read->timer_set) { + cscf = ngx_http_get_module_srv_conf(stream->request, + ngx_http_core_module); + ngx_add_timer(fc->read, cscf->client_header_timeout); } } @@ -2928,6 +2929,7 @@ ngx_http_v2_create_stream(ngx_http_v2_co stream->request = r; stream->connection = h2c; + stream->fake_connection = fc; h2scf = ngx_http_get_module_srv_conf(r, ngx_http_v2_module); @@ -3712,7 +3714,7 @@ ngx_http_v2_read_request_body(ngx_http_r } if (!buf) { - ngx_add_timer(r->connection->read, clcf->client_body_timeout); + ngx_add_timer(stream->fake_connection->read, clcf->client_body_timeout); } r->read_event_handler = ngx_http_v2_read_client_request_body_handler; @@ -3732,7 +3734,7 @@ ngx_http_v2_process_request_body(ngx_htt ngx_http_request_body_t *rb; ngx_http_core_loc_conf_t *clcf; - fc = r->connection; + fc = r->stream->fake_connection; rb = r->request_body; buf = rb->buf; @@ -3903,7 +3905,7 @@ ngx_http_v2_read_client_request_body_han { ngx_connection_t *fc; - fc = r->connection; + fc = r->stream->fake_connection; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0, "http2 read client request body handler"); @@ -3942,7 +3944,7 @@ ngx_http_v2_read_unbuffered_request_body ngx_http_core_loc_conf_t *clcf; stream = r->stream; - fc = r->connection; + fc = stream->fake_connection; if (fc->read->timedout) { if (stream->recv_window) { @@ -4042,7 +4044,7 @@ ngx_http_v2_terminate_stream(ngx_http_v2 stream->rst_sent = 1; stream->skip_data = 1; - fc = stream->request->connection; + fc = stream->fake_connection; fc->error = 1; rev = fc->read; @@ -4068,7 +4070,7 @@ ngx_http_v2_close_stream(ngx_http_v2_str "http2 close stream %ui, queued %ui, processing %ui", node->id, stream->queued, h2c->processing); - fc = stream->request->connection; + fc = stream->fake_connection; if (stream->queued) { fc->write->handler = ngx_http_v2_close_stream_handler; @@ -4302,7 +4304,6 @@ ngx_http_v2_finalize_connection(ngx_http ngx_uint_t i, size; ngx_event_t *ev; ngx_connection_t *c, *fc; - ngx_http_request_t *r; ngx_http_v2_node_t *node; ngx_http_v2_stream_t *stream; ngx_http_v2_srv_conf_t *h2scf; @@ -4344,9 +4345,7 @@ ngx_http_v2_finalize_connection(ngx_http stream->waiting = 0; - r = stream->request; - fc = r->connection; - + fc = stream->fake_connection; fc->error = 1; if (stream->queued) { @@ -4420,7 +4419,7 @@ ngx_http_v2_adjust_windows(ngx_http_v2_c if (stream->send_window > 0 && stream->exhausted) { stream->exhausted = 0; - wev = stream->request->connection->write; + wev = stream->fake_connection->write; wev->active = 0; wev->ready = 1; diff -r 24b0f9f4ebfa -r 64d12a65309e src/http/v2/ngx_http_v2.h --- a/src/http/v2/ngx_http_v2.h +++ b/src/http/v2/ngx_http_v2.h @@ -165,6 +165,8 @@ struct ngx_http_v2_node_s { struct ngx_http_v2_stream_s { + ngx_connection_t *fake_connection; + ngx_http_request_t *request; ngx_http_v2_connection_t *connection; ngx_http_v2_node_t *node; diff -r 24b0f9f4ebfa -r 64d12a65309e src/http/v2/ngx_http_v2_filter_module.c --- a/src/http/v2/ngx_http_v2_filter_module.c +++ b/src/http/v2/ngx_http_v2_filter_module.c @@ -173,7 +173,7 @@ ngx_http_v2_header_filter(ngx_http_reque return NGX_OK; } - fc = r->connection; + fc = r->stream->fake_connection; if (fc->error) { return NGX_ERROR; @@ -896,7 +896,7 @@ ngx_http_v2_create_headers_frame(ngx_htt cl->next = NULL; frame->last = cl; - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, stream->fake_connection->log, 0, "http2:%ui create HEADERS frame %p: len:%uz", stream->node->id, frame, frame->length); @@ -1175,7 +1175,7 @@ ngx_http_v2_filter_get_data_frame(ngx_ht flags = last->buf->last_buf ? NGX_HTTP_V2_END_STREAM_FLAG : 0; - ngx_log_debug4(NGX_LOG_DEBUG_HTTP, stream->request->connection->log, 0, + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, stream->fake_connection->log, 0, "http2:%ui create DATA frame %p: len:%uz flags:%ui", stream->node->id, frame, len, (ngx_uint_t) flags); @@ -1457,11 +1457,8 @@ static ngx_inline void ngx_http_v2_handle_frame(ngx_http_v2_stream_t *stream, ngx_http_v2_out_frame_t *frame) { - ngx_http_request_t *r; - - r = stream->request; - - r->connection->sent += NGX_HTTP_V2_FRAME_HEADER_SIZE + frame->length; + stream->fake_connection->sent += NGX_HTTP_V2_FRAME_HEADER_SIZE + + frame->length; if (frame->fin) { stream->out_closed = 1; @@ -1485,7 +1482,7 @@ ngx_http_v2_handle_stream(ngx_http_v2_co return; } - fc = stream->request->connection; + fc = stream->fake_connection; if (!fc->error && stream->exhausted) { return; @@ -1561,7 +1558,7 @@ ngx_http_v2_filter_cleanup(void *data) stream->waiting = 0; - wev = stream->request->connection->write; + wev = stream->fake_connection->write; wev->active = 0; wev->ready = 1; From piotrsikora at google.com Thu Jun 22 20:33:11 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Thu, 22 Jun 2017 13:33:11 -0700 Subject: [PATCH 07 of 14] HTTP/2: introduce ngx_http_v2_handle_event() In-Reply-To: <5f5d70428655db0889a2.1498163585@piotrsikora.sfo.corp.google.com> References: <5f5d70428655db0889a2.1498163585@piotrsikora.sfo.corp.google.com> Message-ID: <00bfd879eaf03f32373a.1498163591@piotrsikora.sfo.corp.google.com> # HG changeset patch # User Piotr Sikora # Date 1489114845 28800 # Thu Mar 09 19:00:45 2017 -0800 # Node ID 00bfd879eaf03f32373ab27110dd8f77c2b722a0 # Parent 64d12a65309eca3859055a04eb02cc14f3b3168d HTTP/2: introduce ngx_http_v2_handle_event(). No functional changes. Signed-off-by: Piotr Sikora diff -r 64d12a65309e -r 00bfd879eaf0 src/http/v2/ngx_http_v2.c --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -57,6 +57,8 @@ static void ngx_http_v2_read_handler(ngx_event_t *rev); static void ngx_http_v2_write_handler(ngx_event_t *wev); static void ngx_http_v2_handle_connection(ngx_http_v2_connection_t *h2c); +static ngx_inline void ngx_http_v2_handle_event(ngx_http_v2_connection_t *h2c, + ngx_event_t *ev); static u_char *ngx_http_v2_state_proxy_protocol(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end); @@ -655,6 +657,13 @@ ngx_http_v2_handle_connection(ngx_http_v } +static ngx_inline void +ngx_http_v2_handle_event(ngx_http_v2_connection_t *h2c, ngx_event_t *ev) +{ + ev->handler(ev); +} + + static u_char * ngx_http_v2_state_proxy_protocol(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end) @@ -1853,7 +1862,6 @@ ngx_http_v2_state_rst_stream(ngx_http_v2 u_char *end) { ngx_uint_t status; - ngx_event_t *ev; ngx_connection_t *fc; ngx_http_v2_node_t *node; ngx_http_v2_stream_t *stream; @@ -1923,8 +1931,7 @@ ngx_http_v2_state_rst_stream(ngx_http_v2 break; } - ev = fc->read; - ev->handler(ev); + ngx_http_v2_handle_event(h2c, fc->read); return ngx_http_v2_state_complete(h2c, pos, end); } @@ -2299,7 +2306,7 @@ ngx_http_v2_state_window_update(ngx_http wev->ready = 1; if (!wev->delayed) { - wev->handler(wev); + ngx_http_v2_handle_event(h2c, wev); } } @@ -2334,7 +2341,7 @@ ngx_http_v2_state_window_update(ngx_http wev->ready = 1; if (!wev->delayed) { - wev->handler(wev); + ngx_http_v2_handle_event(h2c, wev); if (h2c->send_window == 0) { break; @@ -4028,7 +4035,6 @@ static ngx_int_t ngx_http_v2_terminate_stream(ngx_http_v2_connection_t *h2c, ngx_http_v2_stream_t *stream, ngx_uint_t status) { - ngx_event_t *rev; ngx_connection_t *fc; if (stream->rst_sent) { @@ -4047,8 +4053,7 @@ ngx_http_v2_terminate_stream(ngx_http_v2 fc = stream->fake_connection; fc->error = 1; - rev = fc->read; - rev->handler(rev); + ngx_http_v2_handle_event(h2c, fc->read); return NGX_OK; } @@ -4360,7 +4365,7 @@ ngx_http_v2_finalize_connection(ngx_http } ev->eof = 1; - ev->handler(ev); + ngx_http_v2_handle_event(h2c, ev); } } @@ -4425,7 +4430,7 @@ ngx_http_v2_adjust_windows(ngx_http_v2_c wev->ready = 1; if (!wev->delayed) { - wev->handler(wev); + ngx_http_v2_handle_event(h2c, wev); } } } From piotrsikora at google.com Thu Jun 22 20:33:12 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Thu, 22 Jun 2017 13:33:12 -0700 Subject: [PATCH 08 of 14] HTTP/2: add HTTP/2 to upstreams In-Reply-To: <5f5d70428655db0889a2.1498163585@piotrsikora.sfo.corp.google.com> References: <5f5d70428655db0889a2.1498163585@piotrsikora.sfo.corp.google.com> Message-ID: <154ca6c5e62a1931a616.1498163592@piotrsikora.sfo.corp.google.com> # HG changeset patch # User Piotr Sikora # Date 1490767180 25200 # Tue Mar 28 22:59:40 2017 -0700 # Node ID 154ca6c5e62a1931a616e9f2b99ef2553b7c2c8b # Parent 00bfd879eaf03f32373ab27110dd8f77c2b722a0 HTTP/2: add HTTP/2 to upstreams. Signed-off-by: Piotr Sikora diff -r 00bfd879eaf0 -r 154ca6c5e62a auto/modules --- a/auto/modules +++ b/auto/modules @@ -429,7 +429,8 @@ if [ $HTTP = YES ]; then src/http/v2/ngx_http_v2_table.c \ src/http/v2/ngx_http_v2_huff_decode.c \ src/http/v2/ngx_http_v2_huff_encode.c \ - src/http/v2/ngx_http_v2_module.c" + src/http/v2/ngx_http_v2_module.c \ + src/http/v2/ngx_http_v2_upstream.c" ngx_module_libs= ngx_module_link=$HTTP_V2 diff -r 00bfd879eaf0 -r 154ca6c5e62a src/core/ngx_connection.h --- a/src/core/ngx_connection.h +++ b/src/core/ngx_connection.h @@ -175,6 +175,10 @@ struct ngx_connection_s { unsigned close:1; unsigned shared:1; +#if (NGX_HTTP_V2 || NGX_COMPAT) + unsigned http2:1; +#endif + unsigned sendfile:1; unsigned sndlowat:1; unsigned tcp_nodelay:2; /* ngx_connection_tcp_nodelay_e */ diff -r 00bfd879eaf0 -r 154ca6c5e62a src/http/ngx_http_upstream.c --- a/src/http/ngx_http_upstream.c +++ b/src/http/ngx_http_upstream.c @@ -52,7 +52,8 @@ static ngx_int_t ngx_http_upstream_test_ ngx_http_upstream_t *u); static ngx_int_t ngx_http_upstream_intercept_errors(ngx_http_request_t *r, ngx_http_upstream_t *u); -static ngx_int_t ngx_http_upstream_test_connect(ngx_connection_t *c); +static ngx_int_t ngx_http_upstream_test_connect(ngx_http_upstream_t *u, + ngx_connection_t *c); static ngx_int_t ngx_http_upstream_process_headers(ngx_http_request_t *r, ngx_http_upstream_t *u); static void ngx_http_upstream_process_body_in_memory(ngx_http_request_t *r, @@ -187,6 +188,11 @@ static ngx_int_t ngx_http_upstream_ssl_n ngx_http_upstream_t *u, ngx_connection_t *c); #endif +#if (NGX_HTTP_V2) +static ngx_int_t ngx_http_upstream_v2_init_connection(ngx_http_request_t *, + ngx_http_upstream_t *u, ngx_connection_t *c); +#endif + static ngx_http_upstream_header_t ngx_http_upstream_headers_in[] = { @@ -1510,6 +1516,18 @@ ngx_http_upstream_connect(ngx_http_reque c = u->peer.connection; +#if (NGX_HTTP_V2) + + if (u->http2 && c->http2) { + if (ngx_http_upstream_v2_init_connection(r, u, c) != NGX_OK) { + return; + } + + c = u->peer.connection; + } + +#endif + c->data = r; c->write->handler = ngx_http_upstream_handler; @@ -1533,10 +1551,15 @@ ngx_http_upstream_connect(ngx_http_reque } } - c->log = r->connection->log; - c->pool->log = c->log; - c->read->log = c->log; - c->write->log = c->log; +#if (NGX_HTTP_V2) + if (u->stream == NULL) +#endif + { + c->log = r->connection->log; + c->pool->log = c->log; + c->read->log = c->log; + c->write->log = c->log; + } /* init or reinit the ngx_output_chain() and ngx_chain_writer() contexts */ @@ -1596,6 +1619,16 @@ ngx_http_upstream_connect(ngx_http_reque #endif +#if (NGX_HTTP_V2) + + if (u->http2 && u->stream == NULL) { + if (ngx_http_upstream_v2_init_connection(r, u, c) != NGX_OK) { + return; + } + } + +#endif + ngx_http_upstream_send_request(r, u, 1); } @@ -1609,7 +1642,7 @@ ngx_http_upstream_ssl_init_connection(ng ngx_int_t rc; ngx_http_core_loc_conf_t *clcf; - if (ngx_http_upstream_test_connect(c) != NGX_OK) { + if (ngx_http_upstream_test_connect(u, c) != NGX_OK) { ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR); return; } @@ -1709,6 +1742,16 @@ ngx_http_upstream_ssl_handshake(ngx_conn c->write->handler = ngx_http_upstream_handler; c->read->handler = ngx_http_upstream_handler; +#if (NGX_HTTP_V2) + + if (u->http2 && u->stream == NULL) { + if (ngx_http_upstream_v2_init_connection(r, u, c) != NGX_OK) { + return; + } + } + +#endif + c = r->connection; ngx_http_upstream_send_request(r, u, 1); @@ -1830,6 +1873,51 @@ done: #endif +#if (NGX_HTTP_V2) + +static ngx_int_t +ngx_http_upstream_v2_init_connection(ngx_http_request_t *r, + ngx_http_upstream_t *u, ngx_connection_t *c) +{ + ngx_connection_t *fc; + ngx_http_v2_connection_t *h2c; + + if (ngx_http_upstream_test_connect(u, c) != NGX_OK) { + ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR); + return NGX_ERROR; + } + + c->sendfile = 0; + u->output.sendfile = 0; + + h2c = ngx_http_v2_init_connection(c, r->http_connection, 0); + if (h2c == NULL) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_ERROR; + } + + u->stream = ngx_http_v2_create_stream(h2c, r); + if (u->stream == NULL) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_ERROR; + } + + fc = u->stream->fake_connection; + + fc->write->handler = ngx_http_upstream_handler; + fc->read->handler = ngx_http_upstream_handler; + + u->writer.connection = fc; + u->peer.connection = fc; + + return NGX_OK; +} + +#endif + + static ngx_int_t ngx_http_upstream_reinit(ngx_http_request_t *r, ngx_http_upstream_t *u) { @@ -1924,7 +2012,7 @@ ngx_http_upstream_send_request(ngx_http_ u->state->connect_time = ngx_current_msec - u->state->response_time; } - if (!u->request_sent && ngx_http_upstream_test_connect(c) != NGX_OK) { + if (!u->request_sent && ngx_http_upstream_test_connect(u, c) != NGX_OK) { ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR); return; } @@ -2126,6 +2214,16 @@ ngx_http_upstream_send_request_handler(n #endif +#if (NGX_HTTP_V2) + + if (u->http2 && u->stream == NULL) { + if (ngx_http_upstream_v2_init_connection(r, u, c) != NGX_OK) { + return; + } + } + +#endif + if (u->header_sent) { u->write_event_handler = ngx_http_upstream_dummy_handler; @@ -2179,11 +2277,34 @@ ngx_http_upstream_process_header(ngx_htt return; } - if (!u->request_sent && ngx_http_upstream_test_connect(c) != NGX_OK) { + if (!u->request_sent && ngx_http_upstream_test_connect(u, c) != NGX_OK) { ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR); return; } +#if (NGX_HTTP_V2) + + if (u->stream) { + rc = (u->headers_in.status_n >= NGX_HTTP_OK) + ? NGX_OK : NGX_HTTP_UPSTREAM_INVALID_HEADER; + + goto done; + + } else if (u->http2 && !u->request_sent) { + + /* early SETTINGS on the real HTTP/2 connection */ + + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + return; + } + +#endif + if (u->buffer.start == NULL) { u->buffer.start = ngx_palloc(r->pool, u->conf->buffer_size); if (u->buffer.start == NULL) { @@ -2274,6 +2395,12 @@ ngx_http_upstream_process_header(ngx_htt break; } +#if (NGX_HTTP_V2) + +done: + +#endif + if (rc == NGX_HTTP_UPSTREAM_INVALID_HEADER) { ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_INVALID_HEADER); return; @@ -2304,6 +2431,36 @@ ngx_http_upstream_process_header(ngx_htt return; } +#if (NGX_HTTP_V2) + + if (u->buffer.start == NULL && u->stream) { + u->buffer.start = ngx_palloc(r->pool, u->conf->buffer_size); + if (u->buffer.start == NULL) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + u->buffer.pos = u->buffer.start; + u->buffer.last = u->buffer.start; + u->buffer.end = u->buffer.start + u->conf->buffer_size; + u->buffer.temporary = 1; + + u->buffer.tag = u->output.tag; + +#if (NGX_HTTP_CACHE) + + if (r->cache) { + u->buffer.pos += r->cache->header_start; + u->buffer.last = u->buffer.pos; + } + +#endif + + } + +#endif + if (!r->subrequest_in_memory) { ngx_http_upstream_send_response(r, u); return; @@ -2522,11 +2679,19 @@ ngx_http_upstream_intercept_errors(ngx_h static ngx_int_t -ngx_http_upstream_test_connect(ngx_connection_t *c) +ngx_http_upstream_test_connect(ngx_http_upstream_t *u, ngx_connection_t *c) { int err; socklen_t len; +#if (NGX_HTTP_V2) + + if (u->stream) { + return NGX_OK; + } + +#endif + #if (NGX_HAVE_KQUEUE) if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) { @@ -4022,6 +4187,14 @@ ngx_http_upstream_next(ngx_http_request_ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http next upstream, %xi", ft_type); +#if (NGX_HTTP_V2) + + if (u->stream) { + ngx_http_v2_upstream_free_stream(r, u); + } + +#endif + if (u->peer.sockaddr) { if (ft_type == NGX_HTTP_UPSTREAM_FT_HTTP_403 @@ -4195,6 +4368,14 @@ ngx_http_upstream_finalize_request(ngx_h u->finalize_request(r, rc); +#if (NGX_HTTP_V2) + + if (u->stream) { + ngx_http_v2_upstream_free_stream(r, u); + } + +#endif + if (u->peer.free && u->peer.sockaddr) { u->peer.free(&u->peer, u->peer.data, 0); u->peer.sockaddr = NULL; diff -r 00bfd879eaf0 -r 154ca6c5e62a src/http/ngx_http_upstream.h --- a/src/http/ngx_http_upstream.h +++ b/src/http/ngx_http_upstream.h @@ -323,6 +323,10 @@ struct ngx_http_upstream_s { ngx_output_chain_ctx_t output; ngx_chain_writer_ctx_t writer; +#if (NGX_HTTP_V2) + ngx_http_v2_stream_t *stream; +#endif + ngx_http_upstream_conf_t *conf; ngx_http_upstream_srv_conf_t *upstream; #if (NGX_HTTP_CACHE) @@ -389,6 +393,10 @@ struct ngx_http_upstream_s { unsigned request_sent:1; unsigned request_body_sent:1; unsigned header_sent:1; + +#if (NGX_HTTP_V2) + unsigned http2:1; +#endif }; diff -r 00bfd879eaf0 -r 154ca6c5e62a src/http/v2/ngx_http_v2.c --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -11,22 +11,6 @@ #include -/* errors */ -#define NGX_HTTP_V2_NO_ERROR 0x0 -#define NGX_HTTP_V2_PROTOCOL_ERROR 0x1 -#define NGX_HTTP_V2_INTERNAL_ERROR 0x2 -#define NGX_HTTP_V2_FLOW_CTRL_ERROR 0x3 -#define NGX_HTTP_V2_SETTINGS_TIMEOUT 0x4 -#define NGX_HTTP_V2_STREAM_CLOSED 0x5 -#define NGX_HTTP_V2_SIZE_ERROR 0x6 -#define NGX_HTTP_V2_REFUSED_STREAM 0x7 -#define NGX_HTTP_V2_CANCEL 0x8 -#define NGX_HTTP_V2_COMP_ERROR 0x9 -#define NGX_HTTP_V2_CONNECT_ERROR 0xa -#define NGX_HTTP_V2_ENHANCE_YOUR_CALM 0xb -#define NGX_HTTP_V2_INADEQUATE_SECURITY 0xc -#define NGX_HTTP_V2_HTTP_1_1_REQUIRED 0xd - /* frame sizes */ #define NGX_HTTP_V2_SETTINGS_ACK_SIZE 0 #define NGX_HTTP_V2_RST_STREAM_SIZE 4 @@ -124,24 +108,17 @@ static u_char *ngx_http_v2_connection_er static ngx_int_t ngx_http_v2_parse_int(ngx_http_v2_connection_t *h2c, u_char **pos, u_char *end, ngx_uint_t prefix); -static ngx_http_v2_stream_t *ngx_http_v2_create_stream( - ngx_http_v2_connection_t *h2c); -static ngx_http_v2_node_t *ngx_http_v2_get_node_by_id( - ngx_http_v2_connection_t *h2c, ngx_uint_t sid, ngx_uint_t alloc); static ngx_http_v2_node_t *ngx_http_v2_get_closed_node( ngx_http_v2_connection_t *h2c); #define ngx_http_v2_index_size(h2scf) (h2scf->streams_index_mask + 1) #define ngx_http_v2_index(h2scf, sid) ((sid >> 1) & h2scf->streams_index_mask) +static ngx_int_t ngx_http_v2_send_preface(ngx_http_v2_connection_t *h2c); static ngx_int_t ngx_http_v2_send_settings(ngx_http_v2_connection_t *h2c); -static ngx_int_t ngx_http_v2_settings_frame_handler( +static ngx_int_t ngx_http_v2_special_frame_handler( ngx_http_v2_connection_t *h2c, ngx_http_v2_out_frame_t *frame); -static ngx_int_t ngx_http_v2_send_window_update(ngx_http_v2_connection_t *h2c, - ngx_uint_t sid, size_t window); static ngx_int_t ngx_http_v2_send_rst_stream(ngx_http_v2_connection_t *h2c, ngx_uint_t sid, ngx_uint_t status); -static ngx_int_t ngx_http_v2_send_goaway(ngx_http_v2_connection_t *h2c, - ngx_uint_t status); static ngx_http_v2_out_frame_t *ngx_http_v2_get_frame( ngx_http_v2_connection_t *h2c, size_t length, ngx_uint_t type, @@ -151,8 +128,8 @@ static ngx_int_t ngx_http_v2_frame_handl static ngx_int_t ngx_http_v2_validate_header(ngx_http_request_t *r, ngx_http_v2_header_t *header); -static ngx_int_t ngx_http_v2_pseudo_header(ngx_http_request_t *r, - ngx_http_v2_header_t *header); +static ngx_int_t ngx_http_v2_pseudo_header(ngx_http_v2_connection_t *h2c, + ngx_http_request_t *r, ngx_http_v2_header_t *header); static ngx_int_t ngx_http_v2_parse_path(ngx_http_request_t *r, ngx_http_v2_header_t *header); static ngx_int_t ngx_http_v2_parse_method(ngx_http_request_t *r, @@ -161,6 +138,8 @@ static ngx_int_t ngx_http_v2_parse_schem ngx_http_v2_header_t *header); static ngx_int_t ngx_http_v2_parse_authority(ngx_http_request_t *r, ngx_http_v2_header_t *header); +static ngx_int_t ngx_http_v2_parse_status(ngx_http_request_t *r, + ngx_http_v2_header_t *header); static ngx_int_t ngx_http_v2_construct_request_line(ngx_http_request_t *r); static ngx_int_t ngx_http_v2_cookie(ngx_http_request_t *r, ngx_http_v2_header_t *header); @@ -176,8 +155,6 @@ static ngx_int_t ngx_http_v2_terminate_s static void ngx_http_v2_close_stream_handler(ngx_event_t *ev); static void ngx_http_v2_handle_connection_handler(ngx_event_t *rev); static void ngx_http_v2_idle_handler(ngx_event_t *rev); -static void ngx_http_v2_finalize_connection(ngx_http_v2_connection_t *h2c, - ngx_uint_t status); static ngx_int_t ngx_http_v2_adjust_windows(ngx_http_v2_connection_t *h2c, ssize_t delta); @@ -185,6 +162,7 @@ static void ngx_http_v2_set_dependency(n ngx_http_v2_node_t *node, ngx_uint_t depend, ngx_uint_t exclusive); static void ngx_http_v2_node_children_update(ngx_http_v2_node_t *node); +static void ngx_http_v2_headers_pool_cleanup(void *data); static void ngx_http_v2_pool_cleanup(void *data); @@ -208,20 +186,36 @@ static ngx_http_v2_handler_pt ngx_http_v void ngx_http_v2_init(ngx_event_t *rev) { - ngx_connection_t *c; + ngx_connection_t *c = rev->data; + ngx_http_connection_t *hc = c->data; + + if (ngx_http_v2_init_connection(c, hc, 1) == NULL) { + return; + } + + ngx_http_v2_read_handler(rev); +} + + +ngx_http_v2_connection_t * +ngx_http_v2_init_connection(ngx_connection_t *c, ngx_http_connection_t *hc, + ngx_uint_t server) +{ + ngx_log_t *log; ngx_pool_cleanup_t *cln; - ngx_http_connection_t *hc; ngx_http_v2_srv_conf_t *h2scf; ngx_http_v2_main_conf_t *h2mcf; ngx_http_v2_connection_t *h2c; - c = rev->data; - hc = c->data; - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "init http2 connection"); c->log->action = "processing HTTP/2 connection"; + if (c->http2) { + h2c = c->data; + goto reuse; + } + h2mcf = ngx_http_get_module_main_conf(hc->conf_ctx, ngx_http_v2_module); if (h2mcf->recv_buffer == NULL) { @@ -229,24 +223,46 @@ ngx_http_v2_init(ngx_event_t *rev) h2mcf->recv_buffer_size); if (h2mcf->recv_buffer == NULL) { ngx_http_close_connection(c); - return; + return NULL; } } + log = ngx_palloc(c->pool, sizeof(ngx_log_t)); + if (log == NULL) { + ngx_http_close_connection(c); + return NULL; + } + + ngx_memcpy(log, c->log, sizeof(ngx_log_t)); + + log->connection = c->number; + + c->log = log; + c->pool->log = log; + c->read->log = log; + c->write->log = log; + h2c = ngx_pcalloc(c->pool, sizeof(ngx_http_v2_connection_t)); if (h2c == NULL) { ngx_http_close_connection(c); - return; + return NULL; } h2c->connection = c; - h2c->http_connection = hc; h2c->conf_ctx = hc->conf_ctx; + h2c->server = server; + + if (h2c->server) { + h2c->http_connection = hc; + } + h2c->send_window = NGX_HTTP_V2_DEFAULT_WINDOW; h2c->recv_window = NGX_HTTP_V2_MAX_WINDOW; - h2c->init_window = NGX_HTTP_V2_DEFAULT_WINDOW; + h2c->init_window = (h2c->server) + ? NGX_HTTP_V2_DEFAULT_WINDOW + : NGX_HTTP_V2_PREREAD_WINDOW; h2c->frame_size = NGX_HTTP_V2_DEFAULT_FRAME_SIZE; @@ -255,13 +271,13 @@ ngx_http_v2_init(ngx_event_t *rev) h2c->pool = ngx_create_pool(h2scf->pool_size, h2c->connection->log); if (h2c->pool == NULL) { ngx_http_close_connection(c); - return; + return NULL; } cln = ngx_pool_cleanup_add(c->pool, 0); if (cln == NULL) { ngx_http_close_connection(c); - return; + return NULL; } cln->handler = ngx_http_v2_pool_cleanup; @@ -271,37 +287,52 @@ ngx_http_v2_init(ngx_event_t *rev) * sizeof(ngx_http_v2_node_t *)); if (h2c->streams_index == NULL) { ngx_http_close_connection(c); - return; + return NULL; + } + + if (!h2c->server) { + if (ngx_http_v2_send_preface(h2c) == NGX_ERROR) { + ngx_http_close_connection(c); + return NULL; + } } if (ngx_http_v2_send_settings(h2c) == NGX_ERROR) { ngx_http_close_connection(c); - return; + return NULL; } if (ngx_http_v2_send_window_update(h2c, 0, NGX_HTTP_V2_MAX_WINDOW - - NGX_HTTP_V2_DEFAULT_WINDOW) + - h2c->init_window) == NGX_ERROR) { ngx_http_close_connection(c); - return; - } - - h2c->state.handler = hc->proxy_protocol ? ngx_http_v2_state_proxy_protocol - : ngx_http_v2_state_preface; + return NULL; + } + + if (h2c->server) { + h2c->state.handler = hc->proxy_protocol + ? ngx_http_v2_state_proxy_protocol + : ngx_http_v2_state_preface; + + } else { + h2c->state.handler = ngx_http_v2_state_head; + } ngx_queue_init(&h2c->waiting); ngx_queue_init(&h2c->dependencies); ngx_queue_init(&h2c->closed); c->data = h2c; - - rev->handler = ngx_http_v2_read_handler; + c->http2 = 1; + c->idle = 1; + +reuse: + + c->read->handler = ngx_http_v2_read_handler; c->write->handler = ngx_http_v2_write_handler; - c->idle = 1; - - ngx_http_v2_read_handler(rev); + return h2c; } @@ -660,7 +691,12 @@ ngx_http_v2_handle_connection(ngx_http_v static ngx_inline void ngx_http_v2_handle_event(ngx_http_v2_connection_t *h2c, ngx_event_t *ev) { - ev->handler(ev); + if (h2c->server) { + ev->handler(ev); + + } else { + ngx_post_event(ev, &ngx_posted_events); + } } @@ -909,6 +945,7 @@ ngx_http_v2_state_read_data(ngx_http_v2_ ngx_buf_t *buf; ngx_int_t rc; ngx_http_request_t *r; + ngx_http_upstream_t *u; ngx_http_v2_stream_t *stream; ngx_http_v2_srv_conf_t *h2scf; @@ -932,39 +969,48 @@ ngx_http_v2_state_read_data(ngx_http_v2_ stream->in_closed = h2c->state.flags & NGX_HTTP_V2_END_STREAM_FLAG; } - r = stream->request; - - if (r->request_body) { - rc = ngx_http_v2_process_request_body(r, pos, size, stream->in_closed); - - if (rc != NGX_OK) { - stream->skip_data = 1; - ngx_http_finalize_request(r, rc); - } - - } else if (size) { - buf = stream->preread; - - if (buf == NULL) { - h2scf = ngx_http_get_module_srv_conf(r, ngx_http_v2_module); - - buf = ngx_create_temp_buf(r->pool, h2scf->preread_size); + if (h2c->server) { + r = stream->request; + + if (r->request_body) { + rc = ngx_http_v2_process_request_body(r, pos, size, + stream->in_closed); + if (rc != NGX_OK) { + stream->skip_data = 1; + ngx_http_finalize_request(r, rc); + } + + } else if (size) { + buf = stream->preread; + if (buf == NULL) { + h2scf = ngx_http_get_module_srv_conf(r, ngx_http_v2_module); + + buf = ngx_create_temp_buf(r->pool, h2scf->preread_size); + if (buf == NULL) { + return ngx_http_v2_connection_error(h2c, + NGX_HTTP_V2_INTERNAL_ERROR); + } + + stream->preread = buf; + } + + if (size > (size_t) (buf->end - buf->last)) { + ngx_log_error(NGX_LOG_ALERT, h2c->connection->log, 0, + "http2 preread buffer overflow"); return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR); } - stream->preread = buf; + buf->last = ngx_cpymem(buf->last, pos, size); } - if (size > (size_t) (buf->end - buf->last)) { - ngx_log_error(NGX_LOG_ALERT, h2c->connection->log, 0, - "http2 preread buffer overflow"); + } else if (size || stream->in_closed) { + + if (ngx_http_v2_stream_buffer_save(stream, pos, size) == NGX_ERROR) { return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR); } - - buf->last = ngx_cpymem(buf->last, pos, size); } pos += size; @@ -975,6 +1021,34 @@ ngx_http_v2_state_read_data(ngx_http_v2_ ngx_http_v2_state_read_data); } + if (!h2c->server) { + u = stream->request->upstream; + + if (u->state) { + u->state->bytes_received += NGX_HTTP_V2_FRAME_HEADER_SIZE; + + if (h2c->state.flags & NGX_HTTP_V2_PADDED_FLAG) { + u->state->bytes_received += 1 + h2c->state.padding; + } + } + + if (h2c->state.flags & NGX_HTTP_V2_PADDED_FLAG) { + if (ngx_http_v2_send_window_update(h2c, stream->node->id, + 1 + h2c->state.padding) + == NGX_ERROR) + { + return ngx_http_v2_connection_error(h2c, + NGX_HTTP_V2_INTERNAL_ERROR); + } + + stream->recv_window += 1 + h2c->state.padding; + } + + if (size || stream->in_closed) { + ngx_post_event(stream->fake_connection->read, &ngx_posted_events); + } + } + if (h2c->state.padding) { return ngx_http_v2_state_skip_padded(h2c, pos, end); } @@ -991,6 +1065,8 @@ ngx_http_v2_state_headers(ngx_http_v2_co ngx_uint_t padded, priority, depend, dependency, excl, weight; ngx_uint_t status; ngx_http_v2_node_t *node; + ngx_http_request_t *r; + ngx_http_upstream_t *u; ngx_http_v2_stream_t *stream; ngx_http_v2_srv_conf_t *h2scf; @@ -1022,7 +1098,7 @@ ngx_http_v2_state_headers(ngx_http_v2_co return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); } - if (h2c->goaway) { + if (h2c->server && h2c->goaway) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, "skipping http2 HEADERS frame"); return ngx_http_v2_state_skip(h2c, pos, end); @@ -1069,7 +1145,10 @@ ngx_http_v2_state_headers(ngx_http_v2_co "http2 HEADERS frame sid:%ui on %ui excl:%ui weight:%ui", h2c->state.sid, depend, excl, weight); - if (h2c->state.sid % 2 == 0 || h2c->state.sid <= h2c->last_sid) { + if (h2c->state.sid % 2 == 0 + || (h2c->server && h2c->state.sid <= h2c->last_sid) + || (!h2c->server && h2c->state.sid != h2c->last_sid)) + { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, "peer sent HEADERS frame with incorrect identifier %ui, " "the last was %ui", h2c->state.sid, h2c->last_sid); @@ -1077,7 +1156,9 @@ ngx_http_v2_state_headers(ngx_http_v2_co return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR); } - h2c->last_sid = h2c->state.sid; + if (h2c->server) { + h2c->last_sid = h2c->state.sid; + } h2c->state.pool = ngx_create_pool(1024, h2c->connection->log); if (h2c->state.pool == NULL) { @@ -1097,7 +1178,7 @@ ngx_http_v2_state_headers(ngx_http_v2_co h2c->state.header_limit = h2scf->max_header_size; - if (h2c->processing >= h2scf->concurrent_streams) { + if (h2c->server && h2c->processing >= h2scf->concurrent_streams) { ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, "concurrent streams exceeded %ui", h2c->processing); @@ -1117,20 +1198,83 @@ ngx_http_v2_state_headers(ngx_http_v2_co goto rst_stream; } - node = ngx_http_v2_get_node_by_id(h2c, h2c->state.sid, 1); - - if (node == NULL) { - return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR); - } - - if (node->parent) { - ngx_queue_remove(&node->reuse); - h2c->closed_nodes--; - } - - stream = ngx_http_v2_create_stream(h2c); - if (stream == NULL) { - return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR); + if (h2c->server) { + node = ngx_http_v2_get_node_by_id(h2c, h2c->state.sid, 1); + + if (node == NULL) { + return ngx_http_v2_connection_error(h2c, + NGX_HTTP_V2_INTERNAL_ERROR); + } + + if (node->parent) { + ngx_queue_remove(&node->reuse); + h2c->closed_nodes--; + } + + stream = ngx_http_v2_create_stream(h2c, NULL); + if (stream == NULL) { + return ngx_http_v2_connection_error(h2c, + NGX_HTTP_V2_INTERNAL_ERROR); + } + + stream->node = node; + node->stream = stream; + + if (priority || node->parent == NULL) { + node->weight = weight; + ngx_http_v2_set_dependency(h2c, node, depend, excl); + } + + stream->request->stream = stream; + + stream->request->request_length = h2c->state.length; + + } else { + node = ngx_http_v2_get_node_by_id(h2c, h2c->state.sid, 0); + + if (node == NULL || node->stream == NULL) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "unknown http2 stream"); + + status = NGX_HTTP_V2_CANCEL; + goto rst_stream; + } + + stream = node->stream; + r = stream->request; + u = r->upstream; + + if (u->headers_in.headers.nalloc == 0) { + + if (ngx_list_init(&u->headers_in.headers, r->pool, 8, + sizeof(ngx_table_elt_t)) + != NGX_OK) + { + return ngx_http_v2_connection_error(h2c, + NGX_HTTP_V2_INTERNAL_ERROR); + } + + } else if (u->headers_in.trailers.nalloc == 0) { + + if (ngx_list_init(&u->headers_in.trailers, r->pool, 2, + sizeof(ngx_table_elt_t)) + != NGX_OK) + { + return ngx_http_v2_connection_error(h2c, + NGX_HTTP_V2_INTERNAL_ERROR); + } + } + + h2c->state.headers = (u->headers_in.status_n == 0) ? 1 : 0; + + if (u->state) { + u->state->bytes_received += NGX_HTTP_V2_FRAME_HEADER_SIZE + + h2c->state.length; + + if (h2c->state.flags & NGX_HTTP_V2_PADDED_FLAG) { + u->state->bytes_received += 1 + h2c->state.padding; + } + } } h2c->state.stream = stream; @@ -1138,19 +1282,9 @@ ngx_http_v2_state_headers(ngx_http_v2_co stream->pool = h2c->state.pool; h2c->state.keep_pool = 1; - stream->request->request_length = h2c->state.length; - stream->in_closed = h2c->state.flags & NGX_HTTP_V2_END_STREAM_FLAG; - stream->node = node; - - node->stream = stream; - - if (priority || node->parent == NULL) { - node->weight = weight; - ngx_http_v2_set_dependency(h2c, node, depend, excl); - } - - if (h2c->connection->requests >= h2scf->max_requests) { + + if (h2c->server && h2c->connection->requests >= h2scf->max_requests) { h2c->goaway = 1; if (ngx_http_v2_send_goaway(h2c, NGX_HTTP_V2_NO_ERROR) == NGX_ERROR) { @@ -1503,14 +1637,17 @@ static u_char * ngx_http_v2_state_process_header(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end) { - size_t len; - ngx_int_t rc; - ngx_table_elt_t *h; - ngx_http_header_t *hh; - ngx_http_request_t *r; - ngx_http_v2_header_t *header; - ngx_http_core_srv_conf_t *cscf; - ngx_http_core_main_conf_t *cmcf; + size_t len; + ngx_int_t rc; + ngx_list_t *headers; + ngx_table_elt_t *h; + ngx_http_header_t *hh; + ngx_http_request_t *r; + ngx_http_v2_header_t *header; + ngx_http_core_srv_conf_t *cscf; + ngx_http_core_main_conf_t *cmcf; + ngx_http_upstream_header_t *uh; + ngx_http_upstream_main_conf_t *umcf; static ngx_str_t cookie = ngx_string("cookie"); @@ -1572,7 +1709,7 @@ ngx_http_v2_state_process_header(ngx_htt } if (header->name.data[0] == ':') { - rc = ngx_http_v2_pseudo_header(r, header); + rc = ngx_http_v2_pseudo_header(h2c, r, header); if (rc == NGX_OK) { ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, @@ -1621,7 +1758,18 @@ ngx_http_v2_state_process_header(ngx_htt } } else { - h = ngx_list_push(&r->headers_in.headers); + + if (h2c->server) { + headers = &r->headers_in.headers; + + } else if (h2c->state.headers) { + headers = &r->upstream->headers_in.headers; + + } else { + headers = &r->upstream->headers_in.trailers; + } + + h = ngx_list_push(headers); if (h == NULL) { return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR); @@ -1641,18 +1789,31 @@ ngx_http_v2_state_process_header(ngx_htt h->lowcase_key = h->key.data; - cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); - - hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash, - h->lowcase_key, h->key.len); - - if (hh && hh->handler(r, h, hh->offset) != NGX_OK) { - goto error; + if (h2c->server) { + cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); + + hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash, + h->lowcase_key, h->key.len); + + if (hh && hh->handler(r, h, hh->offset) != NGX_OK) { + goto error; + } + + } else if (h2c->state.headers) { + umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module); + + uh = ngx_hash_find(&umcf->headers_in_hash, h->hash, + h->lowcase_key, h->key.len); + + if (uh && uh->handler(r, h, uh->offset) != NGX_OK) { + goto error; + } } } - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http2 http header: \"%V: %V\"", + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http2 http %s: \"%V: %V\"", + (h2c->server || h2c->state.headers) ? "header" : "trailer", &header->name, &header->value); return ngx_http_v2_state_header_complete(h2c, pos, end); @@ -1669,6 +1830,9 @@ static u_char * ngx_http_v2_state_header_complete(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end) { + ngx_connection_t *fc; + ngx_http_request_t *r; + ngx_pool_cleanup_t *cln; ngx_http_v2_stream_t *stream; if (h2c->state.length) { @@ -1684,7 +1848,51 @@ ngx_http_v2_state_header_complete(ngx_ht stream = h2c->state.stream; if (stream) { - ngx_http_v2_run_request(stream->request); + r = stream->request; + + if (h2c->server) { + ngx_http_v2_run_request(r); + + } else { + fc = stream->fake_connection; + + if (h2c->state.headers) { + if (ngx_http_v2_stream_buffer_init(stream) == NGX_ERROR) { + return ngx_http_v2_connection_error(h2c, + NGX_HTTP_V2_INTERNAL_ERROR); + } + + if (!stream->in_closed && r->upstream->conf->pass_trailers) { + r->expect_trailers = 1; + } + + } else if (stream->in_closed) { + if (ngx_http_v2_stream_buffer_save(stream, NULL, 0) + == NGX_ERROR) + { + return ngx_http_v2_connection_error(h2c, + NGX_HTTP_V2_INTERNAL_ERROR); + } + } + + /* + * Assign ownership of the pool used for parsing of headers + * to the request, so that it's going to be freed along it. + */ + + cln = ngx_pool_cleanup_add(r->pool, 0); + if (cln == NULL) { + return ngx_http_v2_connection_error(h2c, + NGX_HTTP_V2_INTERNAL_ERROR); + } + + cln->handler = ngx_http_v2_headers_pool_cleanup; + cln->data = stream->pool; + + stream->pool->log = r->pool->log; + + ngx_post_event(fc->read, &ngx_posted_events); + } } if (!h2c->state.keep_pool) { @@ -1706,9 +1914,11 @@ static u_char * ngx_http_v2_handle_continuation(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end, ngx_http_v2_handler_pt handler) { - u_char *p; - size_t len, skip; - uint32_t head; + u_char *p; + size_t len, skip; + uint32_t head; + ngx_http_request_t *r; + ngx_http_upstream_t *u; len = h2c->state.length; @@ -1756,7 +1966,18 @@ ngx_http_v2_handle_continuation(ngx_http h2c->state.length += len; if (h2c->state.stream) { - h2c->state.stream->request->request_length += len; + r = h2c->state.stream->request; + + if (h2c->server) { + r->request_length += len; + + } else { + u = r->upstream; + + if (u->state) { + u->state->bytes_received += NGX_HTTP_V2_FRAME_HEADER_SIZE + len; + } + } } h2c->state.handler = handler; @@ -1832,6 +2053,10 @@ ngx_http_v2_state_priority(ngx_http_v2_c return ngx_http_v2_state_complete(h2c, pos, end); } + if (!h2c->server) { + return ngx_http_v2_state_complete(h2c, pos, end); + } + node = ngx_http_v2_get_node_by_id(h2c, h2c->state.sid, 1); if (node == NULL) { @@ -1909,25 +2134,43 @@ ngx_http_v2_state_rst_stream(ngx_http_v2 stream->out_closed = 1; fc = stream->fake_connection; - fc->error = 1; switch (status) { + case NGX_HTTP_V2_NO_ERROR: + ngx_log_error(NGX_LOG_INFO, fc->log, 0, + "peer closed stream %ui", h2c->state.sid); + + if (h2c->server) { + fc->error = 1; + + } else { + if (ngx_http_v2_stream_buffer_save(stream, NULL, 0) == NGX_ERROR) { + return ngx_http_v2_connection_error(h2c, + NGX_HTTP_V2_INTERNAL_ERROR); + } + } + + break; + case NGX_HTTP_V2_CANCEL: ngx_log_error(NGX_LOG_INFO, fc->log, 0, "peer canceled stream %ui", h2c->state.sid); + fc->error = 1; break; case NGX_HTTP_V2_INTERNAL_ERROR: ngx_log_error(NGX_LOG_INFO, fc->log, 0, "peer terminated stream %ui due to internal error", h2c->state.sid); + fc->error = 1; break; default: ngx_log_error(NGX_LOG_INFO, fc->log, 0, "peer terminated stream %ui with status %ui", h2c->state.sid, status); + fc->error = 1; break; } @@ -2052,11 +2295,14 @@ ngx_http_v2_state_settings_params(ngx_ht NGX_HTTP_V2_PROTOCOL_ERROR); } - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, - "http2 SETTINGS param MAX_FRAME_SIZE:%ui", - value); - - h2c->frame_size = value; + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 SETTINGS param MAX_FRAME_SIZE:%ui%s", + value, h2c->server ? "" : " (ignored)"); + + if (h2c->server) { + h2c->frame_size = value; + } + break; case NGX_HTTP_V2_HEADER_LIST_SIZE_SETTING: @@ -2455,7 +2701,7 @@ ngx_http_v2_state_headers_save(ngx_http_ ngx_http_v2_stream_t *stream; ngx_http_core_srv_conf_t *cscf; - if (h2c->state.stream) { + if (h2c->server && h2c->state.stream) { stream = h2c->state.stream; fc = stream->fake_connection; @@ -2544,6 +2790,60 @@ ngx_http_v2_parse_int(ngx_http_v2_connec static ngx_int_t +ngx_http_v2_send_preface(ngx_http_v2_connection_t *h2c) +{ + ngx_buf_t *buf; + ngx_chain_t *cl; + ngx_http_v2_out_frame_t *frame; + + static const u_char preface[] = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 send client connection preface"); + + frame = ngx_palloc(h2c->pool, sizeof(ngx_http_v2_out_frame_t)); + if (frame == NULL) { + return NGX_ERROR; + } + + cl = ngx_alloc_chain_link(h2c->pool); + if (cl == NULL) { + return NGX_ERROR; + } + + buf = ngx_calloc_buf(h2c->pool); + if (buf == NULL) { + return NGX_ERROR; + } + + buf->pos = (u_char *) preface; + buf->last = buf->pos + sizeof(preface) - 1; + + buf->start = buf->pos; + buf->end = buf->last; + buf->memory = 1; + + buf->last_buf = 1; + + cl->buf = buf; + cl->next = NULL; + + frame->first = cl; + frame->last = cl; + frame->handler = ngx_http_v2_special_frame_handler; + frame->stream = NULL; +#if (NGX_DEBUG) + frame->length = sizeof(preface) - 1; +#endif + frame->blocked = 0; + + ngx_http_v2_queue_blocked_frame(h2c, frame); + + return NGX_OK; +} + + +static ngx_int_t ngx_http_v2_send_settings(ngx_http_v2_connection_t *h2c) { size_t len; @@ -2552,7 +2852,7 @@ ngx_http_v2_send_settings(ngx_http_v2_co ngx_http_v2_srv_conf_t *h2scf; ngx_http_v2_out_frame_t *frame; - len = NGX_HTTP_V2_SETTINGS_PARAM_SIZE * 3; + len = NGX_HTTP_V2_SETTINGS_PARAM_SIZE * (h2c->server ? 3 : 2); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, "http2 send SETTINGS frame params:%uz", @@ -2580,7 +2880,7 @@ ngx_http_v2_send_settings(ngx_http_v2_co frame->first = cl; frame->last = cl; - frame->handler = ngx_http_v2_settings_frame_handler; + frame->handler = ngx_http_v2_special_frame_handler; frame->stream = NULL; #if (NGX_DEBUG) frame->length = len; @@ -2594,33 +2894,52 @@ ngx_http_v2_send_settings(ngx_http_v2_co buf->last = ngx_http_v2_write_sid(buf->last, 0); - h2scf = ngx_http_get_module_srv_conf(h2c->conf_ctx, ngx_http_v2_module); - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, - "http2 send SETTINGS param MAX_CONCURRENT_STREAMS:%ui", - h2scf->concurrent_streams); - - buf->last = ngx_http_v2_write_uint16(buf->last, - NGX_HTTP_V2_MAX_STREAMS_SETTING); - buf->last = ngx_http_v2_write_uint32(buf->last, - h2scf->concurrent_streams); - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, - "http2 send SETTINGS param INITIAL_WINDOW_SIZE:%uz", - h2scf->preread_size); - - buf->last = ngx_http_v2_write_uint16(buf->last, + if (h2c->server) { + h2scf = ngx_http_get_module_srv_conf(h2c->conf_ctx, ngx_http_v2_module); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 send SETTINGS param MAX_CONCURRENT_STREAMS:%ui", + h2scf->concurrent_streams); + + buf->last = ngx_http_v2_write_uint16(buf->last, + NGX_HTTP_V2_MAX_STREAMS_SETTING); + buf->last = ngx_http_v2_write_uint32(buf->last, + h2scf->concurrent_streams); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 send SETTINGS param INITIAL_WINDOW_SIZE:%uz", + h2scf->preread_size); + + buf->last = ngx_http_v2_write_uint16(buf->last, NGX_HTTP_V2_INIT_WINDOW_SIZE_SETTING); - buf->last = ngx_http_v2_write_uint32(buf->last, h2scf->preread_size); - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, - "http2 send SETTINGS param MAX_FRAME_SIZE:%ud", - NGX_HTTP_V2_MAX_FRAME_SIZE); - - buf->last = ngx_http_v2_write_uint16(buf->last, - NGX_HTTP_V2_MAX_FRAME_SIZE_SETTING); - buf->last = ngx_http_v2_write_uint32(buf->last, - NGX_HTTP_V2_MAX_FRAME_SIZE); + buf->last = ngx_http_v2_write_uint32(buf->last, h2scf->preread_size); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 send SETTINGS param MAX_FRAME_SIZE:%ud", + NGX_HTTP_V2_MAX_FRAME_SIZE); + + buf->last = ngx_http_v2_write_uint16(buf->last, + NGX_HTTP_V2_MAX_FRAME_SIZE_SETTING); + buf->last = ngx_http_v2_write_uint32(buf->last, + NGX_HTTP_V2_MAX_FRAME_SIZE); + + } else { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 send SETTINGS param ENABLE_PUSH:0"); + + buf->last = ngx_http_v2_write_uint16(buf->last, + NGX_HTTP_V2_ENABLE_PUSH_SETTING); + buf->last = ngx_http_v2_write_uint32(buf->last, 0); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 send SETTINGS param INITIAL_WINDOW_SIZE:%uz", + NGX_HTTP_V2_PREREAD_WINDOW); + + buf->last = ngx_http_v2_write_uint16(buf->last, + NGX_HTTP_V2_INIT_WINDOW_SIZE_SETTING); + buf->last = ngx_http_v2_write_uint32(buf->last, + NGX_HTTP_V2_PREREAD_WINDOW); + } ngx_http_v2_queue_blocked_frame(h2c, frame); @@ -2629,7 +2948,7 @@ ngx_http_v2_send_settings(ngx_http_v2_co static ngx_int_t -ngx_http_v2_settings_frame_handler(ngx_http_v2_connection_t *h2c, +ngx_http_v2_special_frame_handler(ngx_http_v2_connection_t *h2c, ngx_http_v2_out_frame_t *frame) { ngx_buf_t *buf; @@ -2646,7 +2965,7 @@ ngx_http_v2_settings_frame_handler(ngx_h } -static ngx_int_t +ngx_int_t ngx_http_v2_send_window_update(ngx_http_v2_connection_t *h2c, ngx_uint_t sid, size_t window) { @@ -2696,21 +3015,29 @@ ngx_http_v2_send_rst_stream(ngx_http_v2_ buf->last = ngx_http_v2_write_uint32(buf->last, status); - ngx_http_v2_queue_blocked_frame(h2c, frame); + if (status == NGX_HTTP_V2_NO_ERROR) { + ngx_http_v2_queue_ordered_frame(h2c, frame); + + } else { + ngx_http_v2_queue_blocked_frame(h2c, frame); + } return NGX_OK; } -static ngx_int_t +ngx_int_t ngx_http_v2_send_goaway(ngx_http_v2_connection_t *h2c, ngx_uint_t status) { ngx_buf_t *buf; + ngx_uint_t last_sid; ngx_http_v2_out_frame_t *frame; + last_sid = h2c->server ? h2c->last_sid : 0; + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, "http2 send GOAWAY frame: last sid %ui, error %ui", - h2c->last_sid, status); + last_sid, status); frame = ngx_http_v2_get_frame(h2c, NGX_HTTP_V2_GOAWAY_SIZE, NGX_HTTP_V2_GOAWAY_FRAME, @@ -2721,7 +3048,7 @@ ngx_http_v2_send_goaway(ngx_http_v2_conn buf = frame->first->buf; - buf->last = ngx_http_v2_write_sid(buf->last, h2c->last_sid); + buf->last = ngx_http_v2_write_sid(buf->last, last_sid); buf->last = ngx_http_v2_write_uint32(buf->last, status); ngx_http_v2_queue_blocked_frame(h2c, frame); @@ -2814,14 +3141,13 @@ ngx_http_v2_frame_handler(ngx_http_v2_co } -static ngx_http_v2_stream_t * -ngx_http_v2_create_stream(ngx_http_v2_connection_t *h2c) +ngx_http_v2_stream_t * +ngx_http_v2_create_stream(ngx_http_v2_connection_t *h2c, ngx_http_request_t *r) { ngx_log_t *log; ngx_event_t *rev, *wev; ngx_connection_t *fc; ngx_http_log_ctx_t *ctx; - ngx_http_request_t *r; ngx_http_v2_stream_t *stream; ngx_http_v2_srv_conf_t *h2scf; ngx_http_core_srv_conf_t *cscf; @@ -2861,16 +3187,11 @@ ngx_http_v2_create_stream(ngx_http_v2_co if (ctx == NULL) { return NULL; } - - ctx->connection = fc; - ctx->request = NULL; - ctx->current_request = NULL; } ngx_memcpy(log, h2c->connection->log, sizeof(ngx_log_t)); log->data = ctx; - log->action = "reading client request headers"; ngx_memzero(rev, sizeof(ngx_event_t)); @@ -2885,71 +3206,112 @@ ngx_http_v2_create_stream(ngx_http_v2_co ngx_memcpy(fc, h2c->connection, sizeof(ngx_connection_t)); - fc->data = h2c->http_connection; fc->read = rev; fc->write = wev; fc->sent = 0; fc->log = log; + fc->buffer = NULL; fc->buffered = 0; fc->sndlowat = 1; fc->tcp_nodelay = NGX_TCP_NODELAY_DISABLED; - - r = ngx_http_create_request(fc); - if (r == NULL) { - return NULL; - } - - ngx_str_set(&r->http_protocol, "HTTP/2.0"); - - r->http_version = NGX_HTTP_VERSION_20; - r->valid_location = 1; - - fc->data = r; - h2c->connection->requests++; - - cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); - - r->header_in = ngx_create_temp_buf(r->pool, - cscf->client_header_buffer_size); - if (r->header_in == NULL) { - ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); - return NULL; - } - - if (ngx_list_init(&r->headers_in.headers, r->pool, 20, - sizeof(ngx_table_elt_t)) - != NGX_OK) - { - ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); - return NULL; - } - - r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE; + fc->http2 = 0; + + fc->recv = ngx_http_v2_recv; + fc->recv_chain = ngx_http_v2_recv_chain; + fc->send = NULL; + fc->send_chain = ngx_http_v2_send_chain; + fc->need_last_buf = 1; + + if (h2c->server) { + log->action = "reading client request headers"; + + ctx->connection = fc; + ctx->request = NULL; + ctx->current_request = NULL; + + fc->data = h2c->http_connection; + + r = ngx_http_create_request(fc); + if (r == NULL) { + return NULL; + } + + fc->data = r; + + ngx_str_set(&r->http_protocol, "HTTP/2.0"); + + r->http_version = NGX_HTTP_VERSION_20; + r->valid_location = 1; + + h2c->connection->requests++; + + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + + r->header_in = ngx_create_temp_buf(r->pool, + cscf->client_header_buffer_size); + if (r->header_in == NULL) { + ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NULL; + } + + if (ngx_list_init(&r->headers_in.headers, r->pool, 20, + sizeof(ngx_table_elt_t)) + != NGX_OK) + { + ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NULL; + } + + r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE; + + } else { + log->action = "connecting to upstream"; + + ctx->connection = r->connection; + ctx->request = r; + ctx->current_request = r; + + fc->data = r; + fc->pool = r->pool; + + fc->read->ready = 0; + fc->read->active = 1; + } stream = ngx_pcalloc(r->pool, sizeof(ngx_http_v2_stream_t)); if (stream == NULL) { - ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); - return NULL; - } - - r->stream = stream; + goto failed; + } stream->request = r; stream->connection = h2c; stream->fake_connection = fc; - h2scf = ngx_http_get_module_srv_conf(r, ngx_http_v2_module); - stream->send_window = h2c->init_window; - stream->recv_window = h2scf->preread_size; + + if (h2c->server) { + h2scf = ngx_http_get_module_srv_conf(r, ngx_http_v2_module); + stream->recv_window = h2scf->preread_size; + + } else { + stream->recv_window = NGX_HTTP_V2_PREREAD_WINDOW; + } h2c->processing++; return stream; + +failed: + + if (h2c->server) { + ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + } + + return NULL; } -static ngx_http_v2_node_t * +ngx_http_v2_node_t * ngx_http_v2_get_node_by_id(ngx_http_v2_connection_t *h2c, ngx_uint_t sid, ngx_uint_t alloc) { @@ -3143,7 +3505,8 @@ ngx_http_v2_validate_header(ngx_http_req static ngx_int_t -ngx_http_v2_pseudo_header(ngx_http_request_t *r, ngx_http_v2_header_t *header) +ngx_http_v2_pseudo_header(ngx_http_v2_connection_t *h2c, ngx_http_request_t *r, + ngx_http_v2_header_t *header) { header->name.len--; header->name.data++; @@ -3153,6 +3516,10 @@ ngx_http_v2_pseudo_header(ngx_http_reque if (ngx_memcmp(header->name.data, "path", sizeof("path") - 1) == 0) { + if (!h2c->server) { + goto invalid; + } + return ngx_http_v2_parse_path(r, header); } @@ -3162,21 +3529,43 @@ ngx_http_v2_pseudo_header(ngx_http_reque if (ngx_memcmp(header->name.data, "method", sizeof("method") - 1) == 0) { + if (!h2c->server) { + goto invalid; + } + return ngx_http_v2_parse_method(r, header); } if (ngx_memcmp(header->name.data, "scheme", sizeof("scheme") - 1) == 0) { + if (!h2c->server) { + goto invalid; + } + return ngx_http_v2_parse_scheme(r, header); } + if (ngx_memcmp(header->name.data, "status", sizeof("status") - 1) + == 0) + { + if (h2c->server) { + goto invalid; + } + + return ngx_http_v2_parse_status(r, header); + } + break; case 9: if (ngx_memcmp(header->name.data, "authority", sizeof("authority") - 1) == 0) { + if (!h2c->server) { + goto invalid; + } + return ngx_http_v2_parse_authority(r, header); } @@ -3188,6 +3577,14 @@ ngx_http_v2_pseudo_header(ngx_http_reque &header->name); return NGX_DECLINED; + +invalid: + + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "peer sent invalid pseudo-header \":%V\"", + &header->name); + + return NGX_DECLINED; } @@ -3396,6 +3793,51 @@ ngx_http_v2_parse_authority(ngx_http_req static ngx_int_t +ngx_http_v2_parse_status(ngx_http_request_t *r, ngx_http_v2_header_t *header) +{ + ngx_int_t status; + ngx_http_upstream_t *u; + + u = r->upstream; + + if (u->headers_in.status_n) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "upstream sent duplicate :status header"); + + return NGX_DECLINED; + } + + if (header->value.len != 3) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "upstream sent invalid :status header: \"%V\"", + &header->value); + + return NGX_DECLINED; + } + + status = ngx_atoi(header->value.data, header->value.len); + if (status == NGX_ERROR) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "upstream sent invalid :status header: \"%V\"", + &header->value); + + return NGX_DECLINED; + } + + u->headers_in.status_n = (ngx_uint_t) status; + + u->headers_in.status_line.len = header->value.len; + u->headers_in.status_line.data = header->value.data; + + if (u->state && u->state->status == 0) { + u->state->status = (ngx_uint_t) status; + } + + return NGX_OK; +} + + +static ngx_int_t ngx_http_v2_construct_request_line(ngx_http_request_t *r) { u_char *p; @@ -4113,13 +4555,23 @@ ngx_http_v2_close_stream(ngx_http_v2_str * before getting blocked on flow control. */ - if (stream->recv_window < NGX_HTTP_V2_MAX_WINDOW - && ngx_http_v2_send_window_update(h2c, node->id, - NGX_HTTP_V2_MAX_WINDOW - - stream->recv_window) - != NGX_OK) - { - h2c->connection->error = 1; + if (h2c->server) { + if (stream->recv_window < NGX_HTTP_V2_MAX_WINDOW + && ngx_http_v2_send_window_update(h2c, node->id, + NGX_HTTP_V2_MAX_WINDOW + - stream->recv_window) + != NGX_OK) + { + h2c->connection->error = 1; + } + + } else { + if (ngx_http_v2_send_rst_stream(h2c, node->id, + NGX_HTTP_V2_NO_ERROR) + != NGX_OK) + { + h2c->connection->error = 1; + } } #endif } @@ -4134,23 +4586,27 @@ ngx_http_v2_close_stream(ngx_http_v2_str ngx_queue_insert_tail(&h2c->closed, &node->reuse); h2c->closed_nodes++; - /* - * This pool keeps decoded request headers which can be used by log phase - * handlers in ngx_http_free_request(). - * - * The pointer is stored into local variable because the stream object - * will be destroyed after a call to ngx_http_free_request(). - */ - pool = stream->pool; - - ngx_http_free_request(stream->request, rc); - - if (pool != h2c->state.pool) { - ngx_destroy_pool(pool); - - } else { - /* pool will be destroyed when the complete header is parsed */ - h2c->state.keep_pool = 0; + if (h2c->server) { + + /* + * This pool keeps decoded request headers which can be used by log + * phase handlers in ngx_http_free_request(). + * + * The pointer is stored into local variable because the stream object + * will be destroyed after a call to ngx_http_free_request(). + */ + + pool = stream->pool; + + ngx_http_free_request(stream->request, rc); + + if (pool != h2c->state.pool) { + ngx_destroy_pool(pool); + + } else { + /* pool will be destroyed when the complete header is parsed */ + h2c->state.keep_pool = 0; + } } ev = fc->read; @@ -4302,7 +4758,7 @@ ngx_http_v2_idle_handler(ngx_event_t *re } -static void +void ngx_http_v2_finalize_connection(ngx_http_v2_connection_t *h2c, ngx_uint_t status) { @@ -4552,8 +5008,18 @@ ngx_http_v2_node_children_update(ngx_htt static void +ngx_http_v2_headers_pool_cleanup(void *data) +{ + ngx_pool_t *pool = data; + + ngx_destroy_pool(pool); +} + + +static void ngx_http_v2_pool_cleanup(void *data) { + ngx_connection_t *c; ngx_http_v2_connection_t *h2c = data; if (h2c->state.pool) { @@ -4563,4 +5029,13 @@ ngx_http_v2_pool_cleanup(void *data) if (h2c->pool) { ngx_destroy_pool(h2c->pool); } + + /* c->log and friends are allocated from c->pool. */ + + c = h2c->connection; + + c->log = ngx_cycle->log; + c->pool->log = ngx_cycle->log; + c->read->log = ngx_cycle->log; + c->write->log = ngx_cycle->log; } diff -r 00bfd879eaf0 -r 154ca6c5e62a src/http/v2/ngx_http_v2.h --- a/src/http/v2/ngx_http_v2.h +++ b/src/http/v2/ngx_http_v2.h @@ -26,6 +26,23 @@ #define NGX_HTTP_V2_FRAME_HEADER_SIZE 9 + +/* errors */ +#define NGX_HTTP_V2_NO_ERROR 0x0 +#define NGX_HTTP_V2_PROTOCOL_ERROR 0x1 +#define NGX_HTTP_V2_INTERNAL_ERROR 0x2 +#define NGX_HTTP_V2_FLOW_CTRL_ERROR 0x3 +#define NGX_HTTP_V2_SETTINGS_TIMEOUT 0x4 +#define NGX_HTTP_V2_STREAM_CLOSED 0x5 +#define NGX_HTTP_V2_SIZE_ERROR 0x6 +#define NGX_HTTP_V2_REFUSED_STREAM 0x7 +#define NGX_HTTP_V2_CANCEL 0x8 +#define NGX_HTTP_V2_COMP_ERROR 0x9 +#define NGX_HTTP_V2_CONNECT_ERROR 0xa +#define NGX_HTTP_V2_ENHANCE_YOUR_CALM 0xb +#define NGX_HTTP_V2_INADEQUATE_SECURITY 0xc +#define NGX_HTTP_V2_HTTP_1_1_REQUIRED 0xd + /* frame types */ #define NGX_HTTP_V2_DATA_FRAME 0x0 #define NGX_HTTP_V2_HEADERS_FRAME 0x1 @@ -48,6 +65,7 @@ #define NGX_HTTP_V2_MAX_WINDOW ((1U << 31) - 1) #define NGX_HTTP_V2_DEFAULT_WINDOW 65535 +#define NGX_HTTP_V2_PREREAD_WINDOW 65536 typedef struct ngx_http_v2_connection_s ngx_http_v2_connection_t; @@ -73,6 +91,7 @@ typedef struct { unsigned incomplete:1; unsigned keep_pool:1; + unsigned headers:1; /* HPACK */ unsigned parse_name:1; @@ -147,6 +166,7 @@ struct ngx_http_v2_connection_s { unsigned settings_ack:1; unsigned blocked:1; unsigned goaway:1; + unsigned server:1; }; @@ -268,20 +288,65 @@ static ngx_inline void ngx_http_v2_queue_ordered_frame(ngx_http_v2_connection_t *h2c, ngx_http_v2_out_frame_t *frame) { + if (frame->stream && !frame->blocked) { + frame->blocked = 1; + } + frame->next = h2c->last_out; h2c->last_out = frame; } void ngx_http_v2_init(ngx_event_t *rev); +ngx_http_v2_connection_t *ngx_http_v2_init_connection(ngx_connection_t *c, + ngx_http_connection_t *hc, ngx_uint_t server); + void ngx_http_v2_request_headers_init(void); ngx_int_t ngx_http_v2_read_request_body(ngx_http_request_t *r); ngx_int_t ngx_http_v2_read_unbuffered_request_body(ngx_http_request_t *r); +ngx_http_v2_stream_t *ngx_http_v2_create_stream(ngx_http_v2_connection_t *h2c, + ngx_http_request_t *r); + +ngx_http_v2_node_t * +ngx_http_v2_get_node_by_id(ngx_http_v2_connection_t *h2c, ngx_uint_t sid, + ngx_uint_t alloc); + +ngx_int_t ngx_http_v2_send_output_queue(ngx_http_v2_connection_t *h2c); +ngx_int_t ngx_http_v2_send_window_update(ngx_http_v2_connection_t *h2c, + ngx_uint_t sid, size_t window); +ngx_int_t ngx_http_v2_send_goaway(ngx_http_v2_connection_t *h2c, + ngx_uint_t status); + void ngx_http_v2_close_stream(ngx_http_v2_stream_t *stream, ngx_int_t rc); -ngx_int_t ngx_http_v2_send_output_queue(ngx_http_v2_connection_t *h2c); +void ngx_http_v2_finalize_connection(ngx_http_v2_connection_t *h2c, + ngx_uint_t status); + + +ngx_http_v2_out_frame_t *ngx_http_v2_create_headers_frame( + ngx_http_v2_stream_t *stream, u_char *pos, u_char *end, ngx_uint_t fin); + +ngx_chain_t *ngx_http_v2_send_chain(ngx_connection_t *fc, ngx_chain_t *in, + off_t limit); +ngx_int_t ngx_http_v2_filter_send(ngx_connection_t *fc, + ngx_http_v2_stream_t *stream); +void ngx_http_v2_filter_cleanup(void *data); + + +ngx_int_t ngx_http_v2_upstream_output_filter(void *data, ngx_chain_t *in); + +ngx_int_t ngx_http_v2_stream_buffer_init(ngx_http_v2_stream_t *stream); +ngx_int_t ngx_http_v2_stream_buffer_save(ngx_http_v2_stream_t *stream, + u_char *pos, size_t size); + +ssize_t ngx_http_v2_recv(ngx_connection_t *c, u_char *buf, size_t size); +ssize_t ngx_http_v2_recv_chain(ngx_connection_t *c, ngx_chain_t *cl, + off_t limit); + +void ngx_http_v2_upstream_free_stream(ngx_http_request_t *r, + ngx_http_upstream_t *u); ngx_int_t ngx_http_v2_get_indexed_header(ngx_http_v2_connection_t *h2c, diff -r 00bfd879eaf0 -r 154ca6c5e62a src/http/v2/ngx_http_v2_filter_module.c --- a/src/http/v2/ngx_http_v2_filter_module.c +++ b/src/http/v2/ngx_http_v2_filter_module.c @@ -57,14 +57,9 @@ static u_char *ngx_http_v2_string_encode u_char *tmp, ngx_uint_t lower); static u_char *ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix, ngx_uint_t value); -static ngx_http_v2_out_frame_t *ngx_http_v2_create_headers_frame( - ngx_http_request_t *r, u_char *pos, u_char *end, ngx_uint_t fin); static ngx_http_v2_out_frame_t *ngx_http_v2_create_trailers_frame( ngx_http_request_t *r); -static ngx_chain_t *ngx_http_v2_send_chain(ngx_connection_t *fc, - ngx_chain_t *in, off_t limit); - static ngx_chain_t *ngx_http_v2_filter_get_shadow( ngx_http_v2_stream_t *stream, ngx_buf_t *buf, off_t offset, off_t size); static ngx_http_v2_out_frame_t *ngx_http_v2_filter_get_data_frame( @@ -76,9 +71,6 @@ static ngx_inline ngx_int_t ngx_http_v2_ static void ngx_http_v2_waiting_queue(ngx_http_v2_connection_t *h2c, ngx_http_v2_stream_t *stream); -static ngx_inline ngx_int_t ngx_http_v2_filter_send( - ngx_connection_t *fc, ngx_http_v2_stream_t *stream); - static ngx_int_t ngx_http_v2_headers_frame_handler( ngx_http_v2_connection_t *h2c, ngx_http_v2_out_frame_t *frame); static ngx_int_t ngx_http_v2_data_frame_handler( @@ -88,8 +80,6 @@ static ngx_inline void ngx_http_v2_handl static ngx_inline void ngx_http_v2_handle_stream( ngx_http_v2_connection_t *h2c, ngx_http_v2_stream_t *stream); -static void ngx_http_v2_filter_cleanup(void *data); - static ngx_int_t ngx_http_v2_filter_init(ngx_conf_t *cf); @@ -616,7 +606,8 @@ ngx_http_v2_header_filter(ngx_http_reque header[i].value.len, tmp); } - frame = ngx_http_v2_create_headers_frame(r, start, pos, r->header_only); + frame = ngx_http_v2_create_headers_frame(r->stream, start, pos, + r->header_only); if (frame == NULL) { return NGX_ERROR; } @@ -633,9 +624,6 @@ ngx_http_v2_header_filter(ngx_http_reque cln->handler = ngx_http_v2_filter_cleanup; cln->data = r->stream; - fc->send_chain = ngx_http_v2_send_chain; - fc->need_last_buf = 1; - return ngx_http_v2_filter_send(fc, r->stream); } @@ -748,7 +736,7 @@ ngx_http_v2_create_trailers_frame(ngx_ht header[i].value.len, tmp); } - return ngx_http_v2_create_headers_frame(r, start, pos, 1); + return ngx_http_v2_create_headers_frame(r->stream, start, pos, 1); } @@ -800,18 +788,18 @@ ngx_http_v2_write_int(u_char *pos, ngx_u } -static ngx_http_v2_out_frame_t * -ngx_http_v2_create_headers_frame(ngx_http_request_t *r, u_char *pos, +ngx_http_v2_out_frame_t * +ngx_http_v2_create_headers_frame(ngx_http_v2_stream_t *stream, u_char *pos, u_char *end, ngx_uint_t fin) { u_char type, flags; size_t rest, frame_size; ngx_buf_t *b; ngx_chain_t *cl, **ll; - ngx_http_v2_stream_t *stream; + ngx_http_request_t *r; ngx_http_v2_out_frame_t *frame; - stream = r->stream; + r = stream->request; rest = end - pos; frame = ngx_palloc(r->pool, sizeof(ngx_http_v2_out_frame_t)); @@ -905,7 +893,7 @@ ngx_http_v2_create_headers_frame(ngx_htt } -static ngx_chain_t * +ngx_chain_t * ngx_http_v2_send_chain(ngx_connection_t *fc, ngx_chain_t *in, off_t limit) { off_t size, offset; @@ -918,7 +906,7 @@ ngx_http_v2_send_chain(ngx_connection_t ngx_http_v2_connection_t *h2c; r = fc->data; - stream = r->stream; + stream = (fc == r->connection) ? r->stream : r->upstream->stream; #if (NGX_SUPPRESS_WARN) size = 0; @@ -1052,7 +1040,7 @@ ngx_http_v2_send_chain(ngx_connection_t size -= rest; } - if (cl->buf->last_buf) { + if (cl->buf->last_buf && r->expect_trailers && stream == r->stream) { trailers = ngx_http_v2_create_trailers_frame(r); if (trailers == NULL) { return NGX_CHAIN_ERROR; @@ -1227,7 +1215,7 @@ ngx_http_v2_filter_get_data_frame(ngx_ht } -static ngx_inline ngx_int_t +ngx_int_t ngx_http_v2_filter_send(ngx_connection_t *fc, ngx_http_v2_stream_t *stream) { stream->blocked = 1; @@ -1349,8 +1337,10 @@ ngx_http_v2_headers_frame_handler(ngx_ht "http2:%ui HEADERS frame %p was sent", stream->node->id, frame); - stream->request->header_size += NGX_HTTP_V2_FRAME_HEADER_SIZE - + frame->length; + if (h2c->server) { + stream->request->header_size += NGX_HTTP_V2_FRAME_HEADER_SIZE + + frame->length; + } ngx_http_v2_handle_frame(stream, frame); @@ -1443,7 +1433,9 @@ done: "http2:%ui DATA frame %p was sent", stream->node->id, frame); - stream->request->header_size += NGX_HTTP_V2_FRAME_HEADER_SIZE; + if (h2c->server) { + stream->request->header_size += NGX_HTTP_V2_FRAME_HEADER_SIZE; + } ngx_http_v2_handle_frame(stream, frame); @@ -1501,7 +1493,7 @@ ngx_http_v2_handle_stream(ngx_http_v2_co } -static void +void ngx_http_v2_filter_cleanup(void *data) { ngx_http_v2_stream_t *stream = data; diff -r 00bfd879eaf0 -r 154ca6c5e62a src/http/v2/ngx_http_v2_module.c --- a/src/http/v2/ngx_http_v2_module.c +++ b/src/http/v2/ngx_http_v2_module.c @@ -362,7 +362,8 @@ ngx_http_v2_merge_srv_conf(ngx_conf_t *c ngx_conf_merge_size_value(conf->max_header_size, prev->max_header_size, 16384); - ngx_conf_merge_size_value(conf->preread_size, prev->preread_size, 65536); + ngx_conf_merge_size_value(conf->preread_size, prev->preread_size, + NGX_HTTP_V2_PREREAD_WINDOW); ngx_conf_merge_uint_value(conf->streams_index_mask, prev->streams_index_mask, 32 - 1); diff -r 00bfd879eaf0 -r 154ca6c5e62a src/http/v2/ngx_http_v2_upstream.c --- /dev/null +++ b/src/http/v2/ngx_http_v2_upstream.c @@ -0,0 +1,465 @@ + +/* + * Copyright (C) Nginx, Inc. + * Copyright (C) Google Inc. + */ + + +#include +#include +#include + + +ngx_int_t +ngx_http_v2_upstream_output_filter(void *data, ngx_chain_t *in) +{ + ngx_http_request_t *r = data; + + ngx_buf_t *b; + ngx_uint_t sid; + ngx_http_v2_node_t *node; + ngx_http_upstream_t *u; + ngx_http_v2_stream_t *stream; + ngx_http_v2_out_frame_t *frame; + ngx_http_v2_connection_t *h2c; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http2 upstream output filter"); + + u = r->upstream; + + if (!u->stream->node) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http2 upstream output header"); + + stream = u->stream; + h2c = stream->connection; + + sid = h2c->last_sid ? h2c->last_sid + 2 : 1; + + node = ngx_http_v2_get_node_by_id(h2c, sid, 1); + if (node == NULL) { + return NGX_ERROR; + } + + if (node->parent) { + ngx_queue_remove(&node->reuse); + h2c->closed_nodes--; + } + + node->stream = stream; + stream->node = node; + + h2c->last_sid = sid; + + b = in->buf; + + frame = ngx_http_v2_create_headers_frame(stream, b->pos, b->last, + b->last_buf); + if (frame == NULL) { + return NGX_ERROR; + } + + /* HEADERS frame handler doesn't update original buffer */ + b->pos = b->last; + + ngx_http_v2_queue_blocked_frame(h2c, frame); + stream->queued = 1; + + in = in->next; + + if (in == NULL) { + return ngx_http_v2_filter_send(stream->fake_connection, stream); + } + } + + return ngx_chain_writer(&r->upstream->writer, in); +} + + +ngx_int_t +ngx_http_v2_stream_buffer_init(ngx_http_v2_stream_t *stream) +{ + off_t size; + ngx_event_t *rev; + ngx_connection_t *fc; + ngx_http_upstream_t *u; + + u = stream->request->upstream; + fc = stream->fake_connection; + rev = fc->read; + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, fc->log, 0, + "http2 stream buffer init s:%ui l:%O eof:%ud", + u->headers_in.status_n, u->headers_in.content_length_n, + stream->in_closed); + + if (stream->in_closed) { + rev->eof = 1; + rev->ready = 1; + rev->active = 0; + + return NGX_OK; + } + + if (u->headers_in.status_n == NGX_HTTP_NO_CONTENT + || u->headers_in.status_n == NGX_HTTP_NOT_MODIFIED + || u->headers_in.content_length_n == 0) + { + rev->ready = 0; + rev->active = 1; + + return NGX_OK; + } + + if (u->headers_in.content_length_n == -1 + || u->headers_in.content_length_n > NGX_HTTP_V2_PREREAD_WINDOW) + { + size = NGX_HTTP_V2_PREREAD_WINDOW; + + } else { + size = u->headers_in.content_length_n; + } + + fc->buffer = ngx_create_temp_buf(fc->pool, size); + if (fc->buffer == NULL) { + return NGX_ERROR; + } + + rev->ready = 0; + rev->active = 1; + + return NGX_OK; +} + + +ngx_int_t +ngx_http_v2_stream_buffer_save(ngx_http_v2_stream_t *stream, u_char *pos, + size_t size) +{ + size_t free_chunk, free_total; + ngx_buf_t *b; + ngx_event_t *rev; + ngx_connection_t *fc; + + fc = stream->fake_connection; + rev = fc->read; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, fc->log, 0, + "http2 stream buffer save s:%uz eof:%ud", + size, stream->in_closed); + + if (stream->in_closed) { + if (rev->available || size) { + rev->pending_eof = 1; + + } else { + rev->pending_eof = 0; + rev->eof = 1; + rev->ready = 1; + rev->active = 0; + } + + if (size == 0) { + return NGX_OK; + } + + } else if (rev->pending_eof || rev->eof || size == 0) { + rev->ready = 0; + rev->active = 1; + return NGX_ERROR; + } + + b = fc->buffer; + + if (b == NULL || b->start == NULL) { + rev->ready = 0; + rev->active = 1; + return NGX_ERROR; + } + + if (b->last == b->end) { + b->last = b->start; + } + + if (b->pos <= b->last) { + free_chunk = b->end - b->last; + free_total = free_chunk + (b->pos - b->start); + + } else { + free_chunk = b->pos - b->last; + free_total = free_chunk; + } + + if (size > free_total) { + rev->ready = 0; + rev->active = 1; + return NGX_ERROR; + } + + if (free_chunk > size) { + free_chunk = size; + } + + b->last = ngx_cpymem(b->last, pos, free_chunk); + pos += free_chunk; + size -= free_chunk; + + if (size) { + b->last = ngx_cpymem(b->start, pos, size); + pos += size; + } + + rev->ready = 1; + rev->active = 0; + +#if (NGX_HAVE_KQUEUE) + rev->available += free_chunk + size; +#else + rev->available = 1; +#endif + + return NGX_OK; +} + + +ssize_t +ngx_http_v2_recv(ngx_connection_t *fc, u_char *buf, size_t size) +{ + size_t saved_chunk, saved_total; + ssize_t bytes; + ngx_buf_t *b; + ngx_event_t *rev; + ngx_http_request_t *r; + ngx_http_v2_stream_t *stream; + ngx_http_v2_connection_t *h2c; + + rev = fc->read; + + if (fc->error) { + rev->ready = 0; + rev->active = 1; + return NGX_ERROR; + } + + if (rev->eof) { + rev->ready = 0; + rev->active = 1; + return 0; + } + + b = fc->buffer; + + if (b == NULL || b->start == NULL) { + rev->ready = 0; + rev->active = 1; + return NGX_ERROR; + } + + if (!rev->available) { + return NGX_AGAIN; + } + + if (b->pos == b->end) { + b->pos = b->start; + } + + if (b->pos < b->last) { + saved_chunk = b->last - b->pos; + saved_total = saved_chunk; + + } else { + saved_chunk = b->end - b->pos; + saved_total = saved_chunk + (b->last - b->start); + } + + if (size > saved_total) { + size = saved_total; + } + + if (saved_chunk > size) { + saved_chunk = size; + } + + buf = ngx_cpymem(buf, b->pos, saved_chunk); + b->pos += saved_chunk; + size -= saved_chunk; + + if (size) { + buf = ngx_cpymem(buf, b->start, size); + b->pos = b->start + size; + } + + bytes = saved_chunk + size; + +#if (NGX_HAVE_KQUEUE) + rev->available -= bytes; +#endif + + if (b->last == b->pos) { + + if (rev->pending_eof) { + rev->pending_eof = 0; + rev->eof = 1; + + ngx_pfree(fc->pool, b->start); + b->start = NULL; + + } else { + rev->ready = 0; + rev->active = 1; + + b->pos = b->start; + b->last = b->start; + } + +#if !(NGX_HAVE_KQUEUE) + rev->available = 0; +#endif + } + + if (rev->pending_eof || rev->eof) { + return bytes; + } + + r = fc->data; + stream = r->upstream->stream; + h2c = stream->connection; + + if (ngx_http_v2_send_window_update(h2c, stream->node->id, bytes) + == NGX_ERROR) + { + ngx_http_v2_finalize_connection(h2c, NGX_HTTP_V2_INTERNAL_ERROR); + return NGX_ERROR; + } + + stream->recv_window += bytes; + + if (!h2c->blocked) { + if (ngx_http_v2_send_output_queue(h2c) == NGX_ERROR) { + ngx_http_v2_finalize_connection(h2c, NGX_HTTP_V2_INTERNAL_ERROR); + return NGX_ERROR; + } + } + + return bytes; +} + + +ssize_t +ngx_http_v2_recv_chain(ngx_connection_t *fc, ngx_chain_t *cl, off_t limit) +{ + u_char *last; + ssize_t n, bytes, size; + ngx_buf_t *b; + + bytes = 0; + + b = cl->buf; + last = b->last; + + for ( ;; ) { + size = b->end - last; + + if (limit) { + if (bytes >= limit) { + return bytes; + } + + if (bytes + size > limit) { + size = (ssize_t) (limit - bytes); + } + } + + n = ngx_http_v2_recv(fc, last, size); + + if (n > 0) { + last += n; + bytes += n; + + if (last == b->end) { + cl = cl->next; + + if (cl == NULL) { + return bytes; + } + + b = cl->buf; + last = b->last; + } + + continue; + } + + if (bytes) { + + if (n == 0 || n == NGX_ERROR) { + fc->read->ready = 1; + fc->read->active = 0; + } + + return bytes; + } + + return n; + } +} + + +void +ngx_http_v2_upstream_free_stream(ngx_http_request_t *r, ngx_http_upstream_t *u) +{ + ngx_http_v2_stream_t *stream; + ngx_http_v2_connection_t *h2c; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http2 upstream free stream"); + + stream = u->stream; + u->stream = NULL; + + h2c = stream->connection; + + if (stream->queued) { + ngx_http_v2_filter_cleanup(stream); + + if (stream->queued && !h2c->blocked) { + if (ngx_http_v2_send_output_queue(h2c) == NGX_ERROR) { + ngx_http_v2_finalize_connection(h2c, 0); + goto error; + } + } + } + + ngx_http_v2_close_stream(stream, 0); + + if (h2c->connection->error) { + goto error; + } + + if (stream->queued && !h2c->goaway) { + h2c->goaway = 1; + + if (ngx_http_v2_send_goaway(h2c, NGX_HTTP_V2_NO_ERROR) == NGX_ERROR) { + ngx_http_v2_finalize_connection(h2c, NGX_HTTP_V2_INTERNAL_ERROR); + goto error; + } + } + + if (h2c->last_out && !h2c->blocked) { + if (ngx_http_v2_send_output_queue(h2c) == NGX_ERROR) { + ngx_http_v2_finalize_connection(h2c, 0); + goto error; + } + } + + u->peer.connection = h2c->connection; + u->keepalive = !h2c->goaway; + + return; + +error: + + u->peer.connection = NULL; + u->peer.sockaddr = NULL; +} From piotrsikora at google.com Thu Jun 22 20:33:13 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Thu, 22 Jun 2017 13:33:13 -0700 Subject: [PATCH 09 of 14] Proxy: add "proxy_ssl_alpn" directive In-Reply-To: <5f5d70428655db0889a2.1498163585@piotrsikora.sfo.corp.google.com> References: <5f5d70428655db0889a2.1498163585@piotrsikora.sfo.corp.google.com> Message-ID: <96075d4cd2a6e8bd67ca.1498163593@piotrsikora.sfo.corp.google.com> # HG changeset patch # User Piotr Sikora # Date 1489621682 25200 # Wed Mar 15 16:48:02 2017 -0700 # Node ID 96075d4cd2a6e8bd67caf1d7b78f8e87d757c48d # Parent 154ca6c5e62a1931a616e9f2b99ef2553b7c2c8b Proxy: add "proxy_ssl_alpn" directive. ALPN is used here only to indicate which version of the HTTP protocol is going to be used and we doesn't verify that upstream agreed to it. Please note that upstream is allowed to reject SSL connection with a fatal "no_application_protocol" alert if it doesn't support it. Signed-off-by: Piotr Sikora diff -r 154ca6c5e62a -r 96075d4cd2a6 src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -654,6 +654,29 @@ ngx_ssl_ciphers(ngx_conf_t *cf, ngx_ssl_ ngx_int_t +ngx_ssl_alpn_protos(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *protos) +{ +#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation + + if (SSL_CTX_set_alpn_protos(ssl->ctx, protos->data, protos->len) != 0) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "SSL_CTX_set_alpn_protos() failed"); + return NGX_ERROR; + } + + return NGX_OK; + +#else + + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "nginx was built with OpenSSL that lacks ALPN support"); + return NGX_ERROR; + +#endif +} + + +ngx_int_t ngx_ssl_client_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert, ngx_int_t depth) { diff -r 154ca6c5e62a -r 96075d4cd2a6 src/event/ngx_event_openssl.h --- a/src/event/ngx_event_openssl.h +++ b/src/event/ngx_event_openssl.h @@ -153,6 +153,8 @@ ngx_int_t ngx_ssl_certificate(ngx_conf_t ngx_str_t *cert, ngx_str_t *key, ngx_array_t *passwords); ngx_int_t ngx_ssl_ciphers(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *ciphers, ngx_uint_t prefer_server_ciphers); +ngx_int_t ngx_ssl_alpn_protos(ngx_conf_t *cf, ngx_ssl_t *ssl, + ngx_str_t *protos); ngx_int_t ngx_ssl_client_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert, ngx_int_t depth); ngx_int_t ngx_ssl_trusted_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, diff -r 154ca6c5e62a -r 96075d4cd2a6 src/http/modules/ngx_http_proxy_module.c --- a/src/http/modules/ngx_http_proxy_module.c +++ b/src/http/modules/ngx_http_proxy_module.c @@ -652,6 +652,13 @@ static ngx_command_t ngx_http_proxy_com offsetof(ngx_http_proxy_loc_conf_t, ssl_ciphers), NULL }, + { ngx_string("proxy_ssl_alpn"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_proxy_loc_conf_t, upstream.ssl_alpn), + NULL }, + { ngx_string("proxy_ssl_name"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_http_set_complex_value_slot, @@ -2882,6 +2889,7 @@ ngx_http_proxy_create_loc_conf(ngx_conf_ conf->upstream.intercept_errors = NGX_CONF_UNSET; #if (NGX_HTTP_SSL) + conf->upstream.ssl_alpn = NGX_CONF_UNSET; conf->upstream.ssl_session_reuse = NGX_CONF_UNSET; conf->upstream.ssl_server_name = NGX_CONF_UNSET; conf->upstream.ssl_verify = NGX_CONF_UNSET; @@ -3212,6 +3220,8 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t conf->upstream.ssl_name = prev->upstream.ssl_name; } + ngx_conf_merge_value(conf->upstream.ssl_alpn, + prev->upstream.ssl_alpn, 0); ngx_conf_merge_value(conf->upstream.ssl_server_name, prev->upstream.ssl_server_name, 0); ngx_conf_merge_value(conf->upstream.ssl_verify, @@ -4320,6 +4330,7 @@ ngx_http_proxy_lowat_check(ngx_conf_t *c static ngx_int_t ngx_http_proxy_set_ssl(ngx_conf_t *cf, ngx_http_proxy_loc_conf_t *plcf) { + ngx_str_t alpn; ngx_pool_cleanup_t *cln; plcf->upstream.ssl = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_t)); @@ -4366,6 +4377,24 @@ ngx_http_proxy_set_ssl(ngx_conf_t *cf, n return NGX_ERROR; } + if (plcf->upstream.ssl_alpn) { + + switch (plcf->http_version) { + + case NGX_HTTP_VERSION_10: + ngx_str_set(&alpn, NGX_HTTP_10_ALPN_ADVERTISE); + break; + + case NGX_HTTP_VERSION_11: + ngx_str_set(&alpn, NGX_HTTP_11_ALPN_ADVERTISE); + break; + } + + if (ngx_ssl_alpn_protos(cf, plcf->upstream.ssl, &alpn) != NGX_OK) { + return NGX_ERROR; + } + } + if (plcf->upstream.ssl_verify) { if (plcf->ssl_trusted_certificate.len == 0) { ngx_log_error(NGX_LOG_EMERG, cf->log, 0, diff -r 154ca6c5e62a -r 96075d4cd2a6 src/http/modules/ngx_http_ssl_module.c --- a/src/http/modules/ngx_http_ssl_module.c +++ b/src/http/modules/ngx_http_ssl_module.c @@ -17,8 +17,6 @@ typedef ngx_int_t (*ngx_ssl_variable_han #define NGX_DEFAULT_CIPHERS "HIGH:!aNULL:!MD5" #define NGX_DEFAULT_ECDH_CURVE "auto" -#define NGX_HTTP_NPN_ADVERTISE "\x08http/1.1" - #ifdef TLSEXT_TYPE_application_layer_protocol_negotiation static int ngx_http_ssl_alpn_select(ngx_ssl_conn_t *ssl_conn, diff -r 154ca6c5e62a -r 96075d4cd2a6 src/http/ngx_http.h --- a/src/http/ngx_http.h +++ b/src/http/ngx_http.h @@ -13,6 +13,11 @@ #include +#define NGX_HTTP_10_ALPN_ADVERTISE "\x08http/1.0" +#define NGX_HTTP_11_ALPN_ADVERTISE "\x08http/1.1" +#define NGX_HTTP_NPN_ADVERTISE NGX_HTTP_11_ALPN_ADVERTISE + + typedef struct ngx_http_request_s ngx_http_request_t; typedef struct ngx_http_upstream_s ngx_http_upstream_t; typedef struct ngx_http_cache_s ngx_http_cache_t; diff -r 154ca6c5e62a -r 96075d4cd2a6 src/http/ngx_http_upstream.h --- a/src/http/ngx_http_upstream.h +++ b/src/http/ngx_http_upstream.h @@ -224,6 +224,7 @@ typedef struct { #if (NGX_HTTP_SSL || NGX_COMPAT) ngx_ssl_t *ssl; + ngx_flag_t ssl_alpn; ngx_flag_t ssl_session_reuse; ngx_http_complex_value_t *ssl_name; From piotrsikora at google.com Thu Jun 22 20:33:14 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Thu, 22 Jun 2017 13:33:14 -0700 Subject: [PATCH 10 of 14] Proxy: always emit "Host" header first In-Reply-To: <5f5d70428655db0889a2.1498163585@piotrsikora.sfo.corp.google.com> References: <5f5d70428655db0889a2.1498163585@piotrsikora.sfo.corp.google.com> Message-ID: <068381014f256ad6e2dc.1498163594@piotrsikora.sfo.corp.google.com> # HG changeset patch # User Piotr Sikora # Date 1489618489 25200 # Wed Mar 15 15:54:49 2017 -0700 # Node ID 068381014f256ad6e2dc490bacc2529cebbb0462 # Parent 96075d4cd2a6e8bd67caf1d7b78f8e87d757c48d Proxy: always emit "Host" header first. Signed-off-by: Piotr Sikora diff -r 96075d4cd2a6 -r 068381014f25 src/http/modules/ngx_http_proxy_module.c --- a/src/http/modules/ngx_http_proxy_module.c +++ b/src/http/modules/ngx_http_proxy_module.c @@ -3422,7 +3422,7 @@ ngx_http_proxy_init_headers(ngx_conf_t * uintptr_t *code; ngx_uint_t i; ngx_array_t headers_names, headers_merged; - ngx_keyval_t *src, *s, *h; + ngx_keyval_t *host, *src, *s, *h; ngx_hash_key_t *hk; ngx_hash_init_t hash; ngx_http_script_compile_t sc; @@ -3454,11 +3454,33 @@ ngx_http_proxy_init_headers(ngx_conf_t * return NGX_ERROR; } + h = default_headers; + + if (h->key.len != sizeof("Host") - 1 + || ngx_strcasecmp(h->key.data, (u_char *) "Host") != 0) + { + return NGX_ERROR; + } + + host = ngx_array_push(&headers_merged); + if (host == NULL) { + return NGX_ERROR; + } + + *host = *h++; + if (conf->headers_source) { src = conf->headers_source->elts; for (i = 0; i < conf->headers_source->nelts; i++) { + if (src[i].key.len == sizeof("Host") - 1 + && ngx_strcasecmp(src[i].key.data, (u_char *) "Host") == 0) + { + *host = src[i]; + continue; + } + s = ngx_array_push(&headers_merged); if (s == NULL) { return NGX_ERROR; @@ -3468,8 +3490,6 @@ ngx_http_proxy_init_headers(ngx_conf_t * } } - h = default_headers; - while (h->key.len) { src = headers_merged.elts; From piotrsikora at google.com Thu Jun 22 20:33:15 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Thu, 22 Jun 2017 13:33:15 -0700 Subject: [PATCH 11 of 14] Proxy: split configured header names and values In-Reply-To: <5f5d70428655db0889a2.1498163585@piotrsikora.sfo.corp.google.com> References: <5f5d70428655db0889a2.1498163585@piotrsikora.sfo.corp.google.com> Message-ID: <0637acdb51e29e1f68f5.1498163595@piotrsikora.sfo.corp.google.com> # HG changeset patch # User Piotr Sikora # Date 1489618535 25200 # Wed Mar 15 15:55:35 2017 -0700 # Node ID 0637acdb51e29e1f68f5f3e762115c702cab4e72 # Parent 068381014f256ad6e2dc490bacc2529cebbb0462 Proxy: split configured header names and values. Previously, each configured header was represented in one of two ways, depending on whether or not its value included any variables. If the value didn't include any variables, then it would be represented as as a single script that contained complete header line with HTTP/1.1 delimiters, i.e.: "Header: value\r\n" But if the value included any variables, then it would be represented as a series of three scripts: first contained header name and the ": " delimiter, second evaluated to header value, and third contained only "\r\n", i.e.: "Header: " "$value" "\r\n" This commit changes that, so that each configured header is represented as a series of two scripts: first contains only header name, and second contains (or evaluates to) only header value, i.e.: "Header" "$value" or "Header" "value" This not only makes things more consistent, but also allows header name and value to be accessed separately. Signed-off-by: Piotr Sikora diff -r 068381014f25 -r 0637acdb51e2 src/http/modules/ngx_http_proxy_module.c --- a/src/http/modules/ngx_http_proxy_module.c +++ b/src/http/modules/ngx_http_proxy_module.c @@ -1151,6 +1151,7 @@ static ngx_int_t ngx_http_proxy_create_request(ngx_http_request_t *r) { size_t len, uri_len, loc_len, body_len; + size_t key_len, val_len; uintptr_t escape; ngx_buf_t *b; ngx_str_t method; @@ -1265,11 +1266,20 @@ ngx_http_proxy_create_request(ngx_http_r le.flushed = 1; while (*(uintptr_t *) le.ip) { - while (*(uintptr_t *) le.ip) { + + lcode = *(ngx_http_script_len_code_pt *) le.ip; + key_len = lcode(&le); + + for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) { lcode = *(ngx_http_script_len_code_pt *) le.ip; - len += lcode(&le); } le.ip += sizeof(uintptr_t); + + if (val_len == 0) { + continue; + } + + len += key_len + sizeof(": ") - 1 + val_len + sizeof(CRLF) - 1; } @@ -1369,30 +1379,41 @@ ngx_http_proxy_create_request(ngx_http_r le.ip = headers->lengths->elts; while (*(uintptr_t *) le.ip) { + lcode = *(ngx_http_script_len_code_pt *) le.ip; - - /* skip the header line name length */ (void) lcode(&le); - if (*(ngx_http_script_len_code_pt *) le.ip) { - - for (len = 0; *(uintptr_t *) le.ip; len += lcode(&le)) { - lcode = *(ngx_http_script_len_code_pt *) le.ip; + for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) { + lcode = *(ngx_http_script_len_code_pt *) le.ip; + } + le.ip += sizeof(uintptr_t); + + if (val_len == 0) { + e.skip = 1; + + while (*(uintptr_t *) e.ip) { + code = *(ngx_http_script_code_pt *) e.ip; + code((ngx_http_script_engine_t *) &e); } - - e.skip = (len == sizeof(CRLF) - 1) ? 1 : 0; - - } else { + e.ip += sizeof(uintptr_t); + e.skip = 0; + + continue; } - le.ip += sizeof(uintptr_t); + code = *(ngx_http_script_code_pt *) e.ip; + code((ngx_http_script_engine_t *) &e); + + *e.pos++ = ':'; *e.pos++ = ' '; while (*(uintptr_t *) e.ip) { code = *(ngx_http_script_code_pt *) e.ip; code((ngx_http_script_engine_t *) &e); } e.ip += sizeof(uintptr_t); + + *e.pos++ = CR; *e.pos++ = LF; } b->last = e.pos; @@ -3528,108 +3549,40 @@ ngx_http_proxy_init_headers(ngx_conf_t * continue; } - if (ngx_http_script_variables_count(&src[i].value) == 0) { - copy = ngx_array_push_n(headers->lengths, - sizeof(ngx_http_script_copy_code_t)); - if (copy == NULL) { - return NGX_ERROR; - } - - copy->code = (ngx_http_script_code_pt) - ngx_http_script_copy_len_code; - copy->len = src[i].key.len + sizeof(": ") - 1 - + src[i].value.len + sizeof(CRLF) - 1; - - - size = (sizeof(ngx_http_script_copy_code_t) - + src[i].key.len + sizeof(": ") - 1 - + src[i].value.len + sizeof(CRLF) - 1 - + sizeof(uintptr_t) - 1) - & ~(sizeof(uintptr_t) - 1); - - copy = ngx_array_push_n(headers->values, size); - if (copy == NULL) { - return NGX_ERROR; - } - - copy->code = ngx_http_script_copy_code; - copy->len = src[i].key.len + sizeof(": ") - 1 - + src[i].value.len + sizeof(CRLF) - 1; - - p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t); - - p = ngx_cpymem(p, src[i].key.data, src[i].key.len); - *p++ = ':'; *p++ = ' '; - p = ngx_cpymem(p, src[i].value.data, src[i].value.len); - *p++ = CR; *p = LF; - - } else { - copy = ngx_array_push_n(headers->lengths, - sizeof(ngx_http_script_copy_code_t)); - if (copy == NULL) { - return NGX_ERROR; - } - - copy->code = (ngx_http_script_code_pt) - ngx_http_script_copy_len_code; - copy->len = src[i].key.len + sizeof(": ") - 1; - - - size = (sizeof(ngx_http_script_copy_code_t) - + src[i].key.len + sizeof(": ") - 1 + sizeof(uintptr_t) - 1) - & ~(sizeof(uintptr_t) - 1); - - copy = ngx_array_push_n(headers->values, size); - if (copy == NULL) { - return NGX_ERROR; - } - - copy->code = ngx_http_script_copy_code; - copy->len = src[i].key.len + sizeof(": ") - 1; - - p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t); - p = ngx_cpymem(p, src[i].key.data, src[i].key.len); - *p++ = ':'; *p = ' '; - - - ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); - - sc.cf = cf; - sc.source = &src[i].value; - sc.flushes = &headers->flushes; - sc.lengths = &headers->lengths; - sc.values = &headers->values; - - if (ngx_http_script_compile(&sc) != NGX_OK) { - return NGX_ERROR; - } - - - copy = ngx_array_push_n(headers->lengths, - sizeof(ngx_http_script_copy_code_t)); - if (copy == NULL) { - return NGX_ERROR; - } - - copy->code = (ngx_http_script_code_pt) - ngx_http_script_copy_len_code; - copy->len = sizeof(CRLF) - 1; - - - size = (sizeof(ngx_http_script_copy_code_t) - + sizeof(CRLF) - 1 + sizeof(uintptr_t) - 1) - & ~(sizeof(uintptr_t) - 1); - - copy = ngx_array_push_n(headers->values, size); - if (copy == NULL) { - return NGX_ERROR; - } - - copy->code = ngx_http_script_copy_code; - copy->len = sizeof(CRLF) - 1; - - p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t); - *p++ = CR; *p = LF; + copy = ngx_array_push_n(headers->lengths, + sizeof(ngx_http_script_copy_code_t)); + if (copy == NULL) { + return NGX_ERROR; + } + + copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code; + copy->len = src[i].key.len; + + size = (sizeof(ngx_http_script_copy_code_t) + + src[i].key.len + sizeof(uintptr_t) - 1) + & ~(sizeof(uintptr_t) - 1); + + copy = ngx_array_push_n(headers->values, size); + if (copy == NULL) { + return NGX_ERROR; + } + + copy->code = ngx_http_script_copy_code; + copy->len = src[i].key.len; + + p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t); + ngx_memcpy(p, src[i].key.data, src[i].key.len); + + ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); + + sc.cf = cf; + sc.source = &src[i].value; + sc.flushes = &headers->flushes; + sc.lengths = &headers->lengths; + sc.values = &headers->values; + + if (ngx_http_script_compile(&sc) != NGX_OK) { + return NGX_ERROR; } code = ngx_array_push_n(headers->lengths, sizeof(uintptr_t)); From piotrsikora at google.com Thu Jun 22 20:33:17 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Thu, 22 Jun 2017 13:33:17 -0700 Subject: [PATCH 13 of 14] Proxy: add "proxy_pass_trailers" directive In-Reply-To: <5f5d70428655db0889a2.1498163585@piotrsikora.sfo.corp.google.com> References: <5f5d70428655db0889a2.1498163585@piotrsikora.sfo.corp.google.com> Message-ID: # HG changeset patch # User Piotr Sikora # Date 1490351854 25200 # Fri Mar 24 03:37:34 2017 -0700 # Node ID cde1f42da7b26b7d2b788f916685e736b919138e # Parent 7eb807b056da7abe9c679b59e94595d59a1406e6 Proxy: add "proxy_pass_trailers" directive. Signed-off-by: Piotr Sikora diff -r 7eb807b056da -r cde1f42da7b2 src/http/modules/ngx_http_fastcgi_module.c --- a/src/http/modules/ngx_http_fastcgi_module.c +++ b/src/http/modules/ngx_http_fastcgi_module.c @@ -2788,10 +2788,10 @@ ngx_http_fastcgi_create_loc_conf(ngx_con conf->upstream.intercept_errors = NGX_CONF_UNSET; - /* "fastcgi_cyclic_temp_file" is disabled */ + /* the hardcoded values */ conf->upstream.cyclic_temp_file = 0; - conf->upstream.change_buffering = 1; + conf->upstream.pass_trailers = 0; conf->catch_stderr = NGX_CONF_UNSET_PTR; diff -r 7eb807b056da -r cde1f42da7b2 src/http/modules/ngx_http_memcached_module.c --- a/src/http/modules/ngx_http_memcached_module.c +++ b/src/http/modules/ngx_http_memcached_module.c @@ -619,6 +619,7 @@ ngx_http_memcached_create_loc_conf(ngx_c conf->upstream.pass_request_headers = 0; conf->upstream.pass_request_body = 0; conf->upstream.force_ranges = 1; + conf->upstream.pass_trailers = 0; conf->index = NGX_CONF_UNSET; conf->gzip_flag = NGX_CONF_UNSET_UINT; diff -r 7eb807b056da -r cde1f42da7b2 src/http/modules/ngx_http_proxy_module.c --- a/src/http/modules/ngx_http_proxy_module.c +++ b/src/http/modules/ngx_http_proxy_module.c @@ -630,6 +630,13 @@ static ngx_command_t ngx_http_proxy_com offsetof(ngx_http_proxy_loc_conf_t, upstream.ignore_headers), &ngx_http_upstream_ignore_headers_masks }, + { ngx_string("proxy_pass_trailers"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_proxy_loc_conf_t, upstream.pass_trailers), + NULL }, + { ngx_string("proxy_http_version"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_conf_set_enum_slot, @@ -3483,6 +3490,8 @@ ngx_http_proxy_create_loc_conf(ngx_conf_ conf->upstream.hide_headers = NGX_CONF_UNSET_PTR; conf->upstream.pass_headers = NGX_CONF_UNSET_PTR; + conf->upstream.pass_trailers = NGX_CONF_UNSET; + conf->upstream.intercept_errors = NGX_CONF_UNSET; #if (NGX_HTTP_SSL) @@ -3494,11 +3503,11 @@ ngx_http_proxy_create_loc_conf(ngx_conf_ conf->ssl_passwords = NGX_CONF_UNSET_PTR; #endif - /* "proxy_cyclic_temp_file" is disabled */ + /* the hardcoded values */ conf->upstream.cyclic_temp_file = 0; + conf->upstream.change_buffering = 1; conf->redirect = NGX_CONF_UNSET; - conf->upstream.change_buffering = 1; conf->cookie_domains = NGX_CONF_UNSET_PTR; conf->cookie_paths = NGX_CONF_UNSET_PTR; @@ -3798,6 +3807,9 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t ngx_conf_merge_value(conf->upstream.pass_request_body, prev->upstream.pass_request_body, 1); + ngx_conf_merge_value(conf->upstream.pass_trailers, + prev->upstream.pass_trailers, 0); + ngx_conf_merge_value(conf->upstream.intercept_errors, prev->upstream.intercept_errors, 0); @@ -4018,6 +4030,28 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t #endif } + if (conf->upstream.pass_trailers) { + + if (conf->http_version != NGX_HTTP_VERSION_20) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"proxy_pass_trailers\" requires " + "\"proxy_http_version 2.0\""); + return NGX_CONF_ERROR; + } + +#if (NGX_HTTP_CACHE) + + if (conf->upstream.cache) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"proxy_pass_trailers\" doesn't work with " + "\"proxy_cache\""); + return NGX_CONF_ERROR; + } + +#endif + + } + return NGX_CONF_OK; } diff -r 7eb807b056da -r cde1f42da7b2 src/http/modules/ngx_http_scgi_module.c --- a/src/http/modules/ngx_http_scgi_module.c +++ b/src/http/modules/ngx_http_scgi_module.c @@ -1236,10 +1236,10 @@ ngx_http_scgi_create_loc_conf(ngx_conf_t conf->upstream.intercept_errors = NGX_CONF_UNSET; - /* "scgi_cyclic_temp_file" is disabled */ + /* the hardcoded values */ conf->upstream.cyclic_temp_file = 0; - conf->upstream.change_buffering = 1; + conf->upstream.pass_trailers = 0; ngx_str_set(&conf->upstream.module, "scgi"); diff -r 7eb807b056da -r cde1f42da7b2 src/http/modules/ngx_http_uwsgi_module.c --- a/src/http/modules/ngx_http_uwsgi_module.c +++ b/src/http/modules/ngx_http_uwsgi_module.c @@ -1451,10 +1451,10 @@ ngx_http_uwsgi_create_loc_conf(ngx_conf_ conf->ssl_passwords = NGX_CONF_UNSET_PTR; #endif - /* "uwsgi_cyclic_temp_file" is disabled */ + /* the hardcoded values */ conf->upstream.cyclic_temp_file = 0; - conf->upstream.change_buffering = 1; + conf->upstream.pass_trailers = 0; ngx_str_set(&conf->upstream.module, "uwsgi"); diff -r 7eb807b056da -r cde1f42da7b2 src/http/ngx_http_upstream.c --- a/src/http/ngx_http_upstream.c +++ b/src/http/ngx_http_upstream.c @@ -56,6 +56,8 @@ static ngx_int_t ngx_http_upstream_test_ ngx_connection_t *c); static ngx_int_t ngx_http_upstream_process_headers(ngx_http_request_t *r, ngx_http_upstream_t *u); +static ngx_int_t ngx_http_upstream_process_trailers(ngx_http_request_t *r, + ngx_http_upstream_t *u); static void ngx_http_upstream_process_body_in_memory(ngx_http_request_t *r, ngx_http_upstream_t *u); static void ngx_http_upstream_send_response(ngx_http_request_t *r, @@ -150,6 +152,8 @@ static ngx_int_t ngx_http_upstream_rewri ngx_table_elt_t *h, ngx_uint_t offset); static ngx_int_t ngx_http_upstream_copy_allow_ranges(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); +static ngx_int_t ngx_http_upstream_copy_trailer(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset); #if (NGX_HTTP_GZIP) static ngx_int_t ngx_http_upstream_copy_content_encoding(ngx_http_request_t *r, @@ -167,6 +171,8 @@ static ngx_int_t ngx_http_upstream_respo ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_upstream_header_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_upstream_trailer_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_upstream_cookie_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); @@ -313,6 +319,10 @@ static ngx_http_upstream_header_t ngx_h ngx_http_upstream_process_charset, 0, ngx_http_upstream_copy_header_line, 0, 0 }, + { ngx_string("Trailer"), + ngx_http_upstream_ignore_header_line, 0, + ngx_http_upstream_copy_trailer, 0, 0 }, + { ngx_string("Transfer-Encoding"), ngx_http_upstream_process_transfer_encoding, 0, ngx_http_upstream_ignore_header_line, 0, 0 }, @@ -428,6 +438,9 @@ static ngx_http_variable_t ngx_http_ups { ngx_string("upstream_http_"), NULL, ngx_http_upstream_header_variable, 0, NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_PREFIX, 0 }, + { ngx_string("upstream_trailer_"), NULL, ngx_http_upstream_trailer_variable, + 0, NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_PREFIX, 0 }, + { ngx_string("upstream_cookie_"), NULL, ngx_http_upstream_cookie_variable, 0, NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_PREFIX, 0 }, @@ -1048,6 +1061,13 @@ ngx_http_upstream_cache_send(ngx_http_re return NGX_ERROR; } + if (ngx_list_init(&u->headers_in.trailers, r->pool, 2, + sizeof(ngx_table_elt_t)) + != NGX_OK) + { + return NGX_ERROR; + } + rc = u->process_header(r); if (rc == NGX_OK) { @@ -1942,6 +1962,13 @@ ngx_http_upstream_reinit(ngx_http_reques return NGX_ERROR; } + if (ngx_list_init(&u->headers_in.trailers, r->pool, 2, + sizeof(ngx_table_elt_t)) + != NGX_OK) + { + return NGX_ERROR; + } + /* reinit the request chain */ file_pos = 0; @@ -2329,6 +2356,15 @@ ngx_http_upstream_process_header(ngx_htt return; } + if (ngx_list_init(&u->headers_in.trailers, r->pool, 2, + sizeof(ngx_table_elt_t)) + != NGX_OK) + { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + #if (NGX_HTTP_CACHE) if (r->cache) { @@ -2880,6 +2916,45 @@ ngx_http_upstream_process_headers(ngx_ht } +static ngx_int_t +ngx_http_upstream_process_trailers(ngx_http_request_t *r, + ngx_http_upstream_t *u) +{ + ngx_uint_t i; + ngx_list_part_t *part; + ngx_table_elt_t *h, *ho; + + if (!r->expect_trailers || !u->conf->pass_trailers) { + return NGX_OK; + } + + part = &u->headers_in.trailers.part; + h = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + h = part->elts; + i = 0; + } + + ho = ngx_list_push(&r->headers_out.trailers); + if (ho == NULL) { + return NGX_ERROR; + } + + *ho = h[i]; + } + + return NGX_OK; +} + + static void ngx_http_upstream_process_body_in_memory(ngx_http_request_t *r, ngx_http_upstream_t *u) @@ -4491,6 +4566,13 @@ ngx_http_upstream_finalize_request(ngx_h } if (rc == 0) { + if (ngx_http_upstream_process_trailers(r, u) != NGX_OK) { + rc = NGX_ERROR; + flush = 1; + } + } + + if (rc == 0) { rc = ngx_http_send_special(r, NGX_HTTP_LAST); } else if (flush) { @@ -5277,6 +5359,27 @@ ngx_http_upstream_copy_allow_ranges(ngx_ } +static ngx_int_t +ngx_http_upstream_copy_trailer(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset) +{ + ngx_table_elt_t *ho; + + if (!r->expect_trailers || !r->upstream->conf->pass_trailers) { + return NGX_OK; + } + + ho = ngx_list_push(&r->headers_out.headers); + if (ho == NULL) { + return NGX_ERROR; + } + + *ho = *h; + + return NGX_OK; +} + + #if (NGX_HTTP_GZIP) static ngx_int_t @@ -5615,6 +5718,21 @@ ngx_http_upstream_header_variable(ngx_ht static ngx_int_t +ngx_http_upstream_trailer_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + if (r->upstream == NULL) { + v->not_found = 1; + return NGX_OK; + } + + return ngx_http_variable_unknown_header(v, (ngx_str_t *) data, + &r->upstream->headers_in.trailers.part, + sizeof("upstream_trailer_") - 1); +} + + +static ngx_int_t ngx_http_upstream_cookie_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { diff -r 7eb807b056da -r cde1f42da7b2 src/http/ngx_http_upstream.h --- a/src/http/ngx_http_upstream.h +++ b/src/http/ngx_http_upstream.h @@ -174,6 +174,7 @@ typedef struct { ngx_flag_t request_buffering; ngx_flag_t pass_request_headers; ngx_flag_t pass_request_body; + ngx_flag_t pass_trailers; ngx_flag_t ignore_client_abort; ngx_flag_t intercept_errors; @@ -251,6 +252,7 @@ typedef struct { typedef struct { ngx_list_t headers; + ngx_list_t trailers; ngx_uint_t status_n; ngx_str_t status_line; From piotrsikora at google.com Thu Jun 22 20:33:16 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Thu, 22 Jun 2017 13:33:16 -0700 Subject: [PATCH 12 of 14] Proxy: add HTTP/2 support In-Reply-To: <5f5d70428655db0889a2.1498163585@piotrsikora.sfo.corp.google.com> References: <5f5d70428655db0889a2.1498163585@piotrsikora.sfo.corp.google.com> Message-ID: <7eb807b056da7abe9c67.1498163596@piotrsikora.sfo.corp.google.com> # HG changeset patch # User Piotr Sikora # Date 1490769087 25200 # Tue Mar 28 23:31:27 2017 -0700 # Node ID 7eb807b056da7abe9c679b59e94595d59a1406e6 # Parent 0637acdb51e29e1f68f5f3e762115c702cab4e72 Proxy: add HTTP/2 support. Signed-off-by: Piotr Sikora diff -r 0637acdb51e2 -r 7eb807b056da src/http/modules/ngx_http_proxy_module.c --- a/src/http/modules/ngx_http_proxy_module.c +++ b/src/http/modules/ngx_http_proxy_module.c @@ -125,6 +125,9 @@ static ngx_int_t ngx_http_proxy_eval(ngx static ngx_int_t ngx_http_proxy_create_key(ngx_http_request_t *r); #endif static ngx_int_t ngx_http_proxy_create_request(ngx_http_request_t *r); +#if (NGX_HTTP_V2) +static ngx_int_t ngx_http_proxy_create_v2_request(ngx_http_request_t *r); +#endif static ngx_int_t ngx_http_proxy_reinit_request(ngx_http_request_t *r); static ngx_int_t ngx_http_proxy_body_output_filter(void *data, ngx_chain_t *in); static ngx_int_t ngx_http_proxy_process_status_line(ngx_http_request_t *r); @@ -149,6 +152,8 @@ static ngx_int_t ngx_http_proxy_port_var static ngx_int_t ngx_http_proxy_add_x_forwarded_for_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_proxy_internal_connection_variable( + ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_proxy_internal_body_length_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); @@ -245,6 +250,9 @@ static ngx_conf_bitmask_t ngx_http_prox static ngx_conf_enum_t ngx_http_proxy_http_version[] = { { ngx_string("1.0"), NGX_HTTP_VERSION_10 }, { ngx_string("1.1"), NGX_HTTP_VERSION_11 }, +#if (NGX_HTTP_V2) + { ngx_string("2.0"), NGX_HTTP_VERSION_20 }, +#endif { ngx_null_string, 0 } }; @@ -765,7 +773,7 @@ static char ngx_http_proxy_version_11[] static ngx_keyval_t ngx_http_proxy_headers[] = { { ngx_string("Host"), ngx_string("$proxy_host") }, - { ngx_string("Connection"), ngx_string("close") }, + { ngx_string("Connection"), ngx_string("$proxy_internal_connection") }, { ngx_string("Content-Length"), ngx_string("$proxy_internal_body_length") }, { ngx_string("Transfer-Encoding"), ngx_string("$proxy_internal_chunked") }, { ngx_string("TE"), ngx_string("") }, @@ -793,7 +801,7 @@ static ngx_str_t ngx_http_proxy_hide_he static ngx_keyval_t ngx_http_proxy_cache_headers[] = { { ngx_string("Host"), ngx_string("$proxy_host") }, - { ngx_string("Connection"), ngx_string("close") }, + { ngx_string("Connection"), ngx_string("$proxy_internal_connection") }, { ngx_string("Content-Length"), ngx_string("$proxy_internal_body_length") }, { ngx_string("Transfer-Encoding"), ngx_string("$proxy_internal_chunked") }, { ngx_string("TE"), ngx_string("") }, @@ -828,6 +836,10 @@ static ngx_http_variable_t ngx_http_pro { ngx_string("proxy_add_via"), NULL, NULL, 0, NGX_HTTP_VAR_NOHASH, 0 }, #endif + { ngx_string("proxy_internal_connection"), NULL, + ngx_http_proxy_internal_connection_variable, 0, + NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 }, + { ngx_string("proxy_internal_body_length"), NULL, ngx_http_proxy_internal_body_length_variable, 0, NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 }, @@ -902,6 +914,18 @@ ngx_http_proxy_handler(ngx_http_request_ u->finalize_request = ngx_http_proxy_finalize_request; r->state = 0; +#if (NGX_HTTP_V2) + + if (plcf->http_version == NGX_HTTP_VERSION_20) { + u->http2 = 1; + + u->create_request = ngx_http_proxy_create_v2_request; + u->output.output_filter = ngx_http_v2_upstream_output_filter; + u->output.filter_ctx = r; + } + +#endif + if (plcf->redirects) { u->rewrite_redirect = ngx_http_proxy_rewrite_redirect; } @@ -929,7 +953,7 @@ ngx_http_proxy_handler(ngx_http_request_ if (!plcf->upstream.request_buffering && plcf->body_values == NULL && plcf->upstream.pass_request_body && (!r->headers_in.chunked - || plcf->http_version == NGX_HTTP_VERSION_11)) + || plcf->http_version >= NGX_HTTP_VERSION_11)) { r->request_body_no_buffering = 1; } @@ -1521,6 +1545,509 @@ ngx_http_proxy_create_request(ngx_http_r } +#if (NGX_HTTP_V2) + +static ngx_int_t +ngx_http_proxy_create_v2_request(ngx_http_request_t *r) +{ + size_t len, uri_len, loc_len, body_len; + size_t key_len, val_len, tmp_len; + u_char *p, *code_tmp, *huff_tmp; + uintptr_t escape; + ngx_buf_t *b; + ngx_str_t method; + ngx_uint_t i, unparsed_uri; + ngx_chain_t *cl, *body; + ngx_list_part_t *part; + ngx_table_elt_t *header; + ngx_http_upstream_t *u; + ngx_http_proxy_ctx_t *ctx; + ngx_http_script_code_pt code; + ngx_http_proxy_headers_t *headers; + ngx_http_script_engine_t e, le; + ngx_http_proxy_loc_conf_t *plcf; + ngx_http_script_len_code_pt lcode; + + u = r->upstream; + + plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module); + +#if (NGX_HTTP_CACHE) + headers = u->cacheable ? &plcf->headers_cache : &plcf->headers; +#else + headers = &plcf->headers; +#endif + + if (u->method.len) { + /* HEAD was changed to GET to cache response */ + method = u->method; + + } else if (plcf->method) { + if (ngx_http_complex_value(r, plcf->method, &method) != NGX_OK) { + return NGX_ERROR; + } + + } else { + method = r->method_name; + } + + tmp_len = 0; + + if (method.len == 3 + && ngx_strncasecmp(method.data, (u_char *) "GET", 3) == 0) + { + len = 1; + + } else if (method.len == 4 + && ngx_strncasecmp(method.data, (u_char *) "POST", 4) == 0) + { + len = 1; + + } else { + len = 1 + NGX_HTTP_V2_INT_OCTETS + method.len; + tmp_len = method.len; + } + + escape = 0; + loc_len = 0; + unparsed_uri = 0; + + ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module); + + if (plcf->proxy_lengths && ctx->vars.uri.len) { + uri_len = ctx->vars.uri.len; + + } else if (ctx->vars.uri.len == 0 && r->valid_unparsed_uri && r == r->main) + { + unparsed_uri = 1; + uri_len = r->unparsed_uri.len; + + } else { + loc_len = (r->valid_location && ctx->vars.uri.len) + ? plcf->location.len : 0; + + if (r->quoted_uri || r->space_in_uri || r->internal) { + escape = 2 * ngx_escape_uri(NULL, r->uri.data + loc_len, + r->uri.len - loc_len, NGX_ESCAPE_URI); + } + + uri_len = ctx->vars.uri.len + r->uri.len - loc_len + escape + + sizeof("?") - 1 + r->args.len; + } + + if (uri_len == 0) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "zero length URI to proxy"); + return NGX_ERROR; + } + + len += 1 + NGX_HTTP_V2_INT_OCTETS + uri_len; + + if (uri_len > tmp_len) { + tmp_len = uri_len; + } + + + ngx_memzero(&le, sizeof(ngx_http_script_engine_t)); + + ngx_http_script_flush_no_cacheable_variables(r, plcf->body_flushes); + ngx_http_script_flush_no_cacheable_variables(r, headers->flushes); + + body_len = 0; + + if (plcf->body_lengths) { + le.ip = plcf->body_lengths->elts; + le.request = r; + le.flushed = 1; + + while (*(uintptr_t *) le.ip) { + lcode = *(ngx_http_script_len_code_pt *) le.ip; + body_len += lcode(&le); + } + + ctx->internal_body_length = body_len; + + } else if (r->headers_in.chunked && r->reading_body) { + ctx->internal_body_length = -1; + ctx->internal_chunked = 1; + + } else { + ctx->internal_body_length = r->headers_in.content_length_n; + } + + le.ip = headers->lengths->elts; + le.request = r; + le.flushed = 1; + + while (*(uintptr_t *) le.ip) { + + lcode = *(ngx_http_script_len_code_pt *) le.ip; + key_len = lcode(&le); + + for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) { + lcode = *(ngx_http_script_len_code_pt *) le.ip; + } + le.ip += sizeof(uintptr_t); + + if (val_len == 0) { + continue; + } + + len += 1 + NGX_HTTP_V2_INT_OCTETS + key_len + + NGX_HTTP_V2_INT_OCTETS + val_len; + + if (key_len > tmp_len) { + tmp_len = key_len; + } + + if (val_len > tmp_len) { + tmp_len = val_len; + } + } + + + if (plcf->upstream.pass_request_headers) { + part = &r->headers_in.headers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (ngx_hash_find(&headers->hash, header[i].hash, + header[i].lowcase_key, header[i].key.len)) + { + continue; + } + + if (header[i].key.len > NGX_HTTP_V2_MAX_FIELD) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "too long request header name: \"%V\"", + &header[i].key); + return NGX_ERROR; + } + + if (header[i].value.len > NGX_HTTP_V2_MAX_FIELD) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "too long request header value: \"%V: %V\"", + &header[i].key, &header[i].value); + return NGX_ERROR; + } + + len += 1 + NGX_HTTP_V2_INT_OCTETS + header[i].key.len + + NGX_HTTP_V2_INT_OCTETS + header[i].value.len; + + if (header[i].key.len > tmp_len) { + tmp_len = header[i].key.len; + } + + if (header[i].value.len > tmp_len) { + tmp_len = header[i].value.len; + } + } + } + + code_tmp = ngx_pnalloc(r->pool, tmp_len); + if (code_tmp == NULL) { + return NGX_ERROR; + } + + huff_tmp = ngx_palloc(r->pool, tmp_len); + if (huff_tmp == NULL) { + return NGX_ERROR; + } + + b = ngx_create_temp_buf(r->pool, len); + if (b == NULL) { + return NGX_ERROR; + } + + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { + return NGX_ERROR; + } + + cl->buf = b; + cl->next = NULL; + + /* :method header */ + + if (method.len == 3 + && ngx_strncasecmp(method.data, (u_char *) "GET", 3) == 0) + { + *b->last++ = ngx_http_v2_indexed(NGX_HTTP_V2_METHOD_GET_INDEX); + + } else if (method.len == 4 + && ngx_strncasecmp(method.data, (u_char *) "POST", 4) == 0) + { + *b->last++ = ngx_http_v2_indexed(NGX_HTTP_V2_METHOD_POST_INDEX); + + } else { + *b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_METHOD_INDEX); + b->last = ngx_http_v2_write_value(b->last, method.data, method.len, + huff_tmp); + } + + /* :scheme header */ + + if (u->schema.len == 8 + && ngx_strncasecmp(u->schema.data, (u_char *) "https://", 8) == 0) + { + *b->last++ = ngx_http_v2_indexed(NGX_HTTP_V2_SCHEME_HTTPS_INDEX); + + } else { + *b->last++ = ngx_http_v2_indexed(NGX_HTTP_V2_SCHEME_HTTP_INDEX); + } + + /* :authority header */ + + le.ip = headers->lengths->elts; + + lcode = *(ngx_http_script_len_code_pt *) le.ip; + (void) lcode(&le); + + for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) { + lcode = *(ngx_http_script_len_code_pt *) le.ip; + } + le.ip += sizeof(uintptr_t); + + if (val_len == 0) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "missing or empty \"Host\" header"); + return NGX_ERROR; + } + + ngx_memzero(&e, sizeof(ngx_http_script_engine_t)); + + e.ip = headers->values->elts; + e.request = r; + e.flushed = 1; + e.skip = 1; + + code = *(ngx_http_script_code_pt *) e.ip; + code((ngx_http_script_engine_t *) &e); + + e.skip = 0; + e.pos = code_tmp; + + while (*(uintptr_t *) e.ip) { + code = *(ngx_http_script_code_pt *) e.ip; + code((ngx_http_script_engine_t *) &e); + } + e.ip += sizeof(uintptr_t); + + *b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_AUTHORITY_INDEX); + b->last = ngx_http_v2_write_value(b->last, code_tmp, e.pos - code_tmp, + huff_tmp); + + /* :path header */ + + if (plcf->proxy_lengths && ctx->vars.uri.len) { + u->uri = ctx->vars.uri; + + } else if (unparsed_uri) { + u->uri = r->unparsed_uri; + + } else { + p = ngx_pnalloc(r->pool, uri_len); + if (p == NULL) { + return NGX_ERROR; + } + + u->uri.data = p; + + if (r->valid_location) { + p = ngx_copy(p, ctx->vars.uri.data, ctx->vars.uri.len); + } + + if (escape) { + ngx_escape_uri(p, r->uri.data + loc_len, r->uri.len - loc_len, + NGX_ESCAPE_URI); + p += r->uri.len - loc_len + escape; + + } else { + p = ngx_copy(p, r->uri.data + loc_len, r->uri.len - loc_len); + } + + if (r->args.len > 0) { + *p++ = '?'; + p = ngx_copy(p, r->args.data, r->args.len); + } + + u->uri.len = p - u->uri.data; + } + + if (uri_len == 1) { + *b->last++ = ngx_http_v2_indexed(NGX_HTTP_V2_PATH_ROOT_INDEX); + + } else { + *b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_PATH_INDEX); + b->last = ngx_http_v2_write_value(b->last, u->uri.data, u->uri.len, + huff_tmp); + } + + + while (*(uintptr_t *) le.ip) { + + lcode = *(ngx_http_script_len_code_pt *) le.ip; + (void) lcode(&le); + + for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) { + lcode = *(ngx_http_script_len_code_pt *) le.ip; + } + le.ip += sizeof(uintptr_t); + + if (val_len == 0) { + e.skip = 1; + + while (*(uintptr_t *) e.ip) { + code = *(ngx_http_script_code_pt *) e.ip; + code((ngx_http_script_engine_t *) &e); + } + e.ip += sizeof(uintptr_t); + + e.skip = 0; + + continue; + } + + e.pos = code_tmp; + + code = *(ngx_http_script_code_pt *) e.ip; + code((ngx_http_script_engine_t *) &e); + + *b->last++ = '\0'; + b->last = ngx_http_v2_write_name(b->last, code_tmp, e.pos - code_tmp, + huff_tmp); + + e.pos = code_tmp; + + while (*(uintptr_t *) e.ip) { + code = *(ngx_http_script_code_pt *) e.ip; + code((ngx_http_script_engine_t *) &e); + } + e.ip += sizeof(uintptr_t); + + b->last = ngx_http_v2_write_value(b->last, code_tmp, e.pos - code_tmp, + huff_tmp); + } + + + if (plcf->upstream.pass_request_headers) { + part = &r->headers_in.headers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (ngx_hash_find(&headers->hash, header[i].hash, + header[i].lowcase_key, header[i].key.len)) + { + continue; + } + + *b->last++ = '\0'; + + b->last = ngx_http_v2_write_name(b->last, header[i].key.data, + header[i].key.len, huff_tmp); + + b->last = ngx_http_v2_write_value(b->last, header[i].value.data, + header[i].value.len, huff_tmp); + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "proxy http2 header: \"%*s: %V\"", + header[i].key.len, header[i].lowcase_key, + &header[i].value); + } + } + + + if (plcf->body_values && body_len) { + b = ngx_create_temp_buf(r->pool, body_len); + if (b == NULL) { + return NGX_ERROR; + } + + e.ip = plcf->body_values->elts; + e.pos = b->last; + e.skip = 0; + + while (*(uintptr_t *) e.ip) { + code = *(ngx_http_script_code_pt *) e.ip; + code((ngx_http_script_engine_t *) &e); + } + + b->last = e.pos; + + cl->next = ngx_alloc_chain_link(r->pool); + if (cl->next == NULL) { + return NGX_ERROR; + } + + cl->next->buf = b; + cl->next->next = NULL; + } + + + if (r->request_body_no_buffering) { + u->request_bufs = cl; + b->flush = 1; + + } else if (plcf->body_values == NULL && plcf->upstream.pass_request_body) { + + body = u->request_bufs; + u->request_bufs = cl; + + while (body) { + b = ngx_alloc_buf(r->pool); + if (b == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(b, body->buf, sizeof(ngx_buf_t)); + + cl->next = ngx_alloc_chain_link(r->pool); + if (cl->next == NULL) { + return NGX_ERROR; + } + + cl = cl->next; + cl->buf = b; + + body = body->next; + } + + cl->next = NULL; + b->last_buf = 1; + + } else { + u->request_bufs = cl; + b->last_buf = 1; + } + + return NGX_OK; +} + +#endif + + static ngx_int_t ngx_http_proxy_reinit_request(ngx_http_request_t *r) { @@ -1966,6 +2493,14 @@ ngx_http_proxy_input_filter_init(void *d u->headers_in.status_n, ctx->head, u->headers_in.chunked, u->headers_in.content_length_n); +#if (NGX_HTTP_V2) + + if (u->stream) { + return NGX_OK; + } + +#endif + /* as per RFC2616, 4.4 Message Length */ if (u->headers_in.status_n == NGX_HTTP_NO_CONTENT @@ -2480,6 +3015,34 @@ ngx_http_proxy_add_x_forwarded_for_varia static ngx_int_t +ngx_http_proxy_internal_connection_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ +#if (NGX_HTTP_V2) + + ngx_http_proxy_loc_conf_t *plcf; + + plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module); + + if (plcf->http_version == NGX_HTTP_VERSION_20) { + v->not_found = 1; + return NGX_OK; + } + +#endif + + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + v->data = (u_char *) "close"; + v->len = sizeof("close") - 1; + + return NGX_OK; +} + + +static ngx_int_t ngx_http_proxy_internal_body_length_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { @@ -2512,7 +3075,20 @@ static ngx_int_t ngx_http_proxy_internal_chunked_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { - ngx_http_proxy_ctx_t *ctx; + ngx_http_proxy_ctx_t *ctx; + +#if (NGX_HTTP_V2) + + ngx_http_proxy_loc_conf_t *plcf; + + plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module); + + if (plcf->http_version == NGX_HTTP_VERSION_20) { + v->not_found = 1; + return NGX_OK; + } + +#endif ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module); @@ -3407,6 +3983,18 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t #if (NGX_HTTP_CACHE) if (conf->upstream.cache) { + +#if (NGX_HTTP_V2) + + if (conf->http_version == NGX_HTTP_VERSION_20) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"proxy_cache\" doesn't work with " + "\"proxy_http_version 2.0\""); + return NGX_CONF_ERROR; + } + +#endif + rc = ngx_http_proxy_init_headers(cf, conf, &conf->headers_cache, ngx_http_proxy_cache_headers); if (rc != NGX_OK) { @@ -4361,6 +4949,15 @@ ngx_http_proxy_set_ssl(ngx_conf_t *cf, n case NGX_HTTP_VERSION_11: ngx_str_set(&alpn, NGX_HTTP_11_ALPN_ADVERTISE); break; + +#if (NGX_HTTP_V2) + + case NGX_HTTP_VERSION_20: + ngx_str_set(&alpn, NGX_HTTP_V2_ALPN_ADVERTISE); + break; + +#endif + } if (ngx_ssl_alpn_protos(cf, plcf->upstream.ssl, &alpn) != NGX_OK) { diff -r 0637acdb51e2 -r 7eb807b056da src/http/v2/ngx_http_v2.h --- a/src/http/v2/ngx_http_v2.h +++ b/src/http/v2/ngx_http_v2.h @@ -13,6 +13,28 @@ #include +#define ngx_http_v2_indexed(i) (128 + (i)) +#define ngx_http_v2_inc_indexed(i) (64 + (i)) + + +// :authority +#define NGX_HTTP_V2_AUTHORITY_INDEX 1 + +// :method +#define NGX_HTTP_V2_METHOD_INDEX 2 +#define NGX_HTTP_V2_METHOD_GET_INDEX 2 +#define NGX_HTTP_V2_METHOD_POST_INDEX 3 + +// :path +#define NGX_HTTP_V2_PATH_INDEX 4 +#define NGX_HTTP_V2_PATH_ROOT_INDEX 4 + +// :scheme +#define NGX_HTTP_V2_SCHEME_INDEX 6 +#define NGX_HTTP_V2_SCHEME_HTTP_INDEX 6 +#define NGX_HTTP_V2_SCHEME_HTTPS_INDEX 7 + + #define NGX_HTTP_V2_ALPN_ADVERTISE "\x02h2" #define NGX_HTTP_V2_NPN_ADVERTISE NGX_HTTP_V2_ALPN_ADVERTISE @@ -325,6 +347,9 @@ void ngx_http_v2_finalize_connection(ngx ngx_uint_t status); +u_char *ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len, + u_char *tmp, ngx_uint_t lower); + ngx_http_v2_out_frame_t *ngx_http_v2_create_headers_frame( ngx_http_v2_stream_t *stream, u_char *pos, u_char *end, ngx_uint_t fin); @@ -415,4 +440,11 @@ size_t ngx_http_v2_huff_encode(u_char *s #define ngx_http_v2_write_sid ngx_http_v2_write_uint32 + +#define ngx_http_v2_write_name(dst, src, len, tmp) \ + ngx_http_v2_string_encode(dst, src, len, tmp, 1) +#define ngx_http_v2_write_value(dst, src, len, tmp) \ + ngx_http_v2_string_encode(dst, src, len, tmp, 0) + + #endif /* _NGX_HTTP_V2_H_INCLUDED_ */ diff -r 0637acdb51e2 -r 7eb807b056da src/http/v2/ngx_http_v2_filter_module.c --- a/src/http/v2/ngx_http_v2_filter_module.c +++ b/src/http/v2/ngx_http_v2_filter_module.c @@ -22,14 +22,6 @@ #define ngx_http_v2_literal_size(h) \ (ngx_http_v2_integer_octets(sizeof(h) - 1) + sizeof(h) - 1) -#define ngx_http_v2_indexed(i) (128 + (i)) -#define ngx_http_v2_inc_indexed(i) (64 + (i)) - -#define ngx_http_v2_write_name(dst, src, len, tmp) \ - ngx_http_v2_string_encode(dst, src, len, tmp, 1) -#define ngx_http_v2_write_value(dst, src, len, tmp) \ - ngx_http_v2_string_encode(dst, src, len, tmp, 0) - #define NGX_HTTP_V2_ENCODE_RAW 0 #define NGX_HTTP_V2_ENCODE_HUFF 0x80 @@ -53,8 +45,6 @@ #define NGX_HTTP_V2_NO_TRAILERS (ngx_http_v2_out_frame_t *) -1 -static u_char *ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len, - u_char *tmp, ngx_uint_t lower); static u_char *ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix, ngx_uint_t value); static ngx_http_v2_out_frame_t *ngx_http_v2_create_trailers_frame( @@ -740,7 +730,7 @@ ngx_http_v2_create_trailers_frame(ngx_ht } -static u_char * +u_char * ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len, u_char *tmp, ngx_uint_t lower) { From piotrsikora at google.com Thu Jun 22 20:33:18 2017 From: piotrsikora at google.com (Piotr Sikora) Date: Thu, 22 Jun 2017 13:33:18 -0700 Subject: [PATCH 14 of 14] Cache: add HTTP/2 support In-Reply-To: <5f5d70428655db0889a2.1498163585@piotrsikora.sfo.corp.google.com> References: <5f5d70428655db0889a2.1498163585@piotrsikora.sfo.corp.google.com> Message-ID: <432abcf285745ec6b6ee.1498163598@piotrsikora.sfo.corp.google.com> # HG changeset patch # User Piotr Sikora # Date 1491954701 25200 # Tue Apr 11 16:51:41 2017 -0700 # Node ID 432abcf285745ec6b6ee14d6487a4a42b51b7bce # Parent cde1f42da7b26b7d2b788f916685e736b919138e Cache: add HTTP/2 support. Signed-off-by: Piotr Sikora diff -r cde1f42da7b2 -r 432abcf28574 src/http/modules/ngx_http_proxy_module.c --- a/src/http/modules/ngx_http_proxy_module.c +++ b/src/http/modules/ngx_http_proxy_module.c @@ -132,6 +132,14 @@ static ngx_int_t ngx_http_proxy_reinit_r static ngx_int_t ngx_http_proxy_body_output_filter(void *data, ngx_chain_t *in); static ngx_int_t ngx_http_proxy_process_status_line(ngx_http_request_t *r); static ngx_int_t ngx_http_proxy_process_header(ngx_http_request_t *r); +static ngx_int_t ngx_http_proxy_process_trailer(ngx_http_request_t *r, + ngx_buf_t *buf); +#if (NGX_HTTP_V2 && NGX_HTTP_CACHE) +static ngx_int_t ngx_http_proxy_serialize_headers(ngx_http_request_t *r, + ngx_http_upstream_t *u); +static ngx_chain_t *ngx_http_proxy_serialize_trailers(ngx_http_request_t *r, + ngx_http_upstream_t *u); +#endif static ngx_int_t ngx_http_proxy_input_filter_init(void *data); static ngx_int_t ngx_http_proxy_copy_filter(ngx_event_pipe_t *p, ngx_buf_t *buf); @@ -917,6 +925,7 @@ ngx_http_proxy_handler(ngx_http_request_ u->create_request = ngx_http_proxy_create_request; u->reinit_request = ngx_http_proxy_reinit_request; u->process_header = ngx_http_proxy_process_status_line; + u->process_trailer = ngx_http_proxy_process_trailer; u->abort_request = ngx_http_proxy_abort_request; u->finalize_request = ngx_http_proxy_finalize_request; r->state = 0; @@ -929,6 +938,14 @@ ngx_http_proxy_handler(ngx_http_request_ u->create_request = ngx_http_proxy_create_v2_request; u->output.output_filter = ngx_http_v2_upstream_output_filter; u->output.filter_ctx = r; + +#if (NGX_HTTP_CACHE) + + u->serialize_headers = ngx_http_proxy_serialize_headers; + u->serialize_trailers = ngx_http_proxy_serialize_trailers; + +#endif + } #endif @@ -2482,6 +2499,227 @@ ngx_http_proxy_process_header(ngx_http_r static ngx_int_t +ngx_http_proxy_process_trailer(ngx_http_request_t *r, ngx_buf_t *buf) +{ + ngx_int_t rc; + ngx_table_elt_t *h; + + for ( ;; ) { + + rc = ngx_http_parse_header_line(r, buf, 1); + + if (rc == NGX_OK) { + h = ngx_list_push(&r->upstream->headers_in.trailers); + if (h == NULL) { + return NGX_ERROR; + } + + h->hash = r->header_hash; + + h->key.len = r->header_name_end - r->header_name_start; + h->value.len = r->header_end - r->header_start; + + h->key.data = ngx_pnalloc(r->pool, + h->key.len + 1 + h->value.len + 1 + h->key.len); + if (h->key.data == NULL) { + return NGX_ERROR; + } + + h->value.data = h->key.data + h->key.len + 1; + h->lowcase_key = h->key.data + h->key.len + 1 + h->value.len + 1; + + ngx_memcpy(h->key.data, r->header_name_start, h->key.len); + h->key.data[h->key.len] = '\0'; + ngx_memcpy(h->value.data, r->header_start, h->value.len); + h->value.data[h->value.len] = '\0'; + + if (h->key.len == r->lowcase_index) { + ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len); + + } else { + ngx_strlow(h->lowcase_key, h->key.data, h->key.len); + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http proxy trailer: \"%V: %V\"", + &h->key, &h->value); + + continue; + } + + if (rc == NGX_HTTP_PARSE_HEADER_DONE) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http proxy trailer done"); + + return NGX_OK; + } + + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "upstream sent invalid trailer"); + + return NGX_HTTP_UPSTREAM_INVALID_HEADER; + } +} + + +#if (NGX_HTTP_V2 && NGX_HTTP_CACHE) + +static ngx_int_t +ngx_http_proxy_serialize_headers(ngx_http_request_t *r, ngx_http_upstream_t *u) +{ + size_t len; + ngx_buf_t *b; + ngx_uint_t i; + ngx_list_part_t *part; + ngx_table_elt_t *header; + + len = sizeof("HTTP/1.1 " CRLF) - 1 + u->headers_in.status_line.len + + sizeof(CRLF) - 1; + + part = &u->headers_in.headers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + len += header[i].key.len + sizeof(": ") - 1 + + header[i].value.len + sizeof(CRLF) - 1; + } + + b = &u->buffer; + + if (len > (size_t) (b->end - b->last)) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "upstream sent headers too big to serialize for cache, " + "need:%uz available:%uz", len, b->end - b->last); + + return NGX_ERROR; + } + + b->last = ngx_copy(b->last, "HTTP/1.1 ", sizeof("HTTP/1.1 ") - 1); + b->last = ngx_copy(b->last, u->headers_in.status_line.data, + u->headers_in.status_line.len); + *b->last++ = CR; *b->last++ = LF; + + part = &u->headers_in.headers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + b->last = ngx_copy(b->last, header[i].key.data, header[i].key.len); + *b->last++ = ':'; *b->last++ = ' '; + + b->last = ngx_copy(b->last, header[i].value.data, header[i].value.len); + *b->last++ = CR; *b->last++ = LF; + } + + *b->last++ = CR; *b->last++ = LF; + b->pos = b->last; + + return NGX_OK; +} + + +static ngx_chain_t * +ngx_http_proxy_serialize_trailers(ngx_http_request_t *r, ngx_http_upstream_t *u) +{ + size_t len; + ngx_buf_t *b; + ngx_uint_t i; + ngx_chain_t *cl; + ngx_list_part_t *part; + ngx_table_elt_t *header; + + len = 0; + + part = &u->headers_in.trailers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + len += header[i].key.len + sizeof(": ") - 1 + + header[i].value.len + sizeof(CRLF) - 1; + } + + if (len == 0) { + return NULL; + } + + len += sizeof(CRLF) - 1; + + b = ngx_create_temp_buf(r->pool, len); + if (b == NULL) { + return NGX_CHAIN_ERROR; + } + + *b->last++ = CR; *b->last++ = LF; + + part = &u->headers_in.trailers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + b->last = ngx_copy(b->last, header[i].key.data, header[i].key.len); + *b->last++ = ':'; *b->last++ = ' '; + + b->last = ngx_copy(b->last, header[i].value.data, header[i].value.len); + *b->last++ = CR; *b->last++ = LF; + } + + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { + return NGX_CHAIN_ERROR; + } + + cl->buf = b; + cl->next = NULL; + + return cl; +} + +#endif + + +static ngx_int_t ngx_http_proxy_input_filter_init(void *data) { ngx_http_request_t *r = data; @@ -3995,18 +4233,6 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t #if (NGX_HTTP_CACHE) if (conf->upstream.cache) { - -#if (NGX_HTTP_V2) - - if (conf->http_version == NGX_HTTP_VERSION_20) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "\"proxy_cache\" doesn't work with " - "\"proxy_http_version 2.0\""); - return NGX_CONF_ERROR; - } - -#endif - rc = ngx_http_proxy_init_headers(cf, conf, &conf->headers_cache, ngx_http_proxy_cache_headers); if (rc != NGX_OK) { @@ -4030,26 +4256,13 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t #endif } - if (conf->upstream.pass_trailers) { - - if (conf->http_version != NGX_HTTP_VERSION_20) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "\"proxy_pass_trailers\" requires " - "\"proxy_http_version 2.0\""); - return NGX_CONF_ERROR; - } - -#if (NGX_HTTP_CACHE) - - if (conf->upstream.cache) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "\"proxy_pass_trailers\" doesn't work with " - "\"proxy_cache\""); - return NGX_CONF_ERROR; - } - -#endif - + if (conf->upstream.pass_trailers + && conf->http_version != NGX_HTTP_VERSION_20) + { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"proxy_pass_trailers\" requires " + "\"proxy_http_version 2.0\""); + return NGX_CONF_ERROR; } return NGX_CONF_OK; diff -r cde1f42da7b2 -r 432abcf28574 src/http/ngx_http_cache.h --- a/src/http/ngx_http_cache.h +++ b/src/http/ngx_http_cache.h @@ -27,7 +27,7 @@ #define NGX_HTTP_CACHE_ETAG_LEN 128 #define NGX_HTTP_CACHE_VARY_LEN 128 -#define NGX_HTTP_CACHE_VERSION 5 +#define NGX_HTTP_CACHE_VERSION 6 typedef struct { @@ -82,6 +82,7 @@ struct ngx_http_cache_s { size_t header_start; size_t body_start; + off_t body_length; off_t length; off_t fs_size; @@ -130,6 +131,7 @@ typedef struct { time_t error_sec; time_t last_modified; time_t date; + off_t body_length; uint32_t crc32; u_short valid_msec; u_short header_start; @@ -192,6 +194,8 @@ ngx_int_t ngx_http_file_cache_set_header void ngx_http_file_cache_update(ngx_http_request_t *r, ngx_temp_file_t *tf); void ngx_http_file_cache_update_header(ngx_http_request_t *r); ngx_int_t ngx_http_cache_send(ngx_http_request_t *); +ngx_buf_t *ngx_http_cache_get_trailers(ngx_http_request_t *r, + ngx_http_cache_t *c); void ngx_http_file_cache_free(ngx_http_cache_t *c, ngx_temp_file_t *tf); time_t ngx_http_file_cache_valid(ngx_array_t *cache_valid, ngx_uint_t status); diff -r cde1f42da7b2 -r 432abcf28574 src/http/ngx_http_file_cache.c --- a/src/http/ngx_http_file_cache.c +++ b/src/http/ngx_http_file_cache.c @@ -605,6 +605,7 @@ ngx_http_file_cache_read(ngx_http_reques c->error_sec = h->error_sec; c->last_modified = h->last_modified; c->date = h->date; + c->body_length = h->body_length; c->valid_msec = h->valid_msec; c->body_start = h->body_start; c->etag.len = h->etag_len; @@ -1260,6 +1261,7 @@ ngx_http_file_cache_set_header(ngx_http_ h->error_sec = c->error_sec; h->last_modified = c->last_modified; h->date = c->date; + h->body_length = c->body_length; h->crc32 = c->crc32; h->valid_msec = (u_short) c->valid_msec; h->header_start = (u_short) c->header_start; @@ -1500,6 +1502,7 @@ ngx_http_file_cache_update_header(ngx_ht if (h.version != NGX_HTTP_CACHE_VERSION || h.last_modified != c->last_modified + || (h.body_length != -1 && h.body_length != c->body_length) || h.crc32 != c->crc32 || (size_t) h.header_start != c->header_start || (size_t) h.body_start != c->body_start) @@ -1523,6 +1526,7 @@ ngx_http_file_cache_update_header(ngx_ht h.error_sec = c->error_sec; h.last_modified = c->last_modified; h.date = c->date; + h.body_length = c->body_length; h.crc32 = c->crc32; h.valid_msec = (u_short) c->valid_msec; h.header_start = (u_short) c->header_start; @@ -1561,6 +1565,7 @@ done: ngx_int_t ngx_http_cache_send(ngx_http_request_t *r) { + off_t body_len; ngx_int_t rc; ngx_buf_t *b; ngx_chain_t out; @@ -1571,7 +1576,14 @@ ngx_http_cache_send(ngx_http_request_t * ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http file cache send: %s", c->file.name.data); - if (r != r->main && c->length - c->body_start == 0) { + if (c->body_length != -1) { + body_len = c->body_length; + + } else { + body_len = c->length - c->body_start; + } + + if (r != r->main && body_len == 0) { return ngx_http_send_header(r); } @@ -1594,9 +1606,9 @@ ngx_http_cache_send(ngx_http_request_t * } b->file_pos = c->body_start; - b->file_last = c->length; - - b->in_file = (c->length - c->body_start) ? 1: 0; + b->file_last = c->body_start + body_len; + + b->in_file = body_len ? 1 : 0; b->last_buf = (r == r->main) ? 1: 0; b->last_in_chain = 1; @@ -1611,6 +1623,42 @@ ngx_http_cache_send(ngx_http_request_t * } +ngx_buf_t * +ngx_http_cache_get_trailers(ngx_http_request_t *r, ngx_http_cache_t *c) +{ + off_t offset; + size_t len; + ssize_t n; + ngx_buf_t *b; + + offset = c->body_start + c->body_length + sizeof(CRLF) - 1; + len = c->length - offset; + + b = ngx_create_temp_buf(r->pool, len + sizeof(CRLF) - 1); + if (b == NULL) { + return NULL; + } + + n = ngx_read_file(&c->file, b->pos, len, offset); + + if (n == NGX_ERROR) { + return NULL; + } + + if ((size_t) n != len) { + ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0, + ngx_read_file_n " read only %z of %z from \"%s\"", + n, len, c->file.name.data); + return NULL; + } + + b->last += n; + *b->last++ = CR; *b->last++ = LF; + + return b; +} + + void ngx_http_file_cache_free(ngx_http_cache_t *c, ngx_temp_file_t *tf) { diff -r cde1f42da7b2 -r 432abcf28574 src/http/ngx_http_upstream.c --- a/src/http/ngx_http_upstream.c +++ b/src/http/ngx_http_upstream.c @@ -58,6 +58,10 @@ static ngx_int_t ngx_http_upstream_proce ngx_http_upstream_t *u); static ngx_int_t ngx_http_upstream_process_trailers(ngx_http_request_t *r, ngx_http_upstream_t *u); +#if (NGX_HTTP_V2 && NGX_HTTP_CACHE) +static ngx_int_t ngx_http_upstream_serialize_trailers(ngx_http_request_t *r, + ngx_http_upstream_t *u); +#endif static void ngx_http_upstream_process_body_in_memory(ngx_http_request_t *r, ngx_http_upstream_t *u); static void ngx_http_upstream_send_response(ngx_http_request_t *r, @@ -1035,6 +1039,7 @@ static ngx_int_t ngx_http_upstream_cache_send(ngx_http_request_t *r, ngx_http_upstream_t *u) { ngx_int_t rc; + ngx_buf_t *trailers; ngx_http_cache_t *c; r->cached = 1; @@ -1076,6 +1081,24 @@ ngx_http_upstream_cache_send(ngx_http_re return NGX_DONE; } + if (c->body_length != -1 && u->conf->pass_trailers) { + + trailers = ngx_http_cache_get_trailers(r, c); + if (trailers == NULL) { + return NGX_ERROR; + } + + if (u->process_trailer(r, trailers) != NGX_OK) { + return NGX_ERROR; + } + + r->expect_trailers = 1; + + if (ngx_http_upstream_process_trailers(r, u) != NGX_OK) { + return NGX_ERROR; + } + } + return ngx_http_cache_send(r); } @@ -2955,6 +2978,49 @@ ngx_http_upstream_process_trailers(ngx_h } +#if (NGX_HTTP_V2 && NGX_HTTP_CACHE) + +static ngx_int_t +ngx_http_upstream_serialize_trailers(ngx_http_request_t *r, + ngx_http_upstream_t *u) +{ + off_t body_len; + ngx_chain_t *cl; + + if (u->serialize_trailers == NULL) { + return NGX_OK; + } + + cl = u->serialize_trailers(r, u); + + if (cl == NGX_CHAIN_ERROR) { + return NGX_ERROR; + } + + if (cl == NULL) { + return NGX_OK; + } + + body_len = u->pipe->temp_file->offset - (off_t) r->cache->body_start; + + if (ngx_write_chain_to_temp_file(u->pipe->temp_file, cl) == NGX_ERROR) { + return NGX_ERROR; + } + + if (ngx_write_file(&u->pipe->temp_file->file, + (u_char *) &body_len, sizeof(off_t), + offsetof(ngx_http_file_cache_header_t, body_length)) + != sizeof(off_t)) + { + return NGX_ERROR; + } + + return NGX_OK; +} + +#endif + + static void ngx_http_upstream_process_body_in_memory(ngx_http_request_t *r, ngx_http_upstream_t *u) @@ -3201,9 +3267,16 @@ ngx_http_upstream_send_response(ngx_http } } + if (valid && u->serialize_headers) { + if (u->serialize_headers(r, u) != NGX_OK) { + valid = 0; + } + } + if (valid) { r->cache->date = now; r->cache->body_start = (u_short) (u->buffer.pos - u->buffer.start); + r->cache->body_length = -1; if (u->headers_in.status_n == NGX_HTTP_OK || u->headers_in.status_n == NGX_HTTP_PARTIAL_CONTENT) @@ -4107,6 +4180,13 @@ ngx_http_upstream_process_request(ngx_ht if (u->cacheable) { if (p->upstream_done) { + + if (ngx_http_upstream_serialize_trailers(r, u) != NGX_OK) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + ngx_http_file_cache_update(r, p->temp_file); } else if (p->upstream_eof) { @@ -4118,6 +4198,12 @@ ngx_http_upstream_process_request(ngx_ht || u->headers_in.content_length_n == tf->offset - (off_t) r->cache->body_start)) { + if (ngx_http_upstream_serialize_trailers(r, u) != NGX_OK) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + ngx_http_file_cache_update(r, tf); } else { diff -r cde1f42da7b2 -r 432abcf28574 src/http/ngx_http_upstream.h --- a/src/http/ngx_http_upstream.h +++ b/src/http/ngx_http_upstream.h @@ -359,6 +359,12 @@ struct ngx_http_upstream_s { ngx_int_t (*create_request)(ngx_http_request_t *r); ngx_int_t (*reinit_request)(ngx_http_request_t *r); ngx_int_t (*process_header)(ngx_http_request_t *r); + ngx_int_t (*process_trailer)(ngx_http_request_t *r, + ngx_buf_t *buf); + ngx_int_t (*serialize_headers)(ngx_http_request_t *r, + ngx_http_upstream_t *u); + ngx_chain_t *(*serialize_trailers)(ngx_http_request_t *r, + ngx_http_upstream_t *u); void (*abort_request)(ngx_http_request_t *r); void (*finalize_request)(ngx_http_request_t *r, ngx_int_t rc); From vlad at cloudflare.com Fri Jun 23 01:23:46 2017 From: vlad at cloudflare.com (Vlad Krasnov) Date: Thu, 22 Jun 2017 18:23:46 -0700 Subject: [PATCH] HTTP/2: add support for HPACK encoding Message-ID: <895cea03ac21fb18d2c2.1498181026@debian> # HG changeset patch # User Vlad Krasnov # Date 1498167669 25200 # Thu Jun 22 14:41:09 2017 -0700 # Node ID 895cea03ac21fb18d2c2ba32389cd67dc74ddbd0 # Parent a39bc74873faf9e5bea616561b43f6ecc55229f9 HTTP/2: add support for HPACK encoding Add support for full HPACK encoding as per RFC7541. This modification improves header compression ratio by 5-10% for the first response, and by 40-95% for consequential responses on the connection. The implementation is similar to the one used by Cloudflare. diff -r a39bc74873fa -r 895cea03ac21 auto/modules --- a/auto/modules Mon Jun 19 14:25:42 2017 +0300 +++ b/auto/modules Thu Jun 22 14:41:09 2017 -0700 @@ -436,6 +436,10 @@ . auto/module fi + if [ $HTTP_V2_HPACK_ENC = YES ]; then + have=NGX_HTTP_V2_HPACK_ENC . auto/have + fi + if :; then ngx_module_name=ngx_http_static_module ngx_module_incs= diff -r a39bc74873fa -r 895cea03ac21 auto/options --- a/auto/options Mon Jun 19 14:25:42 2017 +0300 +++ b/auto/options Thu Jun 22 14:41:09 2017 -0700 @@ -59,6 +59,7 @@ HTTP_GZIP=YES HTTP_SSL=NO HTTP_V2=NO +HTTP_V2_HPACK_ENC=NO HTTP_SSI=YES HTTP_POSTPONE=NO HTTP_REALIP=NO @@ -221,6 +222,7 @@ --with-http_ssl_module) HTTP_SSL=YES ;; --with-http_v2_module) HTTP_V2=YES ;; + --with-http_v2_hpack_enc) HTTP_V2_HPACK_ENC=YES ;; --with-http_realip_module) HTTP_REALIP=YES ;; --with-http_addition_module) HTTP_ADDITION=YES ;; --with-http_xslt_module) HTTP_XSLT=YES ;; @@ -430,6 +432,7 @@ --with-http_ssl_module enable ngx_http_ssl_module --with-http_v2_module enable ngx_http_v2_module + --with-http_v2_hpack_enc enable ngx_http_v2_hpack_enc --with-http_realip_module enable ngx_http_realip_module --with-http_addition_module enable ngx_http_addition_module --with-http_xslt_module enable ngx_http_xslt_module diff -r a39bc74873fa -r 895cea03ac21 src/core/ngx_murmurhash.c --- a/src/core/ngx_murmurhash.c Mon Jun 19 14:25:42 2017 +0300 +++ b/src/core/ngx_murmurhash.c Thu Jun 22 14:41:09 2017 -0700 @@ -50,3 +50,63 @@ return h; } + + +uint64_t +ngx_murmur_hash2_64(u_char *data, size_t len, uint64_t seed) +{ + uint64_t h, k; + + h = seed ^ len; + + while (len >= 8) { + k = data[0]; + k |= data[1] << 8; + k |= data[2] << 16; + k |= data[3] << 24; + k |= (uint64_t)data[4] << 32; + k |= (uint64_t)data[5] << 40; + k |= (uint64_t)data[6] << 48; + k |= (uint64_t)data[7] << 56; + + k *= 0xc6a4a7935bd1e995ull; + k ^= k >> 47; + k *= 0xc6a4a7935bd1e995ull; + + h ^= k; + h *= 0xc6a4a7935bd1e995ull; + + data += 8; + len -= 8; + } + + switch (len) { + case 7: + h ^= (uint64_t)data[6] << 48; + /* fall through */ + case 6: + h ^= (uint64_t)data[5] << 40; + /* fall through */ + case 5: + h ^= (uint64_t)data[4] << 32; + /* fall through */ + case 4: + h ^= data[3] << 24; + /* fall through */ + case 3: + h ^= data[2] << 16; + /* fall through */ + case 2: + h ^= data[1] << 8; + /* fall through */ + case 1: + h ^= data[0]; + h *= 0xc6a4a7935bd1e995ull; + } + + h ^= h >> 47; + h *= 0xc6a4a7935bd1e995ull; + h ^= h >> 47; + + return h; +} diff -r a39bc74873fa -r 895cea03ac21 src/core/ngx_murmurhash.h --- a/src/core/ngx_murmurhash.h Mon Jun 19 14:25:42 2017 +0300 +++ b/src/core/ngx_murmurhash.h Thu Jun 22 14:41:09 2017 -0700 @@ -15,5 +15,7 @@ uint32_t ngx_murmur_hash2(u_char *data, size_t len); +uint64_t ngx_murmur_hash2_64(u_char *data, size_t len, uint64_t seed); + #endif /* _NGX_MURMURHASH_H_INCLUDED_ */ diff -r a39bc74873fa -r 895cea03ac21 src/http/v2/ngx_http_v2.c --- a/src/http/v2/ngx_http_v2.c Mon Jun 19 14:25:42 2017 +0300 +++ b/src/http/v2/ngx_http_v2.c Thu Jun 22 14:41:09 2017 -0700 @@ -245,6 +245,8 @@ h2c->frame_size = NGX_HTTP_V2_DEFAULT_FRAME_SIZE; + h2c->max_hpack_table_size = NGX_HTTP_V2_DEFAULT_HPACK_TABLE_SIZE; + h2scf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v2_module); h2c->pool = ngx_create_pool(h2scf->pool_size, h2c->connection->log); @@ -2018,6 +2020,17 @@ h2c->frame_size = value; break; + case NGX_HTTP_V2_HEADER_TABLE_SIZE_SETTING: + + if (value > NGX_HTTP_V2_MAX_HPACK_TABLE_SIZE) { + h2c->max_hpack_table_size = NGX_HTTP_V2_MAX_HPACK_TABLE_SIZE; + } else { + h2c->max_hpack_table_size = value; + } + + h2c->indicate_resize = 1; + break; + default: break; } diff -r a39bc74873fa -r 895cea03ac21 src/http/v2/ngx_http_v2.h --- a/src/http/v2/ngx_http_v2.h Mon Jun 19 14:25:42 2017 +0300 +++ b/src/http/v2/ngx_http_v2.h Thu Jun 22 14:41:09 2017 -0700 @@ -49,6 +49,13 @@ #define NGX_HTTP_V2_MAX_WINDOW ((1U << 31) - 1) #define NGX_HTTP_V2_DEFAULT_WINDOW 65535 +#define HPACK_ENC_HTABLE_SZ 128 /* better to keep a PoT < 64k */ +#define HPACK_ENC_HTABLE_ENTRIES ((HPACK_ENC_HTABLE_SZ * 100) / 128) +#define HPACK_ENC_DYNAMIC_KEY_TBL_SZ 10 /* 10 is sufficient for most */ +#define HPACK_ENC_MAX_ENTRY 512 /* longest header size to match */ + +#define NGX_HTTP_V2_DEFAULT_HPACK_TABLE_SIZE 4096 +#define NGX_HTTP_V2_MAX_HPACK_TABLE_SIZE 16384 /* < 64k */ typedef struct ngx_http_v2_connection_s ngx_http_v2_connection_t; typedef struct ngx_http_v2_node_s ngx_http_v2_node_t; @@ -110,6 +117,46 @@ } ngx_http_v2_hpack_t; +#if (NGX_HTTP_V2_HPACK_ENC) +typedef struct { + uint64_t hash_val; + uint32_t index; + uint16_t pos; + uint16_t klen, vlen; + uint16_t size; + uint16_t next; +} ngx_http_v2_hpack_enc_entry_t; + + +typedef struct { + uint64_t hash_val; + uint32_t index; + uint16_t pos; + uint16_t klen; +} ngx_http_v2_hpack_name_entry_t; + + +typedef struct { + size_t size; /* size as defined in RFC 7541 */ + uint32_t top; /* the last entry */ + uint32_t pos; + uint16_t n_elems; /* number of elements */ + uint16_t base; /* index of the oldest entry */ + uint16_t last; /* index of the newest entry */ + + /* hash table for dynamic entries, instead using a generic hash table, + which would be too slow to process a significant amount of headers, + this table is not determenistic, and might ocasionally fail to insert + a value, at the cost of slightly worse compression, but significantly + faster performance */ + ngx_http_v2_hpack_enc_entry_t htable[HPACK_ENC_HTABLE_SZ]; + ngx_http_v2_hpack_name_entry_t heads[HPACK_ENC_DYNAMIC_KEY_TBL_SZ]; + u_char storage[NGX_HTTP_V2_MAX_HPACK_TABLE_SIZE + + HPACK_ENC_MAX_ENTRY]; +} ngx_http_v2_hpack_enc_t; +#endif + + struct ngx_http_v2_connection_s { ngx_connection_t *connection; ngx_http_connection_t *http_connection; @@ -122,6 +169,8 @@ size_t frame_size; + size_t max_hpack_table_size; + ngx_queue_t waiting; ngx_http_v2_state_t state; @@ -146,6 +195,11 @@ unsigned settings_ack:1; unsigned blocked:1; unsigned goaway:1; + unsigned indicate_resize:1; + +#if (NGX_HTTP_V2_HPACK_ENC) + ngx_http_v2_hpack_enc_t hpack_enc; +#endif }; @@ -347,4 +401,31 @@ #define ngx_http_v2_write_sid ngx_http_v2_write_uint32 +u_char *ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len, + u_char *tmp, ngx_uint_t lower); + +u_char * +ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix, ngx_uint_t value); + +#define ngx_http_v2_write_name(dst, src, len, tmp) \ + ngx_http_v2_string_encode(dst, src, len, tmp, 1) +#define ngx_http_v2_write_value(dst, src, len, tmp) \ + ngx_http_v2_string_encode(dst, src, len, tmp, 0) + +u_char * +ngx_http_v2_write_header(ngx_http_v2_connection_t *h2c, u_char *pos, + u_char *key, size_t key_len, u_char *value, size_t value_len, + u_char *tmp); + +void +ngx_http_v2_table_resize(ngx_http_v2_connection_t *h2c); + +#define ngx_http_v2_write_header_str(key, value) \ + ngx_http_v2_write_header(h2c, pos, (u_char *) key, sizeof(key) - 1, \ + (u_char *) value, sizeof(value) - 1, tmp); + +#define ngx_http_v2_write_header_tbl(key, val) \ + ngx_http_v2_write_header(h2c, pos, (u_char *) key, sizeof(key) - 1, \ + val.data, val.len, tmp); + #endif /* _NGX_HTTP_V2_H_INCLUDED_ */ diff -r a39bc74873fa -r 895cea03ac21 src/http/v2/ngx_http_v2_filter_module.c --- a/src/http/v2/ngx_http_v2_filter_module.c Mon Jun 19 14:25:42 2017 +0300 +++ b/src/http/v2/ngx_http_v2_filter_module.c Thu Jun 22 14:41:09 2017 -0700 @@ -25,11 +25,6 @@ #define ngx_http_v2_indexed(i) (128 + (i)) #define ngx_http_v2_inc_indexed(i) (64 + (i)) -#define ngx_http_v2_write_name(dst, src, len, tmp) \ - ngx_http_v2_string_encode(dst, src, len, tmp, 1) -#define ngx_http_v2_write_value(dst, src, len, tmp) \ - ngx_http_v2_string_encode(dst, src, len, tmp, 0) - #define NGX_HTTP_V2_ENCODE_RAW 0 #define NGX_HTTP_V2_ENCODE_HUFF 0x80 @@ -53,10 +48,6 @@ #define NGX_HTTP_V2_NO_TRAILERS (ngx_http_v2_out_frame_t *) -1 -static u_char *ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len, - u_char *tmp, ngx_uint_t lower); -static u_char *ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix, - ngx_uint_t value); static ngx_http_v2_out_frame_t *ngx_http_v2_create_headers_frame( ngx_http_request_t *r, u_char *pos, u_char *end, ngx_uint_t fin); static ngx_http_v2_out_frame_t *ngx_http_v2_create_trailers_frame( @@ -142,6 +133,7 @@ ngx_http_core_loc_conf_t *clcf; ngx_http_core_srv_conf_t *cscf; u_char addr[NGX_SOCKADDR_STRLEN]; + ngx_http_v2_connection_t *h2c; static const u_char nginx[5] = "\x84\xaa\x63\x55\xe7"; #if (NGX_HTTP_GZIP) @@ -150,11 +142,9 @@ #endif static size_t nginx_ver_len = ngx_http_v2_literal_size(NGINX_VER); - static u_char nginx_ver[ngx_http_v2_literal_size(NGINX_VER)]; static size_t nginx_ver_build_len = ngx_http_v2_literal_size(NGINX_VER_BUILD); - static u_char nginx_ver_build[ngx_http_v2_literal_size(NGINX_VER_BUILD)]; if (!r->stream) { return ngx_http_next_header_filter(r); @@ -415,7 +405,7 @@ } tmp = ngx_palloc(r->pool, tmp_len); - pos = ngx_pnalloc(r->pool, len); + pos = ngx_pnalloc(r->pool, len + 15 + 1); if (pos == NULL || tmp == NULL) { return NGX_ERROR; @@ -423,6 +413,18 @@ start = pos; + h2c = r->stream->connection; + + if (h2c->indicate_resize) { + *pos = 32; + pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(5), + h2c->max_hpack_table_size); + h2c->indicate_resize = 0; +#if (NGX_HTTP_V2_HPACK_ENC) + ngx_http_v2_table_resize(h2c); +#endif + } + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, "http2 output header: \":status: %03ui\"", r->headers_out.status); @@ -431,67 +433,28 @@ *pos++ = status; } else { - *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_STATUS_INDEX); - *pos++ = NGX_HTTP_V2_ENCODE_RAW | 3; - pos = ngx_sprintf(pos, "%03ui", r->headers_out.status); + ngx_sprintf(pos + 8, "%O3ui", r->headers_out.status); + pos = ngx_http_v2_write_header(h2c, pos, (u_char *)":status", + sizeof(":status") - 1, pos + 8, 3, tmp); } if (r->headers_out.server == NULL) { - if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, - "http2 output header: \"server: %s\"", - NGINX_VER); + pos = ngx_http_v2_write_header_str("server", NGINX_VER); } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, - "http2 output header: \"server: %s\"", - NGINX_VER_BUILD); + pos = ngx_http_v2_write_header_str("server", NGINX_VER_BUILD); } else { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0, - "http2 output header: \"server: nginx\""); - } - - *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_SERVER_INDEX); - - if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) { - if (nginx_ver[0] == '\0') { - p = ngx_http_v2_write_value(nginx_ver, (u_char *) NGINX_VER, - sizeof(NGINX_VER) - 1, tmp); - nginx_ver_len = p - nginx_ver; - } - - pos = ngx_cpymem(pos, nginx_ver, nginx_ver_len); - - } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) { - if (nginx_ver_build[0] == '\0') { - p = ngx_http_v2_write_value(nginx_ver_build, - (u_char *) NGINX_VER_BUILD, - sizeof(NGINX_VER_BUILD) - 1, tmp); - nginx_ver_build_len = p - nginx_ver_build; - } - - pos = ngx_cpymem(pos, nginx_ver_build, nginx_ver_build_len); - - } else { - pos = ngx_cpymem(pos, nginx, sizeof(nginx)); + pos = ngx_http_v2_write_header_str("server", "nginx"); } } if (r->headers_out.date == NULL) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, - "http2 output header: \"date: %V\"", - &ngx_cached_http_time); - - *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_DATE_INDEX); - pos = ngx_http_v2_write_value(pos, ngx_cached_http_time.data, - ngx_cached_http_time.len, tmp); + pos = ngx_http_v2_write_header_tbl("date", ngx_cached_http_time); } if (r->headers_out.content_type.len) { - *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_CONTENT_TYPE_INDEX); - if (r->headers_out.content_type_len == r->headers_out.content_type.len && r->headers_out.charset.len) { @@ -517,64 +480,36 @@ r->headers_out.content_type.data = p - len; } - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, - "http2 output header: \"content-type: %V\"", - &r->headers_out.content_type); - - pos = ngx_http_v2_write_value(pos, r->headers_out.content_type.data, - r->headers_out.content_type.len, tmp); + pos = ngx_http_v2_write_header_tbl("content-type", + r->headers_out.content_type); } if (r->headers_out.content_length == NULL && r->headers_out.content_length_n >= 0) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, - "http2 output header: \"content-length: %O\"", - r->headers_out.content_length_n); - - *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_CONTENT_LENGTH_INDEX); - - p = pos; - pos = ngx_sprintf(pos + 1, "%O", r->headers_out.content_length_n); - *p = NGX_HTTP_V2_ENCODE_RAW | (u_char) (pos - p - 1); + p = ngx_sprintf(pos + 15, "%O", r->headers_out.content_length_n); + pos = ngx_http_v2_write_header(h2c, pos, (u_char *)"content-length", + sizeof("content-length") - 1, pos + 15, + p - (pos + 15), tmp); } if (r->headers_out.last_modified == NULL && r->headers_out.last_modified_time != -1) { - *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_LAST_MODIFIED_INDEX); - - ngx_http_time(pos, r->headers_out.last_modified_time); + ngx_http_time(pos + 14, r->headers_out.last_modified_time); len = sizeof("Wed, 31 Dec 1986 18:00:00 GMT") - 1; - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, fc->log, 0, - "http2 output header: \"last-modified: %*s\"", - len, pos); - - /* - * Date will always be encoded using huffman in the temporary buffer, - * so it's safe here to use src and dst pointing to the same address. - */ - pos = ngx_http_v2_write_value(pos, pos, len, tmp); + pos = ngx_http_v2_write_header(h2c, pos, (u_char *)"last-modified", + sizeof("last-modified") - 1, pos + 14, + len, tmp); } if (r->headers_out.location && r->headers_out.location->value.len) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, - "http2 output header: \"location: %V\"", - &r->headers_out.location->value); - - *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_LOCATION_INDEX); - pos = ngx_http_v2_write_value(pos, r->headers_out.location->value.data, - r->headers_out.location->value.len, tmp); + pos = ngx_http_v2_write_header_tbl("location", r->headers_out.location->value); } #if (NGX_HTTP_GZIP) if (r->gzip_vary) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0, - "http2 output header: \"vary: Accept-Encoding\""); - - *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_VARY_INDEX); - pos = ngx_cpymem(pos, accept_encoding, sizeof(accept_encoding)); + pos = ngx_http_v2_write_header_str("vary", "Accept-Encoding"); } #endif @@ -597,23 +532,9 @@ continue; } -#if (NGX_DEBUG) - if (fc->log->log_level & NGX_LOG_DEBUG_HTTP) { - ngx_strlow(tmp, header[i].key.data, header[i].key.len); - - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, fc->log, 0, - "http2 output header: \"%*s: %V\"", - header[i].key.len, tmp, &header[i].value); - } -#endif - - *pos++ = 0; - - pos = ngx_http_v2_write_name(pos, header[i].key.data, - header[i].key.len, tmp); - - pos = ngx_http_v2_write_value(pos, header[i].value.data, - header[i].value.len, tmp); + pos = ngx_http_v2_write_header(h2c, pos, header[i].key.data, + header[i].key.len, header[i].value.data, + header[i].value.len, tmp); } frame = ngx_http_v2_create_headers_frame(r, start, pos, r->header_only); @@ -643,11 +564,12 @@ static ngx_http_v2_out_frame_t * ngx_http_v2_create_trailers_frame(ngx_http_request_t *r) { - u_char *pos, *start, *tmp; - size_t len, tmp_len; - ngx_uint_t i; - ngx_list_part_t *part; - ngx_table_elt_t *header; + u_char *pos, *start, *tmp; + size_t len, tmp_len; + ngx_uint_t i; + ngx_list_part_t *part; + ngx_table_elt_t *header; + ngx_http_v2_connection_t *h2c; len = 0; tmp_len = 0; @@ -713,6 +635,8 @@ part = &r->headers_out.trailers.part; header = part->elts; + h2c = r->stream->connection; + for (i = 0; /* void */; i++) { if (i >= part->nelts) { @@ -739,20 +663,16 @@ } #endif - *pos++ = 0; - - pos = ngx_http_v2_write_name(pos, header[i].key.data, - header[i].key.len, tmp); - - pos = ngx_http_v2_write_value(pos, header[i].value.data, - header[i].value.len, tmp); + pos = ngx_http_v2_write_header(h2c, pos, header[i].key.data, + header[i].key.len, header[i].value.data, + header[i].value.len, tmp); } return ngx_http_v2_create_headers_frame(r, start, pos, 1); } -static u_char * +u_char * ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len, u_char *tmp, ngx_uint_t lower) { @@ -778,7 +698,7 @@ } -static u_char * +u_char * ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix, ngx_uint_t value) { if (value < prefix) { diff -r a39bc74873fa -r 895cea03ac21 src/http/v2/ngx_http_v2_table.c --- a/src/http/v2/ngx_http_v2_table.c Mon Jun 19 14:25:42 2017 +0300 +++ b/src/http/v2/ngx_http_v2_table.c Thu Jun 22 14:41:09 2017 -0700 @@ -347,3 +347,434 @@ return NGX_OK; } + + +#if (NGX_HTTP_V2_HPACK_ENC) + +static ngx_int_t +hpack_get_static_index(ngx_http_v2_connection_t *h2c, u_char *val, size_t len); + +static ngx_int_t +hpack_get_dynamic_index(ngx_http_v2_connection_t *h2c, uint64_t key_hash, + uint8_t *key, size_t key_len); + + +void +ngx_http_v2_table_resize(ngx_http_v2_connection_t *h2c) +{ + ngx_http_v2_hpack_enc_entry_t *table; + uint64_t idx; + + table = h2c->hpack_enc.htable; + + while (h2c->hpack_enc.size > h2c->max_hpack_table_size) { + idx = h2c->hpack_enc.base; + h2c->hpack_enc.base = table[idx].next; + h2c->hpack_enc.size -= table[idx].size; + table[idx].hash_val = 0; + h2c->hpack_enc.n_elems--; + } +} + + +/* checks if a header is in the hpack table - if so returns the table entry, + otherwise encodes and inserts into the table and returns 0, + if failed to insert into table, returns -1 */ +static ngx_int_t +ngx_http_v2_table_encode_strings(ngx_http_v2_connection_t *h2c, + size_t key_len, size_t val_len, uint8_t *key, uint8_t *val, + ngx_int_t *header_idx) +{ + uint64_t hash_val, key_hash, idx, lru; + int i; + size_t size = key_len + val_len + 32; + uint8_t *storage = h2c->hpack_enc.storage; + + ngx_http_v2_hpack_enc_entry_t *table; + ngx_http_v2_hpack_name_entry_t *name; + + *header_idx = NGX_ERROR; + /* step 1: compute the hash value of header */ + if (size > HPACK_ENC_MAX_ENTRY || size > h2c->max_hpack_table_size) { + return NGX_ERROR; + } + + key_hash = ngx_murmur_hash2_64(key, key_len, 0x01234); + hash_val = ngx_murmur_hash2_64(val, val_len, key_hash); + + if (hash_val == 0) { + return NGX_ERROR; + } + + /* step 2: check if full header in the table */ + idx = hash_val; + i = -1; + while (idx) { + /* at most 8 locations are checked, but most will be done in 1 or 2 */ + table = &h2c->hpack_enc.htable[idx % HPACK_ENC_HTABLE_SZ]; + if (table->hash_val == hash_val + && table->klen == key_len + && table->vlen == val_len + && ngx_memcmp(key, storage + table->pos, key_len) == 0 + && ngx_memcmp(val, storage + table->pos + key_len, val_len) == 0) + { + return (h2c->hpack_enc.top - table->index) + 61; + } + + if (table->hash_val == 0 && i == -1) { + i = idx % HPACK_ENC_HTABLE_SZ; + break; + } + + idx >>= 8; + } + + /* step 3: check if key is in one of the tables */ + *header_idx = hpack_get_static_index(h2c, key, key_len); + + if (i == -1) { + return NGX_ERROR; + } + + if (*header_idx == NGX_ERROR) { + *header_idx = hpack_get_dynamic_index(h2c, key_hash, key, key_len); + } + + /* step 4: store the new entry */ + table = h2c->hpack_enc.htable; + + if (h2c->hpack_enc.top == 0xffffffff) { + /* just to be on the safe side, avoid overflow */ + ngx_memset(&h2c->hpack_enc, 0, sizeof(ngx_http_v2_hpack_enc_t)); + } + + while ((h2c->hpack_enc.size + size > h2c->max_hpack_table_size) + || h2c->hpack_enc.n_elems == HPACK_ENC_HTABLE_ENTRIES) { + /* make space for the new entry first */ + idx = h2c->hpack_enc.base; + h2c->hpack_enc.base = table[idx].next; + h2c->hpack_enc.size -= table[idx].size; + table[idx].hash_val = 0; + h2c->hpack_enc.n_elems--; + } + + table[i] = (ngx_http_v2_hpack_enc_entry_t){.hash_val = hash_val, + .index = h2c->hpack_enc.top, + .pos = h2c->hpack_enc.pos, + .klen = key_len, + .vlen = val_len, + .size = size, + .next = 0}; + + table[h2c->hpack_enc.last].next = i; + if (h2c->hpack_enc.n_elems == 0) { + h2c->hpack_enc.base = i; + } + + h2c->hpack_enc.last = i; + h2c->hpack_enc.top++; + h2c->hpack_enc.size += size; + h2c->hpack_enc.n_elems++; + + /* update header name lookup */ + if (*header_idx == NGX_ERROR ) { + lru = h2c->hpack_enc.top; + + for (i=0; ihpack_enc.heads[i]; + + if ( name->hash_val == 0 || (name->hash_val == key_hash + && ngx_memcmp(storage + name->pos, key, key_len) == 0) ) + { + name->hash_val = key_hash; + name->pos = h2c->hpack_enc.pos; + name->index = h2c->hpack_enc.top - 1; + break; + } + + if (lru > name->index) { + lru = name->index; + idx = i; + } + } + + if (i == HPACK_ENC_DYNAMIC_KEY_TBL_SZ) { + name = &h2c->hpack_enc.heads[idx]; + name->hash_val = hash_val; + name->pos = h2c->hpack_enc.pos; + name->index = h2c->hpack_enc.top - 1; + } + } + + ngx_memcpy(storage + h2c->hpack_enc.pos, key, key_len); + ngx_memcpy(storage + h2c->hpack_enc.pos + key_len, val, val_len); + + h2c->hpack_enc.pos += size; + if (h2c->hpack_enc.pos > NGX_HTTP_V2_MAX_HPACK_TABLE_SIZE) { + h2c->hpack_enc.pos = 0; + } + + return NGX_OK; +} + + +u_char * +ngx_http_v2_write_header(ngx_http_v2_connection_t *h2c, u_char *pos, + u_char *key, size_t key_len, + u_char *value, size_t value_len, + u_char *tmp) +{ + ngx_int_t idx, header_idx; + + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 output header: %*s: %*s", key_len, key, value_len, + value); + + /* attempt to find the value in the dynamic table */ + idx = ngx_http_v2_table_encode_strings(h2c, key_len, value_len, key, value, + &header_idx); + + if (idx > 0) { + /* positive index indicates success */ + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 hpack encode: Indexed Header Field: %ud", idx); + + *pos = 128; + pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(7), idx); + + } else { + + if (header_idx == NGX_ERROR) { /* if key is not present */ + + if (idx == NGX_ERROR) { /* if header was not added */ + *pos++ = 0; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 hpack encode: Literal Header Field without" + " Indexing ? New Name"); + } else { /* if header was added */ + *pos++ = 64; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 hpack encode: Literal Header Field with " + "Incremental Indexing ? New Name"); + } + + pos = ngx_http_v2_write_name(pos, key, key_len, tmp); + + } else { /* if key is present */ + + if (idx == NGX_ERROR) { + *pos = 0; + pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(4), header_idx); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 hpack encode: Literal Header Field without" + " Indexing ? Indexed Name: %ud", header_idx); + } else { + *pos = 64; + pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(6), header_idx); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 hpack encode: Literal Header Field with " + "Incremental Indexing ? Indexed Name: %ud", header_idx); + } + } + + pos = ngx_http_v2_write_value(pos, value, value_len, tmp); + } + + return pos; +} + + +static ngx_int_t +hpack_get_dynamic_index(ngx_http_v2_connection_t *h2c, uint64_t key_hash, + uint8_t *key, size_t key_len) +{ + ngx_http_v2_hpack_name_entry_t *name; + int i; + + for (i=0; ihpack_enc.heads[i]; + + if (name->hash_val == key_hash + && ngx_memcmp(h2c->hpack_enc.storage + name->pos, key, key_len) == 0) + { + if (name->index >= h2c->hpack_enc.top - h2c->hpack_enc.n_elems) { + return (h2c->hpack_enc.top - name->index) + 61; + } + break; + } + } + + return NGX_ERROR; +} + + +/* decide if a given header is present in the static dictionary, this could be + done in several ways, but it seems the fastest one is "exhaustive" search */ +static ngx_int_t +hpack_get_static_index(ngx_http_v2_connection_t *h2c, u_char *val, size_t len) +{ + /* the static dictionary of response only headers, + although response headers can be put by origin, + that would be rare */ + static const struct { + u_char len; + const u_char val[28]; + u_char idx; + } server_headers[] = { + { 3, "age", 21},//0 + { 3, "via", 60}, + { 4, "date", 33},//2 + { 4, "etag", 34}, + { 4, "link", 45}, + { 4, "vary", 59}, + { 5, "allow", 22},//6 + { 6, "server", 54},//7 + { 7, "expires", 36},//8 + { 7, "refresh", 52}, + { 8, "location", 46},//10 + {10, "set-cookie", 55},//11 + {11, "retry-after", 53},//12 + {12, "content-type", 31},//13 + {13, "content-range", 30},//14 + {13, "accept-ranges", 18}, + {13, "cache-control", 24}, + {13, "last-modified", 44}, + {14, "content-length", 28},//18 + {16, "content-encoding", 26},//19 + {16, "content-language", 27}, + {16, "content-location", 29}, + {16, "www-authenticate", 61}, + {17, "transfer-encoding", 57},//23 + {18, "proxy-authenticate", 48},//24 + {19, "content-disposition", 25},//25 + {25, "strict-transport-security", 56},//26 + {27, "access-control-allow-origin", 20},//27 + {99, "", 99}, + }, *header; + + /* for a given length, where to start the search + since minimal length is 3, the table has a -3 + offset */ + static const int8_t start_at[] = { + [3-3] = 0, + [4-3] = 2, + [5-3] = 6, + [6-3] = 7, + [7-3] = 8, + [8-3] = 10, + [9-3] = -1, + [10-3] = 11, + [11-3] = 12, + [12-3] = 13, + [13-3] = 14, + [14-3] = 18, + [15-3] = -1, + [16-3] = 19, + [17-3] = 23, + [18-3] = 24, + [19-3] = 25, + [20-3] = -1, + [21-3] = -1, + [22-3] = -1, + [23-3] = -1, + [24-3] = -1, + [25-3] = 26, + [26-3] = -1, + [27-3] = 27, + }; + + uint64_t pref; + size_t save_len = len, i; + int8_t start; + + /* early exit for out of bounds lengths */ + if (len < 3 || len > 27) { + return NGX_ERROR; + } + + start = start_at[len - 3]; + if (start == -1) { + /* exit for non existent lengths */ + return NGX_ERROR; + } + + header = &server_headers[start_at[len - 3]]; + + /* load first 8 bytes of key, for fast comparison */ + if (len < 8) { + pref = 0; + if (len >= 4) { + pref = *(uint32_t *)(val + len - 4) | 0x20202020; + len -= 4; + } + while (len > 0) { /* 3 iterations at most */ + pref = (pref << 8) ^ (val[len - 1] | 0x20); + len--; + } + } else { + pref = *(uint64_t *)val | 0x2020202020202020; + len -= 8; + } + + /* iterate over headers with the right length */ + while (header->len == save_len) { + /* quickly compare the first 8 bytes, most tests will end here */ + if (pref != *(uint64_t *) header->val) { + header++; + continue; + } + + if (len == 0) { + /* len == 0, indicates prefix held the entire key */ + return header->idx; + } + /* for longer keys compare the rest */ + i = 1 + (save_len + 7) % 8; /* align so we can compare in quadwords */ + + while (i + 8 <= save_len) { /* 3 iterations at most */ + if ( *(uint64_t *)&header->val[i] + != (*(uint64_t *) &val[i]| 0x2020202020202020) ) + { + header++; + i = 0; + break; + } + i += 8; + } + + if (i == 0) { + continue; + } + + /* found the corresponding entry in the static dictionary */ + return header->idx; + } + + return NGX_ERROR; +} + +#else + +u_char * +ngx_http_v2_write_header(ngx_http_v2_connection_t *h2c, u_char *pos, + u_char *key, size_t key_len, + u_char *value, size_t value_len, + u_char *tmp) +{ + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 output header: %*s: %*s", key_len, key, value_len, + value); + + *pos++ = 64; + pos = ngx_http_v2_write_name(pos, key, key_len, tmp); + pos = ngx_http_v2_write_value(pos, value, value_len, tmp); + + return pos; +} + +#endif From igor at sysoev.ru Fri Jun 23 12:29:02 2017 From: igor at sysoev.ru (Igor Sysoev) Date: Fri, 23 Jun 2017 12:29:02 +0000 Subject: [njs] Fixed processing of large array indexes. Message-ID: details: http://hg.nginx.org/njs/rev/e33bea39c650 branches: changeset: 376:e33bea39c650 user: Igor Sysoev date: Fri Jun 23 15:27:28 2017 +0300 description: Fixed processing of large array indexes. diffstat: njs/njs_vm.c | 13 +++++++------ njs/test/njs_unit_test.c | 3 +++ 2 files changed, 10 insertions(+), 6 deletions(-) diffs (53 lines): diff -r 44ca33e6afdb -r e33bea39c650 njs/njs_vm.c --- a/njs/njs_vm.c Thu Jun 22 18:56:26 2017 +0300 +++ b/njs/njs_vm.c Fri Jun 23 15:27:28 2017 +0300 @@ -73,7 +73,7 @@ struct njs_property_next_s { static nxt_noinline njs_ret_t njs_property_query(njs_vm_t *vm, njs_property_query_t *pq, njs_value_t *object, njs_value_t *property); static njs_ret_t njs_array_property_query(njs_vm_t *vm, - njs_property_query_t *pq, njs_value_t *object, int32_t index); + njs_property_query_t *pq, njs_value_t *object, uint32_t index); static njs_ret_t njs_object_property_query(njs_vm_t *vm, njs_property_query_t *pq, njs_value_t *value, njs_object_t *object); static njs_ret_t njs_method_private_copy(njs_vm_t *vm, @@ -1054,21 +1054,22 @@ njs_property_query(njs_vm_t *vm, njs_pro static njs_ret_t njs_array_property_query(njs_vm_t *vm, njs_property_query_t *pq, - njs_value_t *object, int32_t index) + njs_value_t *object, uint32_t index) { - int32_t size; + uint32_t size; njs_ret_t ret; njs_value_t *value; njs_array_t *array; array = object->data.u.array; - size = index - array->length; - - if (size >= 0) { + + if (index >= array->length) { if (pq->query != NJS_PROPERTY_QUERY_SET) { return NXT_DECLINED; } + size = index - array->length; + ret = njs_array_expand(vm, array, 0, size + 1); if (nxt_slow_path(ret != NXT_OK)) { return ret; diff -r 44ca33e6afdb -r e33bea39c650 njs/test/njs_unit_test.c --- a/njs/test/njs_unit_test.c Thu Jun 22 18:56:26 2017 +0300 +++ b/njs/test/njs_unit_test.c Fri Jun 23 15:27:28 2017 +0300 @@ -2487,6 +2487,9 @@ static njs_unit_test_t njs_test[] = { nxt_string("var a = [ 1, 2, 3 ]; a[4294967296] = 4; a + a[4294967296]"), nxt_string("1,2,34") }, + { nxt_string("delete[]['4e9']"), + nxt_string("false") }, + { nxt_string("var n = 1, a = [ n += 1 ]; a"), nxt_string("2") }, From vbart at nginx.com Fri Jun 23 13:34:19 2017 From: vbart at nginx.com (Valentin V. Bartenev) Date: Fri, 23 Jun 2017 16:34:19 +0300 Subject: [PATCH] HTTP/2: add support for HPACK encoding In-Reply-To: <895cea03ac21fb18d2c2.1498181026@debian> References: <895cea03ac21fb18d2c2.1498181026@debian> Message-ID: <4997427.Yp2RfHUCHg@vbart-workstation> On Thursday 22 June 2017 18:23:46 Vlad Krasnov via nginx-devel wrote: > # HG changeset patch > # User Vlad Krasnov > # Date 1498167669 25200 > # Thu Jun 22 14:41:09 2017 -0700 > # Node ID 895cea03ac21fb18d2c2ba32389cd67dc74ddbd0 > # Parent a39bc74873faf9e5bea616561b43f6ecc55229f9 > HTTP/2: add support for HPACK encoding > > Add support for full HPACK encoding as per RFC7541. > This modification improves header compression ratio by 5-10% for the first > response, and by 40-95% for consequential responses on the connection. > The implementation is similar to the one used by Cloudflare. > [..] Thank you for the patch. Sorry, I'm busy with other work. I'll look on it later, as time permits. wbr, Valentin V. Bartenev From igor at sysoev.ru Fri Jun 23 14:42:25 2017 From: igor at sysoev.ru (Igor Sysoev) Date: Fri, 23 Jun 2017 14:42:25 +0000 Subject: [njs] Time zone name has been removed from unit tests. Message-ID: details: http://hg.nginx.org/njs/rev/d882561d50e8 branches: changeset: 377:d882561d50e8 user: Igor Sysoev date: Fri Jun 23 15:49:09 2017 +0300 description: Time zone name has been removed from unit tests. diffstat: njs/test/njs_unit_test.c | 13 ++++++------- 1 files changed, 6 insertions(+), 7 deletions(-) diffs (27 lines): diff -r e33bea39c650 -r d882561d50e8 njs/test/njs_unit_test.c --- a/njs/test/njs_unit_test.c Fri Jun 23 15:27:28 2017 +0300 +++ b/njs/test/njs_unit_test.c Fri Jun 23 15:49:09 2017 +0300 @@ -6361,17 +6361,16 @@ static njs_unit_test_t njs_test[] = { nxt_string("var d = new Date(2011, 5, 24, 18, 45); d.valueOf()"), nxt_string("1308895200000") }, - { nxt_string("var d = new Date(2011, 5, 24, 18, 45); d"), - nxt_string("Fri Jun 24 2011 18:45:00 GMT+1245 (CHAST)") }, - - { nxt_string("var d = new Date(2011, 5, 24, 18, 45); d.toString()"), - nxt_string("Fri Jun 24 2011 18:45:00 GMT+1245 (CHAST)") }, + { nxt_string("var d = new Date(2011, 5, 24, 18, 45);" + "d.toString().slice(0, 33)"), + nxt_string("Fri Jun 24 2011 18:45:00 GMT+1245") }, { nxt_string("var d = new Date(2011, 5, 24, 18, 45); d.toDateString()"), nxt_string("Fri Jun 24 2011") }, - { nxt_string("var d = new Date(2011, 5, 24, 18, 45); d.toTimeString()"), - nxt_string("18:45:00 GMT+1245 (CHAST)") }, + { nxt_string("var d = new Date(2011, 5, 24, 18, 45);" + "d.toTimeString().slice(0, 17)"), + nxt_string("18:45:00 GMT+1245") }, { nxt_string("var d = new Date(2011, 5, 24, 18, 45); d.toUTCString()"), nxt_string("Fri Jun 24 2011 06:00:00 GMT") }, From mdounin at mdounin.ru Mon Jun 26 22:58:34 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Mon, 26 Jun 2017 22:58:34 +0000 Subject: [nginx] Range filter: allowed ranges on empty files (ticket #1031). Message-ID: details: http://hg.nginx.org/nginx/rev/aeaac3ccee4f branches: changeset: 7043:aeaac3ccee4f user: Maxim Dounin date: Tue Jun 27 00:53:46 2017 +0300 description: Range filter: allowed ranges on empty files (ticket #1031). As per RFC 2616 / RFC 7233, any range request to an empty file is expected to result in 416 Range Not Satisfiable response, as there cannot be a "byte-range-spec whose first-byte-pos is less than the current length of the entity-body". On the other hand, this makes use of byte-range requests inconvenient in some cases, as reported for the slice module here: http://mailman.nginx.org/pipermail/nginx-devel/2017-June/010177.html This commit changes range filter to instead return 200 if the file is empty and the range requested starts at 0. diffstat: src/http/modules/ngx_http_range_filter_module.c | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-) diffs (13 lines): diff --git a/src/http/modules/ngx_http_range_filter_module.c b/src/http/modules/ngx_http_range_filter_module.c --- a/src/http/modules/ngx_http_range_filter_module.c +++ b/src/http/modules/ngx_http_range_filter_module.c @@ -382,6 +382,9 @@ ngx_http_range_parse(ngx_http_request_t if (ranges-- == 0) { return NGX_DECLINED; } + + } else if (start == 0) { + return NGX_DECLINED; } if (*p++ != ',') { From igor at sysoev.ru Tue Jun 27 11:19:33 2017 From: igor at sysoev.ru (Igor Sysoev) Date: Tue, 27 Jun 2017 11:19:33 +0000 Subject: [njs] Style fixes and small miscellaneous changes. Message-ID: details: http://hg.nginx.org/njs/rev/30cc8990272a branches: changeset: 378:30cc8990272a user: Igor Sysoev date: Tue Jun 27 11:17:54 2017 +0300 description: Style fixes and small miscellaneous changes. diffstat: nginx/ngx_http_js_module.c | 2 +- njs/njs_date.c | 4 ++-- njs/njs_number.c | 14 +++++++------- njs/njs_object.c | 4 ++-- njs/njs_string.c | 24 ++++++++++++------------ nxt/nxt_lvlhsh.c | 4 ++-- nxt/nxt_lvlhsh.h | 12 ++---------- nxt/nxt_rbtree.c | 1 - nxt/nxt_utf8.c | 2 +- 9 files changed, 29 insertions(+), 38 deletions(-) diffs (223 lines): diff -r d882561d50e8 -r 30cc8990272a nginx/ngx_http_js_module.c --- a/nginx/ngx_http_js_module.c Fri Jun 23 15:49:09 2017 +0300 +++ b/nginx/ngx_http_js_module.c Tue Jun 27 11:17:54 2017 +0300 @@ -139,7 +139,7 @@ static ngx_command_t ngx_http_js_comman { ngx_string("js_set"), NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE2, ngx_http_js_set, - NGX_HTTP_LOC_CONF_OFFSET, + 0, 0, NULL }, diff -r d882561d50e8 -r 30cc8990272a njs/njs_date.c --- a/njs/njs_date.c Fri Jun 23 15:49:09 2017 +0300 +++ b/njs/njs_date.c Tue Jun 27 11:17:54 2017 +0300 @@ -1052,8 +1052,8 @@ njs_date_prototype_to_iso_string(njs_vm_ year = tm.tm_year + 1900; size = snprintf((char *) buf, NJS_ISO_DATE_TIME_LEN, - (year < 0) ? "%07d-%02d-%02dT%02d:%02d:%02d.%03dZ": - "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ", + (year < 0) ? "%07d-%02d-%02dT%02d:%02d:%02d.%03dZ" + : "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ", year, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, (int) ((int64_t) time % 1000)); diff -r d882561d50e8 -r 30cc8990272a njs/njs_number.c --- a/njs/njs_number.c Fri Jun 23 15:49:09 2017 +0300 +++ b/njs/njs_number.c Tue Jun 27 11:17:54 2017 +0300 @@ -89,7 +89,7 @@ njs_number_dec_parse(u_char **start, u_c num = 0; while (p < end) { - /* Values below '0' become >= 208. */ + /* Values less than '0' become >= 208. */ c = *p - '0'; if (nxt_slow_path(c > 9)) { @@ -106,7 +106,7 @@ njs_number_dec_parse(u_char **start, u_c scale = 1; for (p++; p < end; p++) { - /* Values below '0' become >= 208. */ + /* Values less than '0' become >= 208. */ c = *p - '0'; if (nxt_slow_path(c > 9)) { @@ -135,7 +135,7 @@ njs_number_dec_parse(u_char **start, u_c } } - /* Values below '0' become >= 208. */ + /* Values less than '0' become >= 208. */ c = *e - '0'; if (nxt_fast_path(c <= 9)) { @@ -143,7 +143,7 @@ njs_number_dec_parse(u_char **start, u_c p = e + 1; while (p < end) { - /* Values below '0' become >= 208. */ + /* Values less than '0' become >= 208. */ c = *p - '0'; if (nxt_slow_path(c > 9)) { @@ -180,11 +180,11 @@ njs_number_hex_parse(u_char **start, u_c while (p < end) { c = (u_char) (*p | 0x20); - /* Values below '0' become >= 208. */ + /* Values less than '0' become >= 208. */ c = c - '0'; if (c > 9) { - /* Values below 'a' become >= 159. */ + /* Values less than 'a' become >= 159. */ c = c - ('a' - '0'); if (nxt_slow_path(c > 5)) { @@ -800,7 +800,7 @@ njs_number_parse_int(njs_vm_t *vm, njs_v n = njs_number_radix_parse(&p, end, radix); if (n >= 0) { - num = (minus) ? -n : n; + num = minus ? -n : n; } } diff -r d882561d50e8 -r 30cc8990272a njs/njs_object.c --- a/njs/njs_object.c Fri Jun 23 15:49:09 2017 +0300 +++ b/njs/njs_object.c Tue Jun 27 11:17:54 2017 +0300 @@ -915,8 +915,8 @@ njs_object_is_extensible(njs_vm_t *vm, n return NXT_ERROR; } - retval = args[1].data.u.object->extensible ? &njs_string_true : - &njs_string_false; + retval = args[1].data.u.object->extensible ? &njs_string_true + : &njs_string_false; vm->retval = *retval; diff -r d882561d50e8 -r 30cc8990272a njs/njs_string.c --- a/njs/njs_string.c Fri Jun 23 15:49:09 2017 +0300 +++ b/njs/njs_string.c Tue Jun 27 11:17:54 2017 +0300 @@ -2555,11 +2555,11 @@ static njs_ret_t njs_string_replace_search(njs_vm_t *vm, njs_value_t *args, njs_string_replace_t *r) { - int captures[2]; - u_char *p, *end; - size_t size; - njs_ret_t ret; - nxt_str_t search; + int captures[2]; + u_char *p, *end; + size_t size; + njs_ret_t ret; + nxt_str_t search; njs_string_get(&args[1], &search); @@ -2908,7 +2908,7 @@ njs_ret_t njs_primitive_value_to_string(njs_vm_t *vm, njs_value_t *dst, const njs_value_t *src) { - const njs_value_t *value; + const njs_value_t *value; switch (src->type) { @@ -2945,10 +2945,10 @@ njs_primitive_value_to_string(njs_vm_t * double njs_string_to_number(njs_value_t *value, nxt_bool_t parse_float) { - u_char *p, *start, *end; - double num; - size_t size; - nxt_bool_t minus; + u_char *p, *start, *end; + double num; + size_t size; + nxt_bool_t minus; const size_t infinity = sizeof("Infinity") - 1; @@ -3716,8 +3716,8 @@ njs_value_index(njs_vm_t *vm, njs_parser lhq.value = value; lhq.pool = vm->mem_cache_pool; - values_hash = parser->runtime ? &vm->values_hash: - &vm->shared->values_hash; + values_hash = parser->runtime ? &vm->values_hash + : &vm->shared->values_hash; ret = nxt_lvlhsh_insert(values_hash, &lhq); diff -r d882561d50e8 -r 30cc8990272a nxt/nxt_lvlhsh.c --- a/nxt/nxt_lvlhsh.c Fri Jun 23 15:49:09 2017 +0300 +++ b/nxt/nxt_lvlhsh.c Tue Jun 27 11:17:54 2017 +0300 @@ -848,8 +848,8 @@ nxt_lvlhsh_bucket_each(nxt_lvlhsh_each_t if (lhe->entries == 0) { next = *nxt_lvlhsh_next_bucket(lhe->proto, lhe->bucket); - lhe->bucket = (next == NULL) ? NXT_LVLHSH_BUCKET_DONE: - nxt_lvlhsh_bucket(lhe->proto, next); + lhe->bucket = (next == NULL) ? NXT_LVLHSH_BUCKET_DONE + : nxt_lvlhsh_bucket(lhe->proto, next); lhe->entries = nxt_lvlhsh_bucket_entries(lhe->proto, next); lhe->entry = 0; diff -r d882561d50e8 -r 30cc8990272a nxt/nxt_lvlhsh.h --- a/nxt/nxt_lvlhsh.h Fri Jun 23 15:49:09 2017 +0300 +++ b/nxt/nxt_lvlhsh.h Tue Jun 27 11:17:54 2017 +0300 @@ -93,7 +93,7 @@ typedef struct { typedef struct { - void *slot; + void *slot; } nxt_lvlhsh_t; @@ -179,15 +179,7 @@ typedef struct { (lhe)->proto = _proto; \ } while (0) -NXT_EXPORT void *nxt_lvlhsh_each(nxt_lvlhsh_t *lh, nxt_lvlhsh_each_t *le); - - -NXT_EXPORT void *nxt_lvlhsh_alloc(void *data, size_t size, nxt_uint_t nalloc); -NXT_EXPORT void nxt_lvlhsh_free(void *data, void *p, size_t size); - -NXT_EXPORT void *nxt_lvlhsh_pool_alloc(void *ctx, size_t size, - nxt_uint_t nalloc); -NXT_EXPORT void nxt_lvlhsh_pool_free(void *ctx, void *p, size_t size); +NXT_EXPORT void *nxt_lvlhsh_each(nxt_lvlhsh_t *lh, nxt_lvlhsh_each_t *lhe); #endif /* _NXT_LVLHSH_H_INCLUDED_ */ diff -r d882561d50e8 -r 30cc8990272a nxt/nxt_rbtree.c --- a/nxt/nxt_rbtree.c Fri Jun 23 15:49:09 2017 +0300 +++ b/nxt/nxt_rbtree.c Tue Jun 27 11:17:54 2017 +0300 @@ -349,7 +349,6 @@ nxt_rbtree_delete_fixup(nxt_rbtree_t *tr * Prefetching parent nodes does not help here according * to microbenchmarks. */ - parent = node->parent; if (node == parent->left) { diff -r d882561d50e8 -r 30cc8990272a nxt/nxt_utf8.c --- a/nxt/nxt_utf8.c Fri Jun 23 15:49:09 2017 +0300 +++ b/nxt/nxt_utf8.c Tue Jun 27 11:17:54 2017 +0300 @@ -35,7 +35,7 @@ nxt_utf8_encode(u_char *p, uint32_t u) if (u < 0x0800) { *p++ = (u_char) (( u >> 6) | 0xC0); - *p++ = (u_char) (( u & 0x3f) | 0x80); + *p++ = (u_char) (( u & 0x3F) | 0x80); return p; } From igor at sysoev.ru Tue Jun 27 11:19:35 2017 From: igor at sysoev.ru (Igor Sysoev) Date: Tue, 27 Jun 2017 11:19:35 +0000 Subject: [njs] Version 0.1.11. Message-ID: details: http://hg.nginx.org/njs/rev/fc5df33f4e6b branches: changeset: 379:fc5df33f4e6b user: Igor Sysoev date: Tue Jun 27 14:17:12 2017 +0300 description: Version 0.1.11. diffstat: CHANGES | 22 ++++++++++++++++++++++ Makefile | 2 +- 2 files changed, 23 insertions(+), 1 deletions(-) diffs (39 lines): diff -r 30cc8990272a -r fc5df33f4e6b CHANGES --- a/CHANGES Tue Jun 27 11:17:54 2017 +0300 +++ b/CHANGES Tue Jun 27 14:17:12 2017 +0300 @@ -1,3 +1,25 @@ + +Changes with nJScript 0.1.11 27 Jun 2017 + + *) Feature: Object.keys(), Object.prototype.hasOwnProperty() methods. + + *) Feature: Object.defineProperty(), Object.defineProperties(), + Object.getOwnPropertyDescriptor() methods. + + *) Feature: Object.getPrototypeOf(), Object.prototype.isPrototypeOf() + methods. + + *) Feature: Object.preventExtensions(), Object.isExtensible(), + Object.freeze(), Object.isFrozen(), Object.seal(), Object.isSealed() + methods. + + *) Feature: scientific notation literals support. + + *) Feature: hexadecimal literals support. + + *) Bugfix: processing of large array indexes. + + *) Bugfix: in parseInt() and Date.parse(). Changes with nJScript 0.1.10 04 Apr 2017 diff -r 30cc8990272a -r fc5df33f4e6b Makefile --- a/Makefile Tue Jun 27 11:17:54 2017 +0300 +++ b/Makefile Tue Jun 27 14:17:12 2017 +0300 @@ -1,5 +1,5 @@ -NJS_VER = 0.1.10 +NJS_VER = 0.1.11 NXT_LIB = nxt From igor at sysoev.ru Tue Jun 27 11:19:37 2017 From: igor at sysoev.ru (Igor Sysoev) Date: Tue, 27 Jun 2017 11:19:37 +0000 Subject: [njs] Added tag 0.1.11 for changeset fc5df33f4e6b Message-ID: details: http://hg.nginx.org/njs/rev/a0285736926b branches: changeset: 380:a0285736926b user: Igor Sysoev date: Tue Jun 27 14:19:20 2017 +0300 description: Added tag 0.1.11 for changeset fc5df33f4e6b diffstat: .hgtags | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diffs (8 lines): diff -r fc5df33f4e6b -r a0285736926b .hgtags --- a/.hgtags Tue Jun 27 14:17:12 2017 +0300 +++ b/.hgtags Tue Jun 27 14:19:20 2017 +0300 @@ -9,3 +9,4 @@ 15dc54100400f99c3ec044d8fb0175dd3d69adcb a29f29d481125db6101ecdc23dc20187c143cdc9 0.1.8 5bd2833988222900f60ad9b330ebc44df3b30662 0.1.9 b1456ef3e002376d9d146a8a02acf6a4a21748e9 0.1.10 +fc5df33f4e6b02a673daf3728ff690fb1e09b95e 0.1.11 From mdounin at mdounin.ru Tue Jun 27 14:47:08 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Tue, 27 Jun 2017 14:47:08 +0000 Subject: [nginx] nginx-1.13.2-RELEASE Message-ID: details: http://hg.nginx.org/nginx/rev/5be2b25bdc65 branches: changeset: 7044:5be2b25bdc65 user: Maxim Dounin date: Tue Jun 27 17:44:17 2017 +0300 description: nginx-1.13.2-RELEASE diffstat: docs/xml/nginx/changes.xml | 73 ++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 73 insertions(+), 0 deletions(-) diffs (83 lines): diff --git a/docs/xml/nginx/changes.xml b/docs/xml/nginx/changes.xml --- a/docs/xml/nginx/changes.xml +++ b/docs/xml/nginx/changes.xml @@ -5,6 +5,79 @@ + + + + +?????? ??? ??????? ?????????, ????????????? ? 0, ?? ??????? ????? +nginx ?????????? ????? 200 ?????? 416. + + +nginx now returns 200 instead of 416 +when a range starting with 0 is requested from an empty file. + + + + + +????????? add_trailer.
+??????? Piotr Sikora. +
+ +the "add_trailer" directive.
+Thanks to Piotr Sikora. +
+
+ + + +nginx ?? ????????? ??? Cygwin ? NetBSD; +?????? ????????? ? 1.13.0. + + +nginx could not be built on Cygwin and NetBSD; +the bug had appeared in 1.13.0. + + + + + +nginx ?? ????????? ??? MSYS2 / MinGW 64-bit.
+??????? Orgad Shaneh. +
+ +nginx could not be built under MSYS2 / MinGW 64-bit.
+Thanks to Orgad Shaneh. +
+
+ + + +??? ????????????? SSI ? ??????? ??????????? ??????????? +? proxy_pass ? ??????????? +? ??????? ???????? ??? ????????? segmentation fault. + + +a segmentation fault might occur in a worker process +when using SSI with many includes +and proxy_pass with variables. + + + + + +? ?????? ngx_http_v2_module.
+??????? Piotr Sikora. +
+ +in the ngx_http_v2_module.
+Thanks to Piotr Sikora. +
+
+ +
+ + From mdounin at mdounin.ru Tue Jun 27 14:47:12 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Tue, 27 Jun 2017 14:47:12 +0000 Subject: [nginx] release-1.13.2 tag Message-ID: details: http://hg.nginx.org/nginx/rev/a1c6685e80cb branches: changeset: 7045:a1c6685e80cb user: Maxim Dounin date: Tue Jun 27 17:44:18 2017 +0300 description: release-1.13.2 tag diffstat: .hgtags | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diffs (8 lines): diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -414,3 +414,4 @@ 7f394e433f0003222aa6531931ecc0b24740d5e4 3d0e8655f897959e48cc74e87670bb5492a58871 release-1.11.13 3671096a45bce570a2afa20b9faf42c7fb0f7e66 release-1.13.0 539f7893ecb96bee60965528c8958d7eb2f1ce6b release-1.13.1 +5be2b25bdc65775a85f18f68a4be4f58c7384415 release-1.13.2 From zelenkov at nginx.com Tue Jun 27 15:31:52 2017 From: zelenkov at nginx.com (Andrey Zelenkov) Date: Tue, 27 Jun 2017 15:31:52 +0000 Subject: [njs] Fixed Object.isSealed() method. Message-ID: details: http://hg.nginx.org/njs/rev/63d7430291f2 branches: changeset: 381:63d7430291f2 user: Andrey Zelenkov date: Tue Jun 27 17:03:16 2017 +0300 description: Fixed Object.isSealed() method. diffstat: njs/njs_object.c | 2 +- njs/test/njs_unit_test.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diffs (36 lines): diff -r a0285736926b -r 63d7430291f2 njs/njs_object.c --- a/njs/njs_object.c Tue Jun 27 14:19:20 2017 +0300 +++ b/njs/njs_object.c Tue Jun 27 17:03:16 2017 +0300 @@ -872,7 +872,7 @@ njs_object_is_sealed(njs_vm_t *vm, njs_v break; } - if (prop->writable) { + if (prop->configurable) { goto done; } } diff -r a0285736926b -r 63d7430291f2 njs/test/njs_unit_test.c --- a/njs/test/njs_unit_test.c Tue Jun 27 14:19:20 2017 +0300 +++ b/njs/test/njs_unit_test.c Tue Jun 27 17:03:16 2017 +0300 @@ -6280,17 +6280,17 @@ static njs_unit_test_t njs_test[] = { nxt_string("var o = Object.defineProperties({}, {a:{}, b:{writable:1}});" "o = Object.preventExtensions(o);" "Object.isSealed(o)"), - nxt_string("false") }, + nxt_string("true") }, { nxt_string("var o = Object.defineProperties({}, {a:{writable:1}});" "o = Object.preventExtensions(o);" "Object.isSealed(o)"), - nxt_string("false") }, + nxt_string("true") }, { nxt_string("var o = Object.defineProperties({}, {a:{configurable:1}});" "o = Object.preventExtensions(o);" "Object.isSealed(o)"), - nxt_string("true") }, + nxt_string("false") }, { nxt_string("var o = Object.preventExtensions({a:1});" "Object.isFrozen(o)"), From hongzhidao at gmail.com Wed Jun 28 09:25:13 2017 From: hongzhidao at gmail.com (=?UTF-8?B?5rSq5b+X6YGT?=) Date: Wed, 28 Jun 2017 17:25:13 +0800 Subject: [nginx] support request id in error log Message-ID: Hi! Does nginx plan support request id in error log? In the real case, it's really helpful to position question by the unique id of request. And it will become better if support in error log also. For example showed in our error log: 2017/06/26 23:56:09 [info] 52590#52590: *1 client prematurely closed connection (104: Connection reset by peer) while sendin g response to client, client: 127.0.0.1, server: test.com, request: "GET http://test.com/test.gz HTTP/1.1", *request_id: "ca8cef60e275* *06d52a9933700682f0e6"*, host: "test.com" We support it by writing customer module. Maybe it's more reasonable in official code. Thanks. B.R. -------------- next part -------------- An HTML attachment was scrubbed... URL: From ivanovdiit at gmail.com Wed Jun 28 20:33:05 2017 From: ivanovdiit at gmail.com (=?UTF-8?B?0JjQstCw0L0g0JjQstCw0L3QvtCy?=) Date: Wed, 28 Jun 2017 23:33:05 +0300 Subject: Fwd: Patch. Add new directive '2square' for image_filter In-Reply-To: References: Message-ID: Add new directive '2square' to image_filter, which resizes and converts image in square and add white border. It's cood be usefull for convert image for facebook banners. In attach: nginx.patch - patch for main repo (http://hg.nginx.org/nginx), image_filter_2square.t - file with tests for (http://hg.nginx.org/nginx- tests) nginx_org.patch - docs for (http://hg.nginx.org/nginx.org/) -- ? ?????????, ?????? ???? -- ? ?????????, ?????? ???? -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: nginx.patch Type: text/x-patch Size: 6376 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: nginx_org.patch Type: text/x-patch Size: 3058 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: image_filter_2square.t Type: application/x-troff Size: 2028 bytes Desc: not available URL: From spencer at kogosoftwarellc.com Wed Jun 28 21:15:16 2017 From: spencer at kogosoftwarellc.com (Joseph Spencer) Date: Wed, 28 Jun 2017 14:15:16 -0700 Subject: Help developing a block directive In-Reply-To: References: Message-ID: Hello, I'm trying to support this in my nginx.conf: my_custom_module_block { directive_for_my_custom_module_block "Some value"; } Here is how I'm setting up the commands: static ngx_command_t ngx_my_custom_module_commands[] = { { ngx_string("my_custom_module_block"), /* directive */ NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_NOARGS | NGX_CONF_BLOCK, my_custom_module_block, /* configuration setup function */ 0, /* No offset. Only one context is supported. */ 0, /* No offset when storing the module configuration on struct. */ NULL }, { ngx_string("directive_for_my_custom_module_block"), /* directive */ NGX_CONF_TAKE1, ngx_directive_for_my_custom_module_block_cmd, /* configuration setup function */ 0, /* No offset. Only one context is supported. */ 0, /* No offset when storing the module configuration on struct. */ NULL }, ngx_null_command /* command termination */ }; Everything compiles fine; however, I receive the following error and nginx won't start: 2017/06/28 21:02:44 [emerg] 1#1: "directive_for_my_custom_module_block" directive is not allowed here in /etc/nginx/nginx.conf:19 nginx: [emerg] "directive_for_my_custom_module_block" directive is not allowed here in /etc/nginx/nginx.conf:19 If anyone could be of assistance I would greatly appreciate it. -- Thanks, Joe Spencer (member) Kogo Software LLC -------------- next part -------------- An HTML attachment was scrubbed... URL: From vl at nginx.com Wed Jun 28 21:38:23 2017 From: vl at nginx.com (Vladimir Homutov) Date: Thu, 29 Jun 2017 00:38:23 +0300 Subject: Help developing a block directive In-Reply-To: References: Message-ID: <008e51d2-b95f-4db8-012c-6a47154d29d4@nginx.com> On 29.06.2017 00:15, Joseph Spencer wrote: > Hello, > > I'm trying to support this in my nginx.conf: > > my_custom_module_block { > directive_for_my_custom_module_block "Some value"; > } > > Here is how I'm setting up the commands: > > static ngx_command_t ngx_my_custom_module_commands[] = { > > { > > ngx_string("my_custom_module_block"), /* directive */ > > NGX_HTTP_SRV_CONF | > > NGX_HTTP_LOC_CONF | > > NGX_CONF_NOARGS | > > NGX_CONF_BLOCK, > > my_custom_module_block, /* configuration setup function */ > > 0, /* No offset. Only one context is supported. */ > > 0, /* No offset when storing the module configuration on struct. > */ > NULL > > }, > > > > { > > ngx_string("directive_for_my_custom_module_block"), /* directive > */ > NGX_CONF_TAKE1, > > ngx_directive_for_my_custom_module_block_cmd, /* configuration > setup function */ > 0, /* No offset. Only one context is supported. */ > > 0, /* No offset when storing the module configuration on struct. > */ > NULL > > }, > > > > ngx_null_command /* command termination */ > > }; > > Everything compiles fine; however, I receive the following error and > nginx won't start: > > 2017/06/28 21:02:44 [emerg] 1#1: "directive_for_my_custom_module_block" > directive is not allowed here in /etc/nginx/nginx.conf:19 > nginx: [emerg] "directive_for_my_custom_module_block" directive is not > allowed here in /etc/nginx/nginx.conf:19 > > If anyone could be of assistance I would greatly appreciate it. > Take a look at ngx_conf_file.h to understand which structure flags have. You did not specify any possible context for the command that will appear in your block: NGX_CONF_TAKE1 solo only describes number of arguments, but command is not matching anything, thus the error. You can either declare your own context, like ngx_http_upstream_module does (NGX_HTTP_UPS_CONF for commands inside upstream{} block) or use API from ngx_conf_file.h to process commands inside your block like ngx_conf_handler() does. From spencer at kogosoftwarellc.com Wed Jun 28 22:00:58 2017 From: spencer at kogosoftwarellc.com (Joseph Spencer) Date: Wed, 28 Jun 2017 15:00:58 -0700 Subject: Help developing a block directive In-Reply-To: <008e51d2-b95f-4db8-012c-6a47154d29d4@nginx.com> References: <008e51d2-b95f-4db8-012c-6a47154d29d4@nginx.com> Message-ID: Thanks for the fast reply! So I think I see how this works now; however, I'm getting a new error. Seems to be parsing related. Here is what I have now: #define NGX_MY_CUSTOM_MODULE_CONF 0x80000001 static ngx_command_t ngx_my_custom_module_commands[] = { { ngx_string("my_custom_module_block"), /* directive */ NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_NOARGS | NGX_CONF_BLOCK, my_custom_module_block, /* configuration setup function */ 0, /* No offset. Only one context is supported. */ 0, /* No offset when storing the module configuration on struct. */ NULL }, { ngx_string("directive_for_my_custom_module_block"), /* directive */ NGX_CONF_TAKE1|NGX_MY_CUSTOM_MODULE_CONF, ngx_directive_for_my_custom_module_block_cmd, /* configuration setup function */ 0, /* No offset. Only one context is supported. */ 0, /* No offset when storing the module configuration on struct. */ NULL }, ngx_null_command /* command termination */ }; static char *my_custom_module_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { cf->cmd_type = NGX_MY_CUSTOM_MODULE_CONF; ngx_http_core_loc_conf_t *clcf; clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); clcf->handler = ngx_my_custom_module_handler; return NGX_CONF_OK; } How I'm seeing the following error: 2017/06/28 21:48:46 [emerg] 1#1: "location" directive is not allowed here in /etc/nginx/nginx.conf:26 nginx: [emerg] "location" directive is not allowed here in /etc/nginx/nginx.conf:26 Here is my full configuration file: worker_processes 1; load_module "modules/ngx_my_custom_module.so"; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; server { listen 80; server_name localhost; location / { my_custom_module_block { directive_for_my_custom_module_block "Some value"; } } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } } On Wed, Jun 28, 2017 at 2:38 PM, Vladimir Homutov wrote: > On 29.06.2017 00:15, Joseph Spencer wrote: > > Hello, > > > > I'm trying to support this in my nginx.conf: > > > > my_custom_module_block { > > directive_for_my_custom_module_block "Some value"; > > } > > > > Here is how I'm setting up the commands: > > > > static ngx_command_t ngx_my_custom_module_commands[] = { > > > > { > > > > ngx_string("my_custom_module_block"), /* directive */ > > > > NGX_HTTP_SRV_CONF | > > > > NGX_HTTP_LOC_CONF | > > > > NGX_CONF_NOARGS | > > > > NGX_CONF_BLOCK, > > > > my_custom_module_block, /* configuration setup function */ > > > > 0, /* No offset. Only one context is supported. */ > > > > 0, /* No offset when storing the module configuration on struct. > > */ > > NULL > > > > }, > > > > > > > > { > > > > ngx_string("directive_for_my_custom_module_block"), /* directive > > */ > > NGX_CONF_TAKE1, > > > > ngx_directive_for_my_custom_module_block_cmd, /* configuration > > setup function */ > > 0, /* No offset. Only one context is supported. */ > > > > 0, /* No offset when storing the module configuration on struct. > > */ > > NULL > > > > }, > > > > > > > > ngx_null_command /* command termination */ > > > > }; > > > > Everything compiles fine; however, I receive the following error and > > nginx won't start: > > > > 2017/06/28 21:02:44 [emerg] 1#1: "directive_for_my_custom_module_block" > > directive is not allowed here in /etc/nginx/nginx.conf:19 > > nginx: [emerg] "directive_for_my_custom_module_block" directive is not > > allowed here in /etc/nginx/nginx.conf:19 > > > > If anyone could be of assistance I would greatly appreciate it. > > > > Take a look at ngx_conf_file.h to understand which structure flags have. > You did not specify any possible context for the command that will > appear in your block: NGX_CONF_TAKE1 solo only describes number of > arguments, but command is not matching anything, thus the error. > > You can either declare your own context, like ngx_http_upstream_module > does (NGX_HTTP_UPS_CONF for commands inside upstream{} block) or > use API from ngx_conf_file.h to process commands inside your block > like ngx_conf_handler() does. > > > > > > > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel > -- Thanks, Joe Spencer (member) Kogo Software LLC -------------- next part -------------- An HTML attachment was scrubbed... URL: From utkarsh.tewari at sjsu.edu Wed Jun 28 22:07:33 2017 From: utkarsh.tewari at sjsu.edu (Utkarsh Tewari) Date: Wed, 28 Jun 2017 15:07:33 -0700 Subject: Testing TLSv1.3 session resumption in Nginx using PSK Message-ID: Hi, I have been trying to implement a TLSv1.3 session resumption mechanism with Nginx and openssl-1.1.1-dev. Basic TLSv1.3 handshake works fine. Now I wanna test session resumption via PSK but couldn't find a way to do it. *Please guide me on how to enable the Nginx server to send a new_session_ticket to the client in order to enable session resumption using PSK in TLSv1.3* Thank you, Utkarsh Tewari Graduate student at San Jose State University ? -------------- next part -------------- An HTML attachment was scrubbed... URL: From Nate.Karstens at garmin.com Thu Jun 29 04:28:37 2017 From: Nate.Karstens at garmin.com (Karstens, Nate) Date: Thu, 29 Jun 2017 04:28:37 +0000 Subject: Testing TLSv1.3 session resumption in Nginx using PSK In-Reply-To: References: Message-ID: <145451D4E6785E4DA4DFA353A58CB7B2015E171E6E@OLAWPA-EXMB03.ad.garmin.com> Hello, I submitted some patches a few days ago that implement PSK support in nginx, maybe you could take a look and comment if these will meet your requirements (or at least serve as a starting point)? Please see: * http://mailman.nginx.org/pipermail/nginx-devel/2017-June/010203.html * http://mailman.nginx.org/pipermail/nginx-devel/2017-June/010202.html * http://mailman.nginx.org/pipermail/nginx-devel/2017-June/010204.html Thanks, Nate Karstens Garmin International, Inc. From: nginx-devel [mailto:nginx-devel-bounces at nginx.org] On Behalf Of Utkarsh Tewari Sent: Wednesday, June 28, 2017 5:08 PM To: nginx-devel at nginx.org Subject: Testing TLSv1.3 session resumption in Nginx using PSK Hi, I have been trying to implement a TLSv1.3 session resumption mechanism with Nginx and openssl-1.1.1-dev. Basic TLSv1.3 handshake works fine. Now I wanna test session resumption via PSK but couldn't find a way to do it. Please guide me on how to enable the Nginx server to send a new_session_ticket to the client in order to enable session resumption using PSK in TLSv1.3 Thank you, Utkarsh Tewari Graduate student at San Jose State University [Image removed by sender.]? ________________________________ CONFIDENTIALITY NOTICE: This email and any attachments are for the sole use of the intended recipient(s) and contain information that may be Garmin confidential and/or Garmin legally privileged. If you have received this email in error, please notify the sender by reply email and delete the message. Any disclosure, copying, distribution or use of this communication (including attachments) by someone other than the intended recipient is prohibited. Thank you. -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: image001.jpg Type: image/jpeg Size: 524 bytes Desc: image001.jpg URL: From eran.kornblau at kaltura.com Thu Jun 29 06:03:09 2017 From: eran.kornblau at kaltura.com (Eran Kornblau) Date: Thu, 29 Jun 2017 06:03:09 +0000 Subject: Help developing a block directive In-Reply-To: References: <008e51d2-b95f-4db8-012c-6a47154d29d4@nginx.com> Message-ID: I believe it happens because you changed cf->cmd_type, so when nginx tries to parse the second location, it thinks it?s in a scope of ?my custom module conf? and not in a scope of ?server?, and therefore doesn?t recognize the location directive. Nginx doesn?t automatically parse the contents of the block, you have to handle that yourself in your block handler. Usually this is done by updating the ngx_conf_t and calling ngx_conf_parse (make sure to save the original cf in a local variable so that you can restore it when ngx_conf_parse returns) There quite a few built-in nginx modules that add block directives, I suggest you look at one of them and match your code to their implementation (for example, I used ?map? as reference) Hope this help, Eran From: nginx-devel [mailto:nginx-devel-bounces at nginx.org] On Behalf Of Joseph Spencer Sent: Thursday, June 29, 2017 1:01 AM To: nginx-devel at nginx.org Subject: Re: Help developing a block directive Thanks for the fast reply! So I think I see how this works now; however, I'm getting a new error. Seems to be parsing related. Here is what I have now: #define NGX_MY_CUSTOM_MODULE_CONF 0x80000001 static ngx_command_t ngx_my_custom_module_commands[] = { { ngx_string("my_custom_module_block"), /* directive */ NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_NOARGS | NGX_CONF_BLOCK, my_custom_module_block, /* configuration setup function */ 0, /* No offset. Only one context is supported. */ 0, /* No offset when storing the module configuration on struct. */ NULL }, { ngx_string("directive_for_my_custom_module_block"), /* directive */ NGX_CONF_TAKE1|NGX_MY_CUSTOM_MODULE_CONF, ngx_directive_for_my_custom_module_block_cmd, /* configuration setup function */ 0, /* No offset. Only one context is supported. */ 0, /* No offset when storing the module configuration on struct. */ NULL }, ngx_null_command /* command termination */ }; static char *my_custom_module_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { cf->cmd_type = NGX_MY_CUSTOM_MODULE_CONF; ngx_http_core_loc_conf_t *clcf; clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); clcf->handler = ngx_my_custom_module_handler; return NGX_CONF_OK; } How I'm seeing the following error: 2017/06/28 21:48:46 [emerg] 1#1: "location" directive is not allowed here in /etc/nginx/nginx.conf:26 nginx: [emerg] "location" directive is not allowed here in /etc/nginx/nginx.conf:26 Here is my full configuration file: worker_processes 1; load_module "modules/ngx_my_custom_module.so"; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; server { listen 80; server_name localhost; location / { my_custom_module_block { directive_for_my_custom_module_block "Some value"; } } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } } On Wed, Jun 28, 2017 at 2:38 PM, Vladimir Homutov > wrote: On 29.06.2017 00:15, Joseph Spencer wrote: > Hello, > > I'm trying to support this in my nginx.conf: > > my_custom_module_block { > directive_for_my_custom_module_block "Some value"; > } > > Here is how I'm setting up the commands: > > static ngx_command_t ngx_my_custom_module_commands[] = { > > { > > ngx_string("my_custom_module_block"), /* directive */ > > NGX_HTTP_SRV_CONF | > > NGX_HTTP_LOC_CONF | > > NGX_CONF_NOARGS | > > NGX_CONF_BLOCK, > > my_custom_module_block, /* configuration setup function */ > > 0, /* No offset. Only one context is supported. */ > > 0, /* No offset when storing the module configuration on struct. > */ > NULL > > }, > > > > { > > ngx_string("directive_for_my_custom_module_block"), /* directive > */ > NGX_CONF_TAKE1, > > ngx_directive_for_my_custom_module_block_cmd, /* configuration > setup function */ > 0, /* No offset. Only one context is supported. */ > > 0, /* No offset when storing the module configuration on struct. > */ > NULL > > }, > > > > ngx_null_command /* command termination */ > > }; > > Everything compiles fine; however, I receive the following error and > nginx won't start: > > 2017/06/28 21:02:44 [emerg] 1#1: "directive_for_my_custom_module_block" > directive is not allowed here in /etc/nginx/nginx.conf:19 > nginx: [emerg] "directive_for_my_custom_module_block" directive is not > allowed here in /etc/nginx/nginx.conf:19 > > If anyone could be of assistance I would greatly appreciate it. > Take a look at ngx_conf_file.h to understand which structure flags have. You did not specify any possible context for the command that will appear in your block: NGX_CONF_TAKE1 solo only describes number of arguments, but command is not matching anything, thus the error. You can either declare your own context, like ngx_http_upstream_module does (NGX_HTTP_UPS_CONF for commands inside upstream{} block) or use API from ngx_conf_file.h to process commands inside your block like ngx_conf_handler() does. _______________________________________________ nginx-devel mailing list nginx-devel at nginx.org http://mailman.nginx.org/mailman/listinfo/nginx-devel -- Thanks, Joe Spencer (member) Kogo Software LLC -------------- next part -------------- An HTML attachment was scrubbed... URL: From spencer at kogosoftwarellc.com Thu Jun 29 07:27:50 2017 From: spencer at kogosoftwarellc.com (Joseph Spencer) Date: Thu, 29 Jun 2017 00:27:50 -0700 Subject: Dynamic Module Portability Message-ID: I'm looking to create a portable binary, and from everything I can read, it is almost impossible. The recommended approach seems to be to expose source code and require users to compile. This is painful because it requires the source code and gcc to be available: a hard sell for the lazy sysadmin. My goal is to create a proprietary module that is used in conjunction with a paid service. Users simply install the module and provide access token credentials. As you can imagine it's been really difficult, mainly because practically *every* configure option is compared at run time. I added some logging, and found that the module signature is indeed embedded in the resulting .so file. I was able to successfully use sed to get my module to work, but I'm thinking this is an obvious hack not even worth considering for a production binary: sed -i'' 's|8,4,8,0011111111010111001111111111111111|8,4,8,0000111111010111001110101111000110|' ngx_my_custom_module-nginx-1.11.5.so Having nginx -V is nice, but it could be beneficial to expose NGX_MODULE_SIGNATURE somehow. That way I could have an installer script that checkes to ensure that essential modules are available and modify the binary after it's been downloaded. I realize this is dangerous, but I'm not willing to expose source code and require gcc yet. Any opinions or guidance would be greatly appreciated. -- Thanks, Joe Spencer (member) Kogo Software LLC -------------- next part -------------- An HTML attachment was scrubbed... URL: From spencer at kogosoftwarellc.com Thu Jun 29 07:38:18 2017 From: spencer at kogosoftwarellc.com (Joseph Spencer) Date: Thu, 29 Jun 2017 00:38:18 -0700 Subject: Dynamic Module Portability In-Reply-To: References: Message-ID: Looks like I can grep NGX_MODULE_SIGNATURE from the nginx binary itself. Depending on the version of nginx, it could be a decent option. I'm literally only dependent on core and http. Most of the elements of the signature appear to be extraneous. On Thu, Jun 29, 2017 at 12:27 AM, Joseph Spencer < spencer at kogosoftwarellc.com> wrote: > I'm looking to create a portable binary, and from everything I can read, > it is almost impossible. The recommended approach seems to be to expose > source code and require users to compile. This is painful because it > requires the source code and gcc to be available: a hard sell for the lazy > sysadmin. > > My goal is to create a proprietary module that is used in conjunction with > a paid service. Users simply install the module and provide access token > credentials. > > As you can imagine it's been really difficult, mainly because practically > *every* configure option is compared at run time. > > I added some logging, and found that the module signature is indeed > embedded in the resulting .so file. I was able to successfully use sed to > get my module to work, but I'm thinking this is an obvious hack not even > worth considering for a production binary: > > sed -i'' 's|8,4,8,0011111111010111001111111111111111|8,4,8, > 0000111111010111001110101111000110|' ngx_my_custom_module-nginx-1.11.5.so > > Having nginx -V is nice, but it could be beneficial to > expose NGX_MODULE_SIGNATURE somehow. That way I could have an installer > script that checkes to ensure that essential modules are available and > modify the binary after it's been downloaded. I realize this is dangerous, > but I'm not willing to expose source code and require gcc yet. > > Any opinions or guidance would be greatly appreciated. > > -- > Thanks, > Joe Spencer (member) > Kogo Software LLC > -- Thanks, Joe Spencer (member) Kogo Software LLC -------------- next part -------------- An HTML attachment was scrubbed... URL: From spencer at kogosoftwarellc.com Thu Jun 29 08:17:32 2017 From: spencer at kogosoftwarellc.com (Joseph Spencer) Date: Thu, 29 Jun 2017 01:17:32 -0700 Subject: Dynamic Module Portability In-Reply-To: References: Message-ID: Or, I can just require that the host version of nginx be installed with the --with-compat option. On Thu, Jun 29, 2017 at 12:38 AM, Joseph Spencer < spencer at kogosoftwarellc.com> wrote: > Looks like I can grep NGX_MODULE_SIGNATURE from the nginx binary itself. > Depending on the version of nginx, it could be a decent option. I'm > literally only dependent on core and http. Most of the elements of the > signature appear to be extraneous. > > On Thu, Jun 29, 2017 at 12:27 AM, Joseph Spencer < > spencer at kogosoftwarellc.com> wrote: > >> I'm looking to create a portable binary, and from everything I can read, >> it is almost impossible. The recommended approach seems to be to expose >> source code and require users to compile. This is painful because it >> requires the source code and gcc to be available: a hard sell for the lazy >> sysadmin. >> >> My goal is to create a proprietary module that is used in conjunction >> with a paid service. Users simply install the module and provide access >> token credentials. >> >> As you can imagine it's been really difficult, mainly because practically >> *every* configure option is compared at run time. >> >> I added some logging, and found that the module signature is indeed >> embedded in the resulting .so file. I was able to successfully use sed to >> get my module to work, but I'm thinking this is an obvious hack not even >> worth considering for a production binary: >> >> sed -i'' 's|8,4,8,0011111111010111001111111111111111|8,4,8,0000111111010111001110101111000110|' >> ngx_my_custom_module-nginx-1.11.5.so >> >> Having nginx -V is nice, but it could be beneficial to >> expose NGX_MODULE_SIGNATURE somehow. That way I could have an installer >> script that checkes to ensure that essential modules are available and >> modify the binary after it's been downloaded. I realize this is dangerous, >> but I'm not willing to expose source code and require gcc yet. >> >> Any opinions or guidance would be greatly appreciated. >> >> -- >> Thanks, >> Joe Spencer (member) >> Kogo Software LLC >> > > > > -- > Thanks, > Joe Spencer (member) > Kogo Software LLC > -- Thanks, Joe Spencer (member) Kogo Software LLC -------------- next part -------------- An HTML attachment was scrubbed... URL: From mdounin at mdounin.ru Thu Jun 29 14:07:32 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Thu, 29 Jun 2017 17:07:32 +0300 Subject: Dynamic Module Portability In-Reply-To: References: Message-ID: <20170629140732.GK55433@mdounin.ru> Hello! On Thu, Jun 29, 2017 at 12:27:50AM -0700, Joseph Spencer wrote: > I'm looking to create a portable binary, and from everything I can read, it > is almost impossible. The recommended approach seems to be to expose > source code and require users to compile. This is painful because it > requires the source code and gcc to be available: a hard sell for the lazy > sysadmin. > > My goal is to create a proprietary module that is used in conjunction with > a paid service. Users simply install the module and provide access token > credentials. > > As you can imagine it's been really difficult, mainly because practically > *every* configure option is compared at run time. There are two basic approaches you can follow: 1. Distribute a module built with the same configure options and on the same platform as a target nginx binary. This is usually possible as long as you are targeting a particular OS and a package repository. The "nginx -V" output should contain enough information to reproduce the build. 2. Distribute a module built with the `--with-compat` configure option (available since nginx 1.11.5). Such a module will be compatible with any nginx binary built with the `--with-compat` option as well. Since all supported branches (1.13.x mainline, 1.12.x stable) already contain the `--with-compat` option, I would recommend following (2) unless there are specific reasons to support older versions. > I added some logging, and found that the module signature is indeed > embedded in the resulting .so file. I was able to successfully use sed to > get my module to work, but I'm thinking this is an obvious hack not even > worth considering for a production binary: > > sed -i'' > 's|8,4,8,0011111111010111001111111111111111|8,4,8,0000111111010111001110101111000110|' > ngx_my_custom_module-nginx-1.11.5.so > > Having nginx -V is nice, but it could be beneficial to > expose NGX_MODULE_SIGNATURE somehow. That way I could have an installer > script that checkes to ensure that essential modules are available and > modify the binary after it's been downloaded. I realize this is dangerous, > but I'm not willing to expose source code and require gcc yet. It is not expected to work that way. The signature is to prevent accidental loading of incompatible modules. It is neither expected to be exposed to users, nor modified. Instead, you should built a compatible module based on the "nginx -V" information, notably configure options - either by using the same options, or using `--with-compat`, as suggested above. If the resulting signature do not match, it merely indicates that you've done something wrong while building the module. -- Maxim Dounin http://nginx.org/ From spencer at kogosoftwarellc.com Thu Jun 29 15:34:17 2017 From: spencer at kogosoftwarellc.com (Joseph Spencer) Date: Thu, 29 Jun 2017 08:34:17 -0700 Subject: Dynamic Module Portability In-Reply-To: References: <20170629140732.GK55433@mdounin.ru> Message-ID: Got it. Thank you so much! I'll probably do a mix of both by having an install script that parses the output of -V and provide guidance accordingly for the stray cows :D On Jun 29, 2017 7:07 AM, "Maxim Dounin" wrote: Hello! On Thu, Jun 29, 2017 at 12:27:50AM -0700, Joseph Spencer wrote: > I'm looking to create a portable binary, and from everything I can read, it > is almost impossible. The recommended approach seems to be to expose > source code and require users to compile. This is painful because it > requires the source code and gcc to be available: a hard sell for the lazy > sysadmin. > > My goal is to create a proprietary module that is used in conjunction with > a paid service. Users simply install the module and provide access token > credentials. > > As you can imagine it's been really difficult, mainly because practically > *every* configure option is compared at run time. There are two basic approaches you can follow: 1. Distribute a module built with the same configure options and on the same platform as a target nginx binary. This is usually possible as long as you are targeting a particular OS and a package repository. The "nginx -V" output should contain enough information to reproduce the build. 2. Distribute a module built with the `--with-compat` configure option (available since nginx 1.11.5). Such a module will be compatible with any nginx binary built with the `--with-compat` option as well. Since all supported branches (1.13.x mainline, 1.12.x stable) already contain the `--with-compat` option, I would recommend following (2) unless there are specific reasons to support older versions. > I added some logging, and found that the module signature is indeed > embedded in the resulting .so file. I was able to successfully use sed to > get my module to work, but I'm thinking this is an obvious hack not even > worth considering for a production binary: > > sed -i'' > 's|8,4,8,0011111111010111001111111111111111|8,4,8, 0000111111010111001110101111000110|' > ngx_my_custom_module-nginx-1.11.5.so > > Having nginx -V is nice, but it could be beneficial to > expose NGX_MODULE_SIGNATURE somehow. That way I could have an installer > script that checkes to ensure that essential modules are available and > modify the binary after it's been downloaded. I realize this is dangerous, > but I'm not willing to expose source code and require gcc yet. It is not expected to work that way. The signature is to prevent accidental loading of incompatible modules. It is neither expected to be exposed to users, nor modified. Instead, you should built a compatible module based on the "nginx -V" information, notably configure options - either by using the same options, or using `--with-compat`, as suggested above. If the resulting signature do not match, it merely indicates that you've done something wrong while building the module. -- Maxim Dounin http://nginx.org/ _______________________________________________ nginx-devel mailing list nginx-devel at nginx.org http://mailman.nginx.org/mailman/listinfo/nginx-devel -------------- next part -------------- An HTML attachment was scrubbed... URL: From mdounin at mdounin.ru Thu Jun 29 21:08:36 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Fri, 30 Jun 2017 00:08:36 +0300 Subject: [PATCH 1 of 3] PSK: connection support In-Reply-To: <145451D4E6785E4DA4DFA353A58CB7B2015E16FAA8@OLAWPA-EXMB03.ad.garmin.com> References: <145451D4E6785E4DA4DFA353A58CB7B2015E16FAA8@OLAWPA-EXMB03.ad.garmin.com> Message-ID: <20170629210836.GO55433@mdounin.ru> Hello! On Thu, Jun 22, 2017 at 01:24:54PM +0000, Karstens, Nate wrote: > # HG changeset patch > # User Nate Karstens > # Date 1498137180 18000 > # Thu Jun 22 08:13:00 2017 -0500 > # Node ID 3fb3c4928d06029ca1d57853a163c9f56fa90bca > # Parent 6169dbad37d85fa8642b9d0a51f0f0f6c19dd3d1 > PSK: connection support Please use "SSL: " prefix for all SSL-related commits. Please use dot at the end of summary. Overral, this should look like: SSL: PSK cipher suites support. Some additional comments mostly unrelated to the code: - Please add something like [diff] showfunc=1 to your Mercurial's ~/.hgrc to simplify further review. This will ensure that function names are directly visible in patches. - When submitting a patch series, please do so in a single thread using In-Reply-To / References. This will happen automatically when using "hg email". Please also provide references to the previous threads, if any, or use "hg email --in-reply-to " to submit a new / updated series to the same thread. > Adds support for TLS connections using PSK cipher suites. A new > configuration directive, ssl_psk_file, specifies the file that > contains a list of identities and associated PSKs. Each line of > the file begins with the identity, followed by a colon character > (':'), and ending with the PSK. As required by RFC 4279 section > 5.4, PSKs may be entered either as plain text or using hexadecimal > encoding. Hexadecimal PSKs must begin with "{HEX}". PSKs without > this prefix are assumed to be plain text, but they may optionally > begin with "{PLAIN}" to denote this. Some examples: > > gary:plain_text_password > min:{PLAIN}another_text_password > cliff:{HEX}ab0123CD > > The format of the given PSK file is checked at server startup. This checking at server startup looks wrong. As long as the file can be modified at any time, it doesn't provide anything but an additional maintanance problems - as syntax errors can be introduced (and fixed) at any time. Note well that there is another problem with runtime-modifiable secrets file though. Being able to access the file at runtime means that worker process have to able to read it, so the file can't be only readable by root. Given that secrets are not encrypted (in contrast to auth basic passwords, which are hashed and generally non-recoverable even if leaked), this might not be secure enough. It might be actually be a better idea to read all the keys at start as you initially suggested. Not sure though. > > PSK functionality can be easily tested with the OpenSSL s_client > using the "-psk" and "-psk_identity" options. > > Signed-off-by: Nate Karstens > > diff -r 6169dbad37d8 -r 3fb3c4928d06 contrib/vim/syntax/nginx.vim > --- a/contrib/vim/syntax/nginx.vim Wed Jun 14 20:13:41 2017 +0300 > +++ b/contrib/vim/syntax/nginx.vim Thu Jun 22 08:13:00 2017 -0500 > @@ -550,6 +550,7 @@ > syn keyword ngxDirective contained ssl_prefer_server_ciphers > syn keyword ngxDirective contained ssl_preread > syn keyword ngxDirective contained ssl_protocols > +syn keyword ngxDirective contained ssl_psk_file > syn keyword ngxDirective contained ssl_session_cache > syn keyword ngxDirective contained ssl_session_ticket_key > syn keyword ngxDirective contained ssl_session_tickets > diff -r 6169dbad37d8 -r 3fb3c4928d06 src/event/ngx_event_openssl.c > --- a/src/event/ngx_event_openssl.c Wed Jun 14 20:13:41 2017 +0300 > +++ b/src/event/ngx_event_openssl.c Thu Jun 22 08:13:00 2017 -0500 > @@ -11,6 +11,7 @@ > > > #define NGX_SSL_PASSWORD_BUFFER_SIZE 4096 > +#define NGX_SSL_PSK_BUFFER_SIZE 4096 > > > typedef struct { > @@ -23,6 +24,8 @@ > static int ngx_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store); > static void ngx_ssl_info_callback(const ngx_ssl_conn_t *ssl_conn, int where, > int ret); > +static unsigned int ngx_ssl_psk_callback(SSL *ssl, const char *identity, > + unsigned char *psk, unsigned int max_psk_len); > static void ngx_ssl_passwords_cleanup(void *data); > static void ngx_ssl_handshake_handler(ngx_event_t *ev); > static ngx_int_t ngx_ssl_handle_recv(ngx_connection_t *c, int n); > @@ -65,6 +68,11 @@ > #endif > ASN1_TIME *asn1time); > > +static ngx_int_t ngx_ssl_psk_convert_hex(u_char **buf, const u_char *psk, > + const u_char *last, ngx_uint_t line); > +static ngx_int_t ngx_ssl_psk_read(ngx_str_t *file, const char *identity, > + unsigned char *psk, int max_psk_len); > + > static void *ngx_openssl_create_conf(ngx_cycle_t *cycle); > static char *ngx_openssl_engine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); > static void ngx_openssl_exit(ngx_cycle_t *cycle); > @@ -114,6 +122,7 @@ > int ngx_ssl_next_certificate_index; > int ngx_ssl_certificate_name_index; > int ngx_ssl_stapling_index; > +int ngx_ssl_psk_index; > > > ngx_int_t > @@ -225,6 +234,13 @@ > return NGX_ERROR; > } > > + ngx_ssl_psk_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); > + > + if (ngx_ssl_psk_index == -1) { > + ngx_ssl_error(NGX_LOG_ALERT, log, 0, "SSL_get_ex_new_index() failed"); > + return NGX_ERROR; > + } > + > return NGX_OK; > } > > @@ -345,6 +361,7 @@ > SSL_CTX_set_read_ahead(ssl->ctx, 1); > > SSL_CTX_set_info_callback(ssl->ctx, ngx_ssl_info_callback); > + SSL_CTX_set_psk_server_callback(ssl->ctx, ngx_ssl_psk_callback); I would rather avoid using SSL_CTX_set_psk_server_callback() in a function which initializes a generic SSL context. Instead, it would be better to set the callback explicitly when (and if) if a PSK file is provided. Additionally, the SSL_CTX_set_psk_server_callback() function appeared only in OpenSSL 1.0.0. While we are moving away from OpenSSL 0.9.7 support, we are certainly going to support 0.9.8 for some time, so this needs additional preprocessor checks. > > return NGX_OK; > } > @@ -875,6 +892,29 @@ > } > > > +static unsigned int ngx_ssl_psk_callback(SSL *ssl, const char *identity, Style: function name should start at its own line. Style: please use "ngx_ssl_conn_t *ssl_conn" instead of "SSL *ssl" as in other callbacks. > + unsigned char *psk, unsigned int max_psk_len) > +{ > + SSL_CTX *ssl_ctx; > + ngx_str_t *psk_file; > + ngx_int_t psk_len; Style: there should less spaces. > + > + ssl_ctx = SSL_get_SSL_CTX(ssl); > + > + psk_file = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_psk_index); > + if (psk_file == NULL) { > + return 0; > + } > + > + psk_len = ngx_ssl_psk_read(psk_file, identity, psk, max_psk_len); > + if (psk_len < 0) { It is not clear what "psk_len < 0" check is expected to mean. As long as I see, ngx_ssl_psk_read() can either return positive number or NGX_ERROR. Checking explicitly for NGX_ERROR instead might be a better idea. Alternatively, ngx_ssl_psk_read() interface may be changed to return 0 on errors, so it will be possible to simply do return ngx_ssl_psk_read(...); instead. > + return 0; > + } > + > + return psk_len; > +} > + > + > RSA * > ngx_ssl_rsa512_key_callback(ngx_ssl_conn_t *ssl_conn, int is_export, > int key_length) > @@ -3137,6 +3177,24 @@ > #endif > > > +ngx_int_t > +ngx_ssl_psk_file(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file) > + > +{ Style: extra empty line. > + ngx_int_t rc; > + > + if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_psk_index, file) == 0) { > + ngx_ssl_error(NGX_LOG_ALERT, ssl->log, 0, > + "SSL_CTX_set_ex_data() failed"); > + return NGX_ERROR; > + } > + > + rc = ngx_ssl_psk_read(file, NULL, NULL, 0); > + > + return rc == 0 ? NGX_OK : NGX_ERROR; > +} > + > + > void > ngx_ssl_cleanup_ctx(void *data) > { > @@ -4127,6 +4185,223 @@ > } > > > +static ngx_int_t > +ngx_ssl_psk_convert_hex(u_char **buf, const u_char *psk, const u_char *last, > + ngx_uint_t line) Style: there should be 4 spaces on function arguments continuation. Style: a function is expected to be defined after the function which calls it. That is, ngx_ssl_psk_convert_hex() is expected to go after ngx_ssl_psk_read() which calls it. Additionally, both ngx_ssl_psk_convert_hex() and ngx_ssl_psk_read() seems to be badly placed in the file. It should be better to put them right after ngx_ssl_psk_file(). And may be no where ngx_ssl_psk_file() currently placed, but rather after ngx_ssl_ecdh_curve(), following other configuration-related functions. > +{ > + ngx_int_t len; > + u_char *out = NULL; > + u_char val; > + > + if (buf != NULL) { > + *buf = NULL; > + } > + > + if ((last - psk) & 1) { > + len = NGX_ERROR; > + goto error; > + } > + > + len = (last - psk) / 2; > + > + if (buf != NULL) { > + out = ngx_alloc(len, ngx_cycle->log); > + if (out == NULL) { > + return NGX_ERROR; > + } > + *buf = out; > + } There should be no need to allocate any buffers here. > + > + while (psk < last) { > + if (*psk >= '0' && *psk <= '9') { > + val = *psk - '0'; > + } else if (*psk >= 'a' && *psk <= 'f') { > + val = *psk - 'a' + 10; > + } else if (*psk >= 'A' && *psk <= 'F') { > + val = *psk - 'A' + 10; Testing lowercased version as in ngx_hextoi() and many other places might be a better idea. It might be also a good idea to introduce a generic function to decode hex dumps instead of adding a PSK-specific function. > + } else { > + len = NGX_ERROR; > + goto error; > + } > + > + val <<= 4; > + psk++; > + > + if (*psk >= '0' && *psk <= '9') { > + val += *psk - '0'; > + } else if (*psk >= 'a' && *psk <= 'f') { > + val += *psk - 'a' + 10; > + } else if (*psk >= 'A' && *psk <= 'F') { > + val += *psk - 'A' + 10; > + } else { > + len = NGX_ERROR; > + goto error; > + } > + > + psk++; > + > + if (out != NULL) { > + *out = val; > + out++; > + } > + } > + > +error: > + > + if (len == NGX_ERROR) { > + ngx_ssl_error(NGX_LOG_EMERG, ngx_cycle->log, 0, > + "The PSK on line %ui is not valid hex", line); Logging to the cycle's log is generally a bad idea, unless no other options are available. When parsing a PSK file for a given connection it should be possible to use connection log. When parsing a configuration, ssl->log should be available. Also, style of error messages here and in other places is far from the one generally used in nginx. Please avoid uppercase unless it is really needed (as in function names). The use of NGX_LOG_EMERG logging level looks incorrect, as the particular problem can appear at any time and is not fatal. > + if (buf != NULL) { > + ngx_free(*buf); > + *buf = NULL; > + } > + } > + > + return len; > +} > + > + > +static ngx_int_t > +ngx_ssl_psk_read(ngx_str_t *file, const char *identity, unsigned char *psk, > + int max_psk_len) > +{ > + ngx_int_t rc = 0; > + ngx_fd_t fd; > + ngx_uint_t line; > + size_t len; > + ssize_t n; > + u_char *p, *last, *end; > + u_char *psk_ptr, *hex_buf = NULL; > + u_char buf[NGX_SSL_PSK_BUFFER_SIZE]; Style: please avoid initialization in declarations. Style: there should be more spaces. General rule is that there should be 2 spaces between longest type and "*". Style: variables should be ordered from shortest type to longest, with large buffers defined after everything. Style: please avoid duplicate types unless there are specific reasons to. So this should look like: u_char *p, *last, *end, *psk_ptr, *hex_buf; size_t len; ssize_t n; ngx_fd_t fd; ngx_int_t rc; ngx_uint_t line; u_char buf[NGX_SSL_PSK_BUFFER_SIZE]; > + > + static const char plain_tag[7] = "{PLAIN}"; > + static const char hex_tag[5] = "{HEX}"; I would rather avoid such constructions. Instead, please either use strings directly like in src/core/ngx_crypt.c, or introduce appropriate string defines. > + > + fd = ngx_open_file(file->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0); > + if (fd == NGX_INVALID_FILE) { > + ngx_ssl_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno, > + ngx_open_file_n " \"%V\" failed", file); > + return NGX_ERROR; > + } > + > + len = 0; > + last = buf; > + > + do { > + n = ngx_read_fd(fd, last, NGX_SSL_PSK_BUFFER_SIZE - len); > + > + if (n == -1) { > + ngx_ssl_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno, > + ngx_read_fd_n " \"%V\" failed", file); > + rc = NGX_ERROR; > + goto done; > + } > + > + end = last + n; > + > + if (len && n == 0) { > + *end++ = LF; > + } > + > + for (p = buf, line = 1; ; p = last, line++) { Style: if a condition is omitted, it should be explicitly indicated using /* void */ comment. Overral, this construction looks unreadable. It might be a better idea to simplify the code and either make it more similar to the ngx_ssl_read_password_file() you've used as a prototype, or to make it use the code similar to the one in ngx_http_auth_basic_module.c instead. > + last = ngx_strlchr(last, end, LF); > + > + if (last == NULL) { > + break; > + } > + > + len = last++ - p; > + > + if (len && p[len - 1] == CR) { > + len--; > + } > + > + if (len == 0) { > + continue; > + } > + > + psk_ptr = (u_char *) ngx_strlchr(p, p + len, ':'); > + if (psk_ptr == NULL) { > + ngx_ssl_error(NGX_LOG_EMERG, ngx_cycle->log, 0, > + "Bad format on line %ui of PSK file \"%V\"", > + line, file); > + rc = NGX_ERROR; > + goto done; > + } > + > + *psk_ptr = '\0'; > + psk_ptr++; > + > + if (identity == NULL) { > + if (ngx_memcmp(psk_ptr, hex_tag, sizeof(hex_tag)) == 0) { > + psk_ptr += sizeof(hex_tag); > + if (ngx_ssl_psk_convert_hex(NULL, psk_ptr, p + len, line) > + == NGX_ERROR) > + { > + rc = NGX_ERROR; > + goto done; > + } > + } > + > + continue; > + } Duplicate code under "if (identity == NULL)" might not be a good idea. Instead, it should be beneficial to use the same code as in normal case, with additional checks after it. Also, the whole idea about testing the file on start looks wrong, see above. > + > + if (ngx_strcmp(p, identity) != 0) { > + continue; > + } > + > + if (ngx_memcmp(psk_ptr, plain_tag, sizeof(plain_tag)) == 0) { > + psk_ptr += sizeof(plain_tag); > + rc = last - 1 - psk_ptr; It might not be a good idea to use ngx_memcmp() unless you are sure both arguments have at least specified number of bytes available. Using ngx_strncmp() might be a better idea. > + } else if (ngx_memcmp(psk_ptr, hex_tag, sizeof(hex_tag)) == 0) { > + psk_ptr += sizeof(hex_tag); > + rc = ngx_ssl_psk_convert_hex(&hex_buf, psk_ptr, p + len, line); > + if (rc == NGX_ERROR) { > + goto done; > + } > + psk_ptr = hex_buf; > + } else { > + rc = last - 1 - psk_ptr; > + } > + > + if (rc > max_psk_len) { > + rc = 0; > + goto done; > + } > + > + ngx_memcpy(psk, psk_ptr, rc); > + goto done; > + } > + > + len = end - p; > + > + if (len == NGX_SSL_PSK_BUFFER_SIZE) { > + ngx_ssl_error(NGX_LOG_EMERG, ngx_cycle->log, 0, > + "too long line in \"%V\"", file); > + rc = NGX_ERROR; > + goto done; > + } > + > + ngx_memmove(buf, p, len); > + last = buf + len; > + > + } while (n != 0); > + > +done: > + > + if (ngx_close_file(fd) == NGX_FILE_ERROR) { > + ngx_ssl_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno, > + ngx_close_file_n " %V failed", file); > + rc = NGX_ERROR; > + } > + > + ngx_memzero(buf, NGX_SSL_PSK_BUFFER_SIZE); > + ngx_free(hex_buf); > + > + return rc; > +} > + > + > static void * > ngx_openssl_create_conf(ngx_cycle_t *cycle) > { > diff -r 6169dbad37d8 -r 3fb3c4928d06 src/event/ngx_event_openssl.h > --- a/src/event/ngx_event_openssl.h Wed Jun 14 20:13:41 2017 +0300 > +++ b/src/event/ngx_event_openssl.h Thu Jun 22 08:13:00 2017 -0500 > @@ -171,6 +171,7 @@ > ssize_t builtin_session_cache, ngx_shm_zone_t *shm_zone, time_t timeout); > ngx_int_t ngx_ssl_session_ticket_keys(ngx_conf_t *cf, ngx_ssl_t *ssl, > ngx_array_t *paths); > +ngx_int_t ngx_ssl_psk_file(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file); > ngx_int_t ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, void *data); > ngx_int_t ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c, > ngx_uint_t flags); > @@ -255,6 +256,7 @@ > extern int ngx_ssl_next_certificate_index; > extern int ngx_ssl_certificate_name_index; > extern int ngx_ssl_stapling_index; > +extern int ngx_ssl_psk_index; > > > #endif /* _NGX_EVENT_OPENSSL_H_INCLUDED_ */ > diff -r 6169dbad37d8 -r 3fb3c4928d06 src/http/modules/ngx_http_ssl_module.c > --- a/src/http/modules/ngx_http_ssl_module.c Wed Jun 14 20:13:41 2017 +0300 > +++ b/src/http/modules/ngx_http_ssl_module.c Thu Jun 22 08:13:00 2017 -0500 > @@ -234,6 +234,13 @@ > offsetof(ngx_http_ssl_srv_conf_t, stapling_verify), > NULL }, > > + { ngx_string("ssl_psk_file"), > + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, > + ngx_conf_set_str_slot, > + NGX_HTTP_SRV_CONF_OFFSET, > + offsetof(ngx_http_ssl_srv_conf_t, psk_file), > + NULL }, > + > ngx_null_command > }; > > @@ -539,6 +546,7 @@ > * sscf->shm_zone = NULL; > * sscf->stapling_file = { 0, NULL }; > * sscf->stapling_responder = { 0, NULL }; > + * sscf->psk_file = { 0, NULL }; > */ > > sscf->enable = NGX_CONF_UNSET; > @@ -620,6 +628,8 @@ > ngx_conf_merge_str_value(conf->stapling_responder, > prev->stapling_responder, ""); > > + ngx_conf_merge_str_value(conf->psk_file, prev->psk_file, ""); > + > conf->ssl.log = cf->log; > > if (conf->enable) { > @@ -800,6 +810,12 @@ > > } > > + if (ngx_ssl_psk_file(cf, &conf->ssl, &conf->psk_file) > + != NGX_OK) > + { > + return NGX_CONF_ERROR; > + } > + > return NGX_CONF_OK; > } > > diff -r 6169dbad37d8 -r 3fb3c4928d06 src/http/modules/ngx_http_ssl_module.h > --- a/src/http/modules/ngx_http_ssl_module.h Wed Jun 14 20:13:41 2017 +0300 > +++ b/src/http/modules/ngx_http_ssl_module.h Thu Jun 22 08:13:00 2017 -0500 > @@ -55,6 +55,8 @@ > ngx_str_t stapling_file; > ngx_str_t stapling_responder; > > + ngx_str_t psk_file; > + > u_char *file; > ngx_uint_t line; > } ngx_http_ssl_srv_conf_t; > > ________________________________ > > CONFIDENTIALITY NOTICE: This email and any attachments are for the sole use of the intended recipient(s) and contain information that may be Garmin confidential and/or Garmin legally privileged. If you have received this email in error, please notify the sender by reply email and delete the message. Any disclosure, copying, distribution or use of this communication (including attachments) by someone other than the intended recipient is prohibited. Thank you. > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel -- Maxim Dounin http://nginx.org/ From mdounin at mdounin.ru Thu Jun 29 21:19:29 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Fri, 30 Jun 2017 00:19:29 +0300 Subject: [PATCH 2 of 3] PSK: add PSK identity variable In-Reply-To: <145451D4E6785E4DA4DFA353A58CB7B2015E16FAB4@OLAWPA-EXMB03.ad.garmin.com> References: <145451D4E6785E4DA4DFA353A58CB7B2015E16FAB4@OLAWPA-EXMB03.ad.garmin.com> Message-ID: <20170629211929.GP55433@mdounin.ru> Hello! On Thu, Jun 22, 2017 at 01:24:57PM +0000, Karstens, Nate wrote: > # HG changeset patch > # User Nate Karstens > # Date 1498137207 18000 > # Thu Jun 22 08:13:27 2017 -0500 > # Node ID a4635fa4a0cabf5312cda617b8010ea14279ab1c > # Parent 3fb3c4928d06029ca1d57853a163c9f56fa90bca > PSK: add PSK identity variable Style: "SSL: " prefix, dot. > > Adds the variable $ssl_psk_identity to get the PSK identity > used in a connnection secured with a PSK cipher suite. > > Signed-off-by: Nate Karstens > > diff -r 3fb3c4928d06 -r a4635fa4a0ca src/event/ngx_event_openssl.c > --- a/src/event/ngx_event_openssl.c Thu Jun 22 08:13:00 2017 -0500 > +++ b/src/event/ngx_event_openssl.c Thu Jun 22 08:13:27 2017 -0500 > @@ -4147,6 +4147,33 @@ > } > > > +ngx_int_t > +ngx_ssl_get_psk_identity(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s) > +{ > + const char *identity; > + size_t len; Style: two spaces between type and "*", order from short type to long type. > + > + identity = SSL_get_psk_identity(c->ssl->connection); > + > + if (identity == NULL) { > + s->len = 0; > + return NGX_OK; > + } > + > + len = ngx_strlen(identity); > + > + s->data = ngx_pnalloc(pool, len); > + if (s->data == NULL) { > + return NGX_ERROR; > + } > + > + ngx_memcpy(s->data, identity, len); > + s->len = len; Is the allocation of additional buffer is needed here? For example, in ngx_ssl_get_server_name() we simply use the memory returned. > + > + return NGX_OK; > +} > + > + > static time_t > ngx_ssl_parse_time( > #if OPENSSL_VERSION_NUMBER > 0x10100000L > diff -r 3fb3c4928d06 -r a4635fa4a0ca src/event/ngx_event_openssl.h > --- a/src/event/ngx_event_openssl.h Thu Jun 22 08:13:00 2017 -0500 > +++ b/src/event/ngx_event_openssl.h Thu Jun 22 08:13:27 2017 -0500 > @@ -233,6 +233,8 @@ > ngx_str_t *s); > ngx_int_t ngx_ssl_get_client_v_remain(ngx_connection_t *c, ngx_pool_t *pool, > ngx_str_t *s); > +ngx_int_t ngx_ssl_get_psk_identity(ngx_connection_t *c, ngx_pool_t *pool, > + ngx_str_t *s); > > > ngx_int_t ngx_ssl_handshake(ngx_connection_t *c); > diff -r 3fb3c4928d06 -r a4635fa4a0ca src/http/modules/ngx_http_ssl_module.c > --- a/src/http/modules/ngx_http_ssl_module.c Thu Jun 22 08:13:00 2017 -0500 > +++ b/src/http/modules/ngx_http_ssl_module.c Thu Jun 22 08:13:27 2017 -0500 > @@ -336,6 +336,9 @@ > { ngx_string("ssl_client_v_remain"), NULL, ngx_http_ssl_variable, > (uintptr_t) ngx_ssl_get_client_v_remain, NGX_HTTP_VAR_CHANGEABLE, 0 }, > > + { ngx_string("ssl_psk_identity"), NULL, ngx_http_ssl_variable, > + (uintptr_t) ngx_ssl_get_psk_identity, NGX_HTTP_VAR_CHANGEABLE, 0 }, > + > { ngx_null_string, NULL, NULL, 0, 0, 0 } > }; > > > ________________________________ > > CONFIDENTIALITY NOTICE: This email and any attachments are for the sole use of the intended recipient(s) and contain information that may be Garmin confidential and/or Garmin legally privileged. If you have received this email in error, please notify the sender by reply email and delete the message. Any disclosure, copying, distribution or use of this communication (including attachments) by someone other than the intended recipient is prohibited. Thank you. > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel -- Maxim Dounin http://nginx.org/ From mdounin at mdounin.ru Thu Jun 29 21:21:51 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Fri, 30 Jun 2017 00:21:51 +0300 Subject: [PATCH 3 of 3] PSK: add identity hint config directive In-Reply-To: <145451D4E6785E4DA4DFA353A58CB7B2015E16FABB@OLAWPA-EXMB03.ad.garmin.com> References: <145451D4E6785E4DA4DFA353A58CB7B2015E16FABB@OLAWPA-EXMB03.ad.garmin.com> Message-ID: <20170629212151.GQ55433@mdounin.ru> Hello! On Thu, Jun 22, 2017 at 01:24:59PM +0000, Karstens, Nate wrote: > # HG changeset patch > # User Nate Karstens > # Date 1498137243 18000 > # Thu Jun 22 08:14:03 2017 -0500 > # Node ID b706695658216c88716904519467a36c1aac7ac9 > # Parent a4635fa4a0cabf5312cda617b8010ea14279ab1c > PSK: add identity hint config directive > > Adds the directive "ssl_psk_identity_hint" to the ngx_http_ssl_module. > This allows the user to specify the PSK identity hint given to the > connecting client. > > Signed-off-by: Nate Karstens > > diff -r a4635fa4a0ca -r b70669565821 contrib/vim/syntax/nginx.vim > --- a/contrib/vim/syntax/nginx.vim Thu Jun 22 08:13:27 2017 -0500 > +++ b/contrib/vim/syntax/nginx.vim Thu Jun 22 08:14:03 2017 -0500 > @@ -551,6 +551,7 @@ > syn keyword ngxDirective contained ssl_preread > syn keyword ngxDirective contained ssl_protocols > syn keyword ngxDirective contained ssl_psk_file > +syn keyword ngxDirective contained ssl_psk_identity_hint > syn keyword ngxDirective contained ssl_session_cache > syn keyword ngxDirective contained ssl_session_ticket_key > syn keyword ngxDirective contained ssl_session_tickets > diff -r a4635fa4a0ca -r b70669565821 src/http/modules/ngx_http_ssl_module.c > --- a/src/http/modules/ngx_http_ssl_module.c Thu Jun 22 08:13:27 2017 -0500 > +++ b/src/http/modules/ngx_http_ssl_module.c Thu Jun 22 08:14:03 2017 -0500 > @@ -241,6 +241,13 @@ > offsetof(ngx_http_ssl_srv_conf_t, psk_file), > NULL }, > > + { ngx_string("ssl_psk_identity_hint"), > + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, > + ngx_conf_set_str_slot, > + NGX_HTTP_SRV_CONF_OFFSET, > + offsetof(ngx_http_ssl_srv_conf_t, psk_identity_hint), > + NULL }, > + > ngx_null_command > }; > > @@ -550,6 +557,7 @@ > * sscf->stapling_file = { 0, NULL }; > * sscf->stapling_responder = { 0, NULL }; > * sscf->psk_file = { 0, NULL }; > + * sscf->psk_identity_hint = { 0, NULL }; > */ > > sscf->enable = NGX_CONF_UNSET; > @@ -632,6 +640,7 @@ > prev->stapling_responder, ""); > > ngx_conf_merge_str_value(conf->psk_file, prev->psk_file, ""); > + ngx_conf_merge_str_value(conf->psk_identity_hint, prev->psk_identity_hint, ""); > > conf->ssl.log = cf->log; > > @@ -819,6 +828,15 @@ > return NGX_CONF_ERROR; > } > > + if (conf->psk_identity_hint.len != 0) { > + if (SSL_CTX_use_psk_identity_hint(conf->ssl.ctx, > + (char *) conf->psk_identity_hint.data) > + != 1) > + { > + return NGX_CONF_ERROR; > + } > + } Please avoid direct calls of SSL library functions. Instead, please pass this as a prameter to ngx_ssl_psk_file(), and use appropriate library calls there. > + > return NGX_CONF_OK; > } > > diff -r a4635fa4a0ca -r b70669565821 src/http/modules/ngx_http_ssl_module.h > --- a/src/http/modules/ngx_http_ssl_module.h Thu Jun 22 08:13:27 2017 -0500 > +++ b/src/http/modules/ngx_http_ssl_module.h Thu Jun 22 08:14:03 2017 -0500 > @@ -56,6 +56,7 @@ > ngx_str_t stapling_responder; > > ngx_str_t psk_file; > + ngx_str_t psk_identity_hint; > > u_char *file; > ngx_uint_t line; > > ________________________________ > > CONFIDENTIALITY NOTICE: This email and any attachments are for the sole use of the intended recipient(s) and contain information that may be Garmin confidential and/or Garmin legally privileged. If you have received this email in error, please notify the sender by reply email and delete the message. Any disclosure, copying, distribution or use of this communication (including attachments) by someone other than the intended recipient is prohibited. Thank you. > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel -- Maxim Dounin http://nginx.org/ From Nate.Karstens at garmin.com Thu Jun 29 22:00:45 2017 From: Nate.Karstens at garmin.com (Karstens, Nate) Date: Thu, 29 Jun 2017 22:00:45 +0000 Subject: [PATCH 1 of 3] PSK: connection support In-Reply-To: <20170629210836.GO55433@mdounin.ru> References: <145451D4E6785E4DA4DFA353A58CB7B2015E16FAA8@OLAWPA-EXMB03.ad.garmin.com> <20170629210836.GO55433@mdounin.ru> Message-ID: <145451D4E6785E4DA4DFA353A58CB7B2015E17251F@OLAWPA-EXMB03.ad.garmin.com> Maxim, Thanks for the comments. I'll try to start on those in a couple of days. My company uses Outlook/Exchange for email, so I don't think I'll be able to use hg email, do you have any other suggestions? Thanks also for your patience, I've used Git quite a bit but am new to Mercurial. Utkarsh sounds like he is trying to use PSK for TLS v1.3 session resumption. Given that each TLS connection could potentially result in a new PSK I think only reading them at startup could result in too many refreshes. I think there might be some benefit to the original approach in regards to storing each PSK in its own file in a designated directory. Benefits include: 1) We can utilize the file system to avoid name collisions. With TLS v1.3 session resumption the PSK identity doesn't have to be user-friendly, so something like mkstemp() could be used to generate the identity/filename. 2) Files include metadata about when they were created, so you could have a cron job that periodically cleans up old entries. Without this you have to maintain creation time in a separate database. 3) No need to worry about hex vs. text -- the contents of the file are the PSK and can be plain text or binary data as appropriate. Utkarsh -- do you have any thoughts on the preferred way to set up the PSKs? Nate -----Original Message----- From: nginx-devel [mailto:nginx-devel-bounces at nginx.org] On Behalf Of Maxim Dounin Sent: Thursday, June 29, 2017 4:09 PM To: nginx-devel at nginx.org Subject: Re: [PATCH 1 of 3] PSK: connection support Hello! On Thu, Jun 22, 2017 at 01:24:54PM +0000, Karstens, Nate wrote: > # HG changeset patch > # User Nate Karstens # Date 1498137180 > 18000 > # Thu Jun 22 08:13:00 2017 -0500 > # Node ID 3fb3c4928d06029ca1d57853a163c9f56fa90bca > # Parent 6169dbad37d85fa8642b9d0a51f0f0f6c19dd3d1 > PSK: connection support Please use "SSL: " prefix for all SSL-related commits. Please use dot at the end of summary. Overral, this should look like: SSL: PSK cipher suites support. Some additional comments mostly unrelated to the code: - Please add something like [diff] showfunc=1 to your Mercurial's ~/.hgrc to simplify further review. This will ensure that function names are directly visible in patches. - When submitting a patch series, please do so in a single thread using In-Reply-To / References. This will happen automatically when using "hg email". Please also provide references to the previous threads, if any, or use "hg email --in-reply-to " to submit a new / updated series to the same thread. > Adds support for TLS connections using PSK cipher suites. A new > configuration directive, ssl_psk_file, specifies the file that > contains a list of identities and associated PSKs. Each line of the > file begins with the identity, followed by a colon character (':'), > and ending with the PSK. As required by RFC 4279 section 5.4, PSKs may > be entered either as plain text or using hexadecimal encoding. > Hexadecimal PSKs must begin with "{HEX}". PSKs without this prefix are > assumed to be plain text, but they may optionally begin with "{PLAIN}" > to denote this. Some examples: > > gary:plain_text_password > min:{PLAIN}another_text_password > cliff:{HEX}ab0123CD > > The format of the given PSK file is checked at server startup. This checking at server startup looks wrong. As long as the file can be modified at any time, it doesn't provide anything but an additional maintanance problems - as syntax errors can be introduced (and fixed) at any time. Note well that there is another problem with runtime-modifiable secrets file though. Being able to access the file at runtime means that worker process have to able to read it, so the file can't be only readable by root. Given that secrets are not encrypted (in contrast to auth basic passwords, which are hashed and generally non-recoverable even if leaked), this might not be secure enough. It might be actually be a better idea to read all the keys at start as you initially suggested. Not sure though. > > PSK functionality can be easily tested with the OpenSSL s_client using > the "-psk" and "-psk_identity" options. > > Signed-off-by: Nate Karstens > > diff -r 6169dbad37d8 -r 3fb3c4928d06 contrib/vim/syntax/nginx.vim > --- a/contrib/vim/syntax/nginx.vim Wed Jun 14 20:13:41 2017 +0300 > +++ b/contrib/vim/syntax/nginx.vim Thu Jun 22 08:13:00 2017 -0500 > @@ -550,6 +550,7 @@ > syn keyword ngxDirective contained ssl_prefer_server_ciphers syn > keyword ngxDirective contained ssl_preread syn keyword ngxDirective > contained ssl_protocols > +syn keyword ngxDirective contained ssl_psk_file > syn keyword ngxDirective contained ssl_session_cache syn keyword > ngxDirective contained ssl_session_ticket_key syn keyword > ngxDirective contained ssl_session_tickets diff -r 6169dbad37d8 -r > 3fb3c4928d06 src/event/ngx_event_openssl.c > --- a/src/event/ngx_event_openssl.c Wed Jun 14 20:13:41 2017 +0300 > +++ b/src/event/ngx_event_openssl.c Thu Jun 22 08:13:00 2017 -0500 > @@ -11,6 +11,7 @@ > > > #define NGX_SSL_PASSWORD_BUFFER_SIZE 4096 > +#define NGX_SSL_PSK_BUFFER_SIZE 4096 > > > typedef struct { > @@ -23,6 +24,8 @@ > static int ngx_ssl_verify_callback(int ok, X509_STORE_CTX > *x509_store); static void ngx_ssl_info_callback(const ngx_ssl_conn_t *ssl_conn, int where, > int ret); > +static unsigned int ngx_ssl_psk_callback(SSL *ssl, const char *identity, > + unsigned char *psk, unsigned int max_psk_len); > static void ngx_ssl_passwords_cleanup(void *data); static void > ngx_ssl_handshake_handler(ngx_event_t *ev); static ngx_int_t > ngx_ssl_handle_recv(ngx_connection_t *c, int n); @@ -65,6 +68,11 @@ > #endif > ASN1_TIME *asn1time); > > +static ngx_int_t ngx_ssl_psk_convert_hex(u_char **buf, const u_char *psk, > + const u_char *last, ngx_uint_t line); static ngx_int_t > +ngx_ssl_psk_read(ngx_str_t *file, const char *identity, > + unsigned char *psk, int max_psk_len); > + > static void *ngx_openssl_create_conf(ngx_cycle_t *cycle); static > char *ngx_openssl_engine(ngx_conf_t *cf, ngx_command_t *cmd, void > *conf); static void ngx_openssl_exit(ngx_cycle_t *cycle); @@ -114,6 > +122,7 @@ int ngx_ssl_next_certificate_index; int > ngx_ssl_certificate_name_index; int ngx_ssl_stapling_index; > +int ngx_ssl_psk_index; > > > ngx_int_t > @@ -225,6 +234,13 @@ > return NGX_ERROR; > } > > + ngx_ssl_psk_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, > + NULL); > + > + if (ngx_ssl_psk_index == -1) { > + ngx_ssl_error(NGX_LOG_ALERT, log, 0, "SSL_get_ex_new_index() failed"); > + return NGX_ERROR; > + } > + > return NGX_OK; > } > > @@ -345,6 +361,7 @@ > SSL_CTX_set_read_ahead(ssl->ctx, 1); > > SSL_CTX_set_info_callback(ssl->ctx, ngx_ssl_info_callback); > + SSL_CTX_set_psk_server_callback(ssl->ctx, ngx_ssl_psk_callback); I would rather avoid using SSL_CTX_set_psk_server_callback() in a function which initializes a generic SSL context. Instead, it would be better to set the callback explicitly when (and if) if a PSK file is provided. Additionally, the SSL_CTX_set_psk_server_callback() function appeared only in OpenSSL 1.0.0. While we are moving away from OpenSSL 0.9.7 support, we are certainly going to support 0.9.8 for some time, so this needs additional preprocessor checks. > > return NGX_OK; > } > @@ -875,6 +892,29 @@ > } > > > +static unsigned int ngx_ssl_psk_callback(SSL *ssl, const char *identity, Style: function name should start at its own line. Style: please use "ngx_ssl_conn_t *ssl_conn" instead of "SSL *ssl" as in other callbacks. > + unsigned char *psk, unsigned int max_psk_len) > +{ > + SSL_CTX *ssl_ctx; > + ngx_str_t *psk_file; > + ngx_int_t psk_len; Style: there should less spaces. > + > + ssl_ctx = SSL_get_SSL_CTX(ssl); > + > + psk_file = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_psk_index); > + if (psk_file == NULL) { > + return 0; > + } > + > + psk_len = ngx_ssl_psk_read(psk_file, identity, psk, max_psk_len); > + if (psk_len < 0) { It is not clear what "psk_len < 0" check is expected to mean. As long as I see, ngx_ssl_psk_read() can either return positive number or NGX_ERROR. Checking explicitly for NGX_ERROR instead might be a better idea. Alternatively, ngx_ssl_psk_read() interface may be changed to return 0 on errors, so it will be possible to simply do return ngx_ssl_psk_read(...); instead. > + return 0; > + } > + > + return psk_len; > +} > + > + > RSA * > ngx_ssl_rsa512_key_callback(ngx_ssl_conn_t *ssl_conn, int is_export, > int key_length) > @@ -3137,6 +3177,24 @@ > #endif > > > +ngx_int_t > +ngx_ssl_psk_file(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file) > + > +{ Style: extra empty line. > + ngx_int_t rc; > + > + if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_psk_index, file) == 0) { > + ngx_ssl_error(NGX_LOG_ALERT, ssl->log, 0, > + "SSL_CTX_set_ex_data() failed"); > + return NGX_ERROR; > + } > + > + rc = ngx_ssl_psk_read(file, NULL, NULL, 0); > + > + return rc == 0 ? NGX_OK : NGX_ERROR; > +} > + > + > void > ngx_ssl_cleanup_ctx(void *data) > { > @@ -4127,6 +4185,223 @@ > } > > > +static ngx_int_t > +ngx_ssl_psk_convert_hex(u_char **buf, const u_char *psk, const u_char *last, > + ngx_uint_t line) Style: there should be 4 spaces on function arguments continuation. Style: a function is expected to be defined after the function which calls it. That is, ngx_ssl_psk_convert_hex() is expected to go after ngx_ssl_psk_read() which calls it. Additionally, both ngx_ssl_psk_convert_hex() and ngx_ssl_psk_read() seems to be badly placed in the file. It should be better to put them right after ngx_ssl_psk_file(). And may be no where ngx_ssl_psk_file() currently placed, but rather after ngx_ssl_ecdh_curve(), following other configuration-related functions. > +{ > + ngx_int_t len; > + u_char *out = NULL; > + u_char val; > + > + if (buf != NULL) { > + *buf = NULL; > + } > + > + if ((last - psk) & 1) { > + len = NGX_ERROR; > + goto error; > + } > + > + len = (last - psk) / 2; > + > + if (buf != NULL) { > + out = ngx_alloc(len, ngx_cycle->log); > + if (out == NULL) { > + return NGX_ERROR; > + } > + *buf = out; > + } There should be no need to allocate any buffers here. > + > + while (psk < last) { > + if (*psk >= '0' && *psk <= '9') { > + val = *psk - '0'; > + } else if (*psk >= 'a' && *psk <= 'f') { > + val = *psk - 'a' + 10; > + } else if (*psk >= 'A' && *psk <= 'F') { > + val = *psk - 'A' + 10; Testing lowercased version as in ngx_hextoi() and many other places might be a better idea. It might be also a good idea to introduce a generic function to decode hex dumps instead of adding a PSK-specific function. > + } else { > + len = NGX_ERROR; > + goto error; > + } > + > + val <<= 4; > + psk++; > + > + if (*psk >= '0' && *psk <= '9') { > + val += *psk - '0'; > + } else if (*psk >= 'a' && *psk <= 'f') { > + val += *psk - 'a' + 10; > + } else if (*psk >= 'A' && *psk <= 'F') { > + val += *psk - 'A' + 10; > + } else { > + len = NGX_ERROR; > + goto error; > + } > + > + psk++; > + > + if (out != NULL) { > + *out = val; > + out++; > + } > + } > + > +error: > + > + if (len == NGX_ERROR) { > + ngx_ssl_error(NGX_LOG_EMERG, ngx_cycle->log, 0, > + "The PSK on line %ui is not valid hex", line); Logging to the cycle's log is generally a bad idea, unless no other options are available. When parsing a PSK file for a given connection it should be possible to use connection log. When parsing a configuration, ssl->log should be available. Also, style of error messages here and in other places is far from the one generally used in nginx. Please avoid uppercase unless it is really needed (as in function names). The use of NGX_LOG_EMERG logging level looks incorrect, as the particular problem can appear at any time and is not fatal. > + if (buf != NULL) { > + ngx_free(*buf); > + *buf = NULL; > + } > + } > + > + return len; > +} > + > + > +static ngx_int_t > +ngx_ssl_psk_read(ngx_str_t *file, const char *identity, unsigned char *psk, > + int max_psk_len) > +{ > + ngx_int_t rc = 0; > + ngx_fd_t fd; > + ngx_uint_t line; > + size_t len; > + ssize_t n; > + u_char *p, *last, *end; > + u_char *psk_ptr, *hex_buf = NULL; > + u_char buf[NGX_SSL_PSK_BUFFER_SIZE]; Style: please avoid initialization in declarations. Style: there should be more spaces. General rule is that there should be 2 spaces between longest type and "*". Style: variables should be ordered from shortest type to longest, with large buffers defined after everything. Style: please avoid duplicate types unless there are specific reasons to. So this should look like: u_char *p, *last, *end, *psk_ptr, *hex_buf; size_t len; ssize_t n; ngx_fd_t fd; ngx_int_t rc; ngx_uint_t line; u_char buf[NGX_SSL_PSK_BUFFER_SIZE]; > + > + static const char plain_tag[7] = "{PLAIN}"; > + static const char hex_tag[5] = "{HEX}"; I would rather avoid such constructions. Instead, please either use strings directly like in src/core/ngx_crypt.c, or introduce appropriate string defines. > + > + fd = ngx_open_file(file->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0); > + if (fd == NGX_INVALID_FILE) { > + ngx_ssl_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno, > + ngx_open_file_n " \"%V\" failed", file); > + return NGX_ERROR; > + } > + > + len = 0; > + last = buf; > + > + do { > + n = ngx_read_fd(fd, last, NGX_SSL_PSK_BUFFER_SIZE - len); > + > + if (n == -1) { > + ngx_ssl_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno, > + ngx_read_fd_n " \"%V\" failed", file); > + rc = NGX_ERROR; > + goto done; > + } > + > + end = last + n; > + > + if (len && n == 0) { > + *end++ = LF; > + } > + > + for (p = buf, line = 1; ; p = last, line++) { Style: if a condition is omitted, it should be explicitly indicated using /* void */ comment. Overral, this construction looks unreadable. It might be a better idea to simplify the code and either make it more similar to the ngx_ssl_read_password_file() you've used as a prototype, or to make it use the code similar to the one in ngx_http_auth_basic_module.c instead. > + last = ngx_strlchr(last, end, LF); > + > + if (last == NULL) { > + break; > + } > + > + len = last++ - p; > + > + if (len && p[len - 1] == CR) { > + len--; > + } > + > + if (len == 0) { > + continue; > + } > + > + psk_ptr = (u_char *) ngx_strlchr(p, p + len, ':'); > + if (psk_ptr == NULL) { > + ngx_ssl_error(NGX_LOG_EMERG, ngx_cycle->log, 0, > + "Bad format on line %ui of PSK file \"%V\"", > + line, file); > + rc = NGX_ERROR; > + goto done; > + } > + > + *psk_ptr = '\0'; > + psk_ptr++; > + > + if (identity == NULL) { > + if (ngx_memcmp(psk_ptr, hex_tag, sizeof(hex_tag)) == 0) { > + psk_ptr += sizeof(hex_tag); > + if (ngx_ssl_psk_convert_hex(NULL, psk_ptr, p + len, line) > + == NGX_ERROR) > + { > + rc = NGX_ERROR; > + goto done; > + } > + } > + > + continue; > + } Duplicate code under "if (identity == NULL)" might not be a good idea. Instead, it should be beneficial to use the same code as in normal case, with additional checks after it. Also, the whole idea about testing the file on start looks wrong, see above. > + > + if (ngx_strcmp(p, identity) != 0) { > + continue; > + } > + > + if (ngx_memcmp(psk_ptr, plain_tag, sizeof(plain_tag)) == 0) { > + psk_ptr += sizeof(plain_tag); > + rc = last - 1 - psk_ptr; It might not be a good idea to use ngx_memcmp() unless you are sure both arguments have at least specified number of bytes available. Using ngx_strncmp() might be a better idea. > + } else if (ngx_memcmp(psk_ptr, hex_tag, sizeof(hex_tag)) == 0) { > + psk_ptr += sizeof(hex_tag); > + rc = ngx_ssl_psk_convert_hex(&hex_buf, psk_ptr, p + len, line); > + if (rc == NGX_ERROR) { > + goto done; > + } > + psk_ptr = hex_buf; > + } else { > + rc = last - 1 - psk_ptr; > + } > + > + if (rc > max_psk_len) { > + rc = 0; > + goto done; > + } > + > + ngx_memcpy(psk, psk_ptr, rc); > + goto done; > + } > + > + len = end - p; > + > + if (len == NGX_SSL_PSK_BUFFER_SIZE) { > + ngx_ssl_error(NGX_LOG_EMERG, ngx_cycle->log, 0, > + "too long line in \"%V\"", file); > + rc = NGX_ERROR; > + goto done; > + } > + > + ngx_memmove(buf, p, len); > + last = buf + len; > + > + } while (n != 0); > + > +done: > + > + if (ngx_close_file(fd) == NGX_FILE_ERROR) { > + ngx_ssl_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno, > + ngx_close_file_n " %V failed", file); > + rc = NGX_ERROR; > + } > + > + ngx_memzero(buf, NGX_SSL_PSK_BUFFER_SIZE); > + ngx_free(hex_buf); > + > + return rc; > +} > + > + > static void * > ngx_openssl_create_conf(ngx_cycle_t *cycle) > { > diff -r 6169dbad37d8 -r 3fb3c4928d06 src/event/ngx_event_openssl.h > --- a/src/event/ngx_event_openssl.h Wed Jun 14 20:13:41 2017 +0300 > +++ b/src/event/ngx_event_openssl.h Thu Jun 22 08:13:00 2017 -0500 > @@ -171,6 +171,7 @@ > ssize_t builtin_session_cache, ngx_shm_zone_t *shm_zone, time_t timeout); > ngx_int_t ngx_ssl_session_ticket_keys(ngx_conf_t *cf, ngx_ssl_t *ssl, > ngx_array_t *paths); > +ngx_int_t ngx_ssl_psk_file(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file); > ngx_int_t ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, void *data); > ngx_int_t ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c, > ngx_uint_t flags); > @@ -255,6 +256,7 @@ > extern int ngx_ssl_next_certificate_index; > extern int ngx_ssl_certificate_name_index; > extern int ngx_ssl_stapling_index; > +extern int ngx_ssl_psk_index; > > > #endif /* _NGX_EVENT_OPENSSL_H_INCLUDED_ */ > diff -r 6169dbad37d8 -r 3fb3c4928d06 src/http/modules/ngx_http_ssl_module.c > --- a/src/http/modules/ngx_http_ssl_module.c Wed Jun 14 20:13:41 2017 +0300 > +++ b/src/http/modules/ngx_http_ssl_module.c Thu Jun 22 08:13:00 2017 -0500 > @@ -234,6 +234,13 @@ > offsetof(ngx_http_ssl_srv_conf_t, stapling_verify), > NULL }, > > + { ngx_string("ssl_psk_file"), > + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, > + ngx_conf_set_str_slot, > + NGX_HTTP_SRV_CONF_OFFSET, > + offsetof(ngx_http_ssl_srv_conf_t, psk_file), > + NULL }, > + > ngx_null_command > }; > > @@ -539,6 +546,7 @@ > * sscf->shm_zone = NULL; > * sscf->stapling_file = { 0, NULL }; > * sscf->stapling_responder = { 0, NULL }; > + * sscf->psk_file = { 0, NULL }; > */ > > sscf->enable = NGX_CONF_UNSET; > @@ -620,6 +628,8 @@ > ngx_conf_merge_str_value(conf->stapling_responder, > prev->stapling_responder, ""); > > + ngx_conf_merge_str_value(conf->psk_file, prev->psk_file, ""); > + > conf->ssl.log = cf->log; > > if (conf->enable) { > @@ -800,6 +810,12 @@ > > } > > + if (ngx_ssl_psk_file(cf, &conf->ssl, &conf->psk_file) > + != NGX_OK) > + { > + return NGX_CONF_ERROR; > + } > + > return NGX_CONF_OK; > } > > diff -r 6169dbad37d8 -r 3fb3c4928d06 src/http/modules/ngx_http_ssl_module.h > --- a/src/http/modules/ngx_http_ssl_module.h Wed Jun 14 20:13:41 2017 +0300 > +++ b/src/http/modules/ngx_http_ssl_module.h Thu Jun 22 08:13:00 2017 -0500 > @@ -55,6 +55,8 @@ > ngx_str_t stapling_file; > ngx_str_t stapling_responder; > > + ngx_str_t psk_file; > + > u_char *file; > ngx_uint_t line; > } ngx_http_ssl_srv_conf_t; > > ________________________________ > > CONFIDENTIALITY NOTICE: This email and any attachments are for the sole use of the intended recipient(s) and contain information that may be Garmin confidential and/or Garmin legally privileged. If you have received this email in error, please notify the sender by reply email and delete the message. Any disclosure, copying, distribution or use of this communication (including attachments) by someone other than the intended recipient is prohibited. Thank you. > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel -- Maxim Dounin http://nginx.org/ _______________________________________________ nginx-devel mailing list nginx-devel at nginx.org http://mailman.nginx.org/mailman/listinfo/nginx-devel From mdounin at mdounin.ru Fri Jun 30 11:58:08 2017 From: mdounin at mdounin.ru (Maxim Dounin) Date: Fri, 30 Jun 2017 14:58:08 +0300 Subject: [PATCH 1 of 3] PSK: connection support In-Reply-To: <145451D4E6785E4DA4DFA353A58CB7B2015E17251F@OLAWPA-EXMB03.ad.garmin.com> References: <145451D4E6785E4DA4DFA353A58CB7B2015E16FAA8@OLAWPA-EXMB03.ad.garmin.com> <20170629210836.GO55433@mdounin.ru> <145451D4E6785E4DA4DFA353A58CB7B2015E17251F@OLAWPA-EXMB03.ad.garmin.com> Message-ID: <20170630115808.GR55433@mdounin.ru> Hello! On Thu, Jun 29, 2017 at 10:00:45PM +0000, Karstens, Nate wrote: > Thanks for the comments. I'll try to start on those in a couple > of days. Just to make it clear: there is no need to hurry. Likely I won't be able to review new patches in at least a couple of weeks, so feel free to spend more time polishing the patches. > My company uses Outlook/Exchange for email, so I don't > think I'll be able to use hg email, do you have any other > suggestions? Thanks also for your patience, I've used Git quite > a bit but am new to Mercurial. The "hg email" command can work with any SMTP server, including Exchange. Or you can ensure proper threading manually by using a "reply" function. > Utkarsh sounds like he is trying to use PSK for TLS v1.3 session > resumption. Given that each TLS connection could potentially > result in a new PSK I think only reading them at startup could > result in too many refreshes. I think there might be some > benefit to the original approach in regards to storing each PSK > in its own file in a designated directory. Benefits include: TLS v1.3 session resumption uses PSK internally, but it is very different from internal usage point of view. It is handled well enough with existing session cache / session tickets mechanisms. [...] > > +ngx_int_t > > +ngx_ssl_psk_file(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file) > > + > > +{ > > Style: extra empty line. > > > + ngx_int_t rc; > > + > > + if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_psk_index, file) == 0) { > > + ngx_ssl_error(NGX_LOG_ALERT, ssl->log, 0, > > + "SSL_CTX_set_ex_data() failed"); > > + return NGX_ERROR; > > + } > > + > > + rc = ngx_ssl_psk_read(file, NULL, NULL, 0); > > + > > + return rc == 0 ? NGX_OK : NGX_ERROR; > > +} [...] > > @@ -800,6 +810,12 @@ > > > > } > > > > + if (ngx_ssl_psk_file(cf, &conf->ssl, &conf->psk_file) > > + != NGX_OK) > > + { > > + return NGX_CONF_ERROR; > > + } Note: this calls ngx_ssl_psk_file() unconditionally, and ngx_ssl_psk_file() also doesn't check if a file is configured. As a result, a configuration without ssl_psk_file fails. -- Maxim Dounin http://nginx.org/ From spencer at kogosoftwarellc.com Fri Jun 30 17:51:55 2017 From: spencer at kogosoftwarellc.com (Joseph Spencer) Date: Fri, 30 Jun 2017 10:51:55 -0700 Subject: SHM and Linked Lists Message-ID: Hello! I'm currently in the process of writing a module that will utilize a linked list, of potentially great size (probably around 1GB). I'd like to initialize this in the master process and share it with each worker. Would SHM be a reliable way to achieve this? -- Thanks, Joe Spencer (member) Kogo Software LLC -------------- next part -------------- An HTML attachment was scrubbed... URL: