From gmm at csdoc.com Thu Mar 1 12:37:42 2018 From: gmm at csdoc.com (Gena Makhomed) Date: Thu, 1 Mar 2018 14:37:42 +0200 Subject: [PATCH] Contrib: vim syntax, update core and 3rd party module directives. Message-ID: # HG changeset patch # User Gena Makhomed # Date 1519907674 -7200 # Thu Mar 01 14:34:34 2018 +0200 # Node ID 42536cf64a89641b90bc0db7223fe60703d663e0 # Parent 20f139e9ffa84f1a1db6039bbbb547cd35fc4534 Contrib: vim syntax, update core and 3rd party module directives. diff -r 20f139e9ffa8 -r 42536cf64a89 contrib/vim/syntax/nginx.vim --- a/contrib/vim/syntax/nginx.vim Wed Feb 28 16:56:58 2018 +0300 +++ b/contrib/vim/syntax/nginx.vim Thu Mar 01 14:34:34 2018 +0200 @@ -268,11 +268,14 @@ syn keyword ngxDirective contained http2_body_preread_size syn keyword ngxDirective contained http2_chunk_size syn keyword ngxDirective contained http2_idle_timeout +syn keyword ngxDirective contained http2_max_concurrent_pushes syn keyword ngxDirective contained http2_max_concurrent_streams syn keyword ngxDirective contained http2_max_field_size syn keyword ngxDirective contained http2_max_header_size syn keyword ngxDirective contained http2_max_requests syn keyword ngxDirective contained http2_pool_size +syn keyword ngxDirective contained http2_push +syn keyword ngxDirective contained http2_push_preload syn keyword ngxDirective contained http2_recv_buffer_size syn keyword ngxDirective contained http2_recv_timeout syn keyword ngxDirective contained http2_streams_index_size @@ -574,6 +577,7 @@ syn keyword ngxDirective contained sub_filter_last_modified syn keyword ngxDirective contained sub_filter_once syn keyword ngxDirective contained sub_filter_types +syn keyword ngxDirective contained subrequest_output_buffer_size syn keyword ngxDirective contained tcp_nodelay syn keyword ngxDirective contained tcp_nopush syn keyword ngxDirective contained thread_pool @@ -2028,6 +2032,7 @@ syn keyword ngxDirectiveThirdParty contained selective_cache_purge_query syn keyword ngxDirectiveThirdParty contained selective_cache_purge_redis_database syn keyword ngxDirectiveThirdParty contained selective_cache_purge_redis_host +syn keyword ngxDirectiveThirdParty contained selective_cache_purge_redis_password syn keyword ngxDirectiveThirdParty contained selective_cache_purge_redis_port syn keyword ngxDirectiveThirdParty contained selective_cache_purge_redis_unix_socket From arut at nginx.com Thu Mar 1 16:13:04 2018 From: arut at nginx.com (Roman Arutyunyan) Date: Thu, 01 Mar 2018 16:13:04 +0000 Subject: [nginx] Postpone filter: prevented uninitialized r->out. Message-ID: details: http://hg.nginx.org/nginx/rev/43585e0e12a3 branches: changeset: 7221:43585e0e12a3 user: Roman Arutyunyan date: Thu Mar 01 18:38:39 2018 +0300 description: Postpone filter: prevented uninitialized r->out. The r->out chain link could be left uninitialized in case of error. A segfault could happen if the subrequest handler accessed it. The issue was introduced in commit 20f139e9ffa8. diffstat: src/http/ngx_http_postpone_filter_module.c | 10 +++++----- 1 files changed, 5 insertions(+), 5 deletions(-) diffs (27 lines): diff -r 20f139e9ffa8 -r 43585e0e12a3 src/http/ngx_http_postpone_filter_module.c --- a/src/http/ngx_http_postpone_filter_module.c Wed Feb 28 16:56:58 2018 +0300 +++ b/src/http/ngx_http_postpone_filter_module.c Thu Mar 01 18:38:39 2018 +0300 @@ -191,11 +191,6 @@ ngx_http_postpone_filter_in_memory(ngx_h "http postpone filter in memory"); if (r->out == NULL) { - r->out = ngx_alloc_chain_link(r->pool); - if (r->out == NULL) { - return NGX_ERROR; - } - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); if (r->headers_out.content_length_n != -1) { @@ -218,6 +213,11 @@ ngx_http_postpone_filter_in_memory(ngx_h b->last_buf = 1; + r->out = ngx_alloc_chain_link(r->pool); + if (r->out == NULL) { + return NGX_ERROR; + } + r->out->buf = b; r->out->next = NULL; } From mdounin at mdounin.ru Thu Mar 1 17:27:14 2018 From: mdounin at mdounin.ru (Maxim Dounin) Date: Thu, 01 Mar 2018 17:27:14 +0000 Subject: [nginx] Core: ngx_current_msec now uses monotonic time if available. Message-ID: details: http://hg.nginx.org/nginx/rev/81fae70d6cb8 branches: changeset: 7222:81fae70d6cb8 user: Maxim Dounin date: Thu Mar 01 20:25:50 2018 +0300 description: Core: ngx_current_msec now uses monotonic time if available. When clock_gettime(CLOCK_MONOTONIC) (or faster variants, _FAST on FreeBSD, and _COARSE on Linux) is available, we now use it for ngx_current_msec. This should improve handling of timers if system time changes (ticket #189). diffstat: auto/unix | 24 ++++++++++++++++++++++++ src/core/ngx_times.c | 30 +++++++++++++++++++++++++++++- 2 files changed, 53 insertions(+), 1 deletions(-) diffs (88 lines): diff --git a/auto/unix b/auto/unix --- a/auto/unix +++ b/auto/unix @@ -791,6 +791,30 @@ ngx_feature_test="struct tm t; time_t c= . auto/feature +ngx_feature="clock_gettime(CLOCK_MONOTONIC)" +ngx_feature_name="NGX_HAVE_CLOCK_MONOTONIC" +ngx_feature_run=no +ngx_feature_incs="#include " +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts)" +. auto/feature + + +if [ $ngx_found = no ]; then + + # Linux before glibc 2.17, notably CentOS 6 + + ngx_feature="clock_gettime(CLOCK_MONOTONIC) in librt" + ngx_feature_libs="-lrt" + . auto/feature + + if [ $ngx_found = yes ]; then + CORE_LIBS="$CORE_LIBS -lrt" + fi +fi + + ngx_feature="posix_memalign()" ngx_feature_name="NGX_HAVE_POSIX_MEMALIGN" ngx_feature_run=no diff --git a/src/core/ngx_times.c b/src/core/ngx_times.c --- a/src/core/ngx_times.c +++ b/src/core/ngx_times.c @@ -9,6 +9,9 @@ #include +static ngx_msec_t ngx_monotonic_time(time_t sec, ngx_uint_t msec); + + /* * The time may be updated by signal handler or by several threads. * The time update operations are rare and require to hold the ngx_time_lock. @@ -93,7 +96,7 @@ ngx_time_update(void) sec = tv.tv_sec; msec = tv.tv_usec / 1000; - ngx_current_msec = (ngx_msec_t) sec * 1000 + msec; + ngx_current_msec = ngx_monotonic_time(sec, msec); tp = &cached_time[slot]; @@ -189,6 +192,31 @@ ngx_time_update(void) } +static ngx_msec_t +ngx_monotonic_time(time_t sec, ngx_uint_t msec) +{ +#if (NGX_HAVE_CLOCK_MONOTONIC) + struct timespec ts; + +#if defined(CLOCK_MONOTONIC_FAST) + clock_gettime(CLOCK_MONOTONIC_FAST, &ts); + +#elif defined(CLOCK_MONOTONIC_COARSE) + clock_gettime(CLOCK_MONOTONIC_COARSE, &ts); + +#else + clock_gettime(CLOCK_MONOTONIC, &ts); +#endif + + sec = ts.tv_sec; + msec = ts.tv_nsec / 1000000; + +#endif + + return (ngx_msec_t) sec * 1000 + msec; +} + + #if !(NGX_WIN32) void From i at lvht.net Fri Mar 2 07:53:07 2018 From: i at lvht.net (Haitao Lv) Date: Fri, 2 Mar 2018 15:53:07 +0800 Subject: [PATCH] HTTP/2: make http2 server support http1 Message-ID: # HG changeset patch # User ??? # Date 1519976498 -28800 # Fri Mar 02 15:41:38 2018 +0800 # Node ID 200955343460c4726015180f20c03e31c0b35ff6 # Parent 81fae70d6cb81c67607931ec3ecc585a609c97e0 make http2 server support http1 diff -r 81fae70d6cb8 -r 200955343460 src/http/ngx_http.c --- a/src/http/ngx_http.c Thu Mar 01 20:25:50 2018 +0300 +++ b/src/http/ngx_http.c Fri Mar 02 15:41:38 2018 +0800 @@ -1197,6 +1197,7 @@ ngx_uint_t ssl; #endif #if (NGX_HTTP_V2) + ngx_uint_t http1; ngx_uint_t http2; #endif @@ -1232,6 +1233,7 @@ ssl = lsopt->ssl || addr[i].opt.ssl; #endif #if (NGX_HTTP_V2) + http1 = lsopt->http1 || addr[i].opt.http1; http2 = lsopt->http2 || addr[i].opt.http2; #endif @@ -1266,6 +1268,7 @@ addr[i].opt.ssl = ssl; #endif #if (NGX_HTTP_V2) + addr[i].opt.http1 = http1; addr[i].opt.http2 = http2; #endif @@ -1802,6 +1805,7 @@ addrs[i].conf.ssl = addr[i].opt.ssl; #endif #if (NGX_HTTP_V2) + addrs[i].conf.http1 = addr[i].opt.http1; addrs[i].conf.http2 = addr[i].opt.http2; #endif addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol; @@ -1867,6 +1871,7 @@ addrs6[i].conf.ssl = addr[i].opt.ssl; #endif #if (NGX_HTTP_V2) + addrs6[i].conf.http1 = addr[i].opt.http1; addrs6[i].conf.http2 = addr[i].opt.http2; #endif addrs6[i].conf.proxy_protocol = addr[i].opt.proxy_protocol; diff -r 81fae70d6cb8 -r 200955343460 src/http/ngx_http_core_module.c --- a/src/http/ngx_http_core_module.c Thu Mar 01 20:25:50 2018 +0300 +++ b/src/http/ngx_http_core_module.c Fri Mar 02 15:41:38 2018 +0800 @@ -3985,6 +3985,18 @@ #endif } + if (ngx_strcmp(value[n].data, "http1") == 0) { +#if (NGX_HTTP_V2) + lsopt.http1 = 1; + continue; +#else + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the \"http1\" parameter requires " + "ngx_http_v2_module"); + return NGX_CONF_ERROR; +#endif + } + if (ngx_strcmp(value[n].data, "spdy") == 0) { ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "invalid parameter \"spdy\": " diff -r 81fae70d6cb8 -r 200955343460 src/http/ngx_http_core_module.h --- a/src/http/ngx_http_core_module.h Thu Mar 01 20:25:50 2018 +0300 +++ b/src/http/ngx_http_core_module.h Fri Mar 02 15:41:38 2018 +0800 @@ -73,6 +73,7 @@ unsigned bind:1; unsigned wildcard:1; unsigned ssl:1; + unsigned http1:1; unsigned http2:1; #if (NGX_HAVE_INET6) unsigned ipv6only:1; @@ -234,6 +235,7 @@ ngx_http_virtual_names_t *virtual_names; unsigned ssl:1; + unsigned http1:1; unsigned http2:1; unsigned proxy_protocol:1; }; diff -r 81fae70d6cb8 -r 200955343460 src/http/ngx_http_parse.c --- a/src/http/ngx_http_parse.c Thu Mar 01 20:25:50 2018 +0300 +++ b/src/http/ngx_http_parse.c Fri Mar 02 15:41:38 2018 +0800 @@ -285,6 +285,14 @@ break; } +#if (NGX_HTTP_V2) + if (ch == '*') { + r->uri_start = p; + state = sw_uri; + break; + } +#endif + c = (u_char) (ch | 0x20); if (c >= 'a' && c <= 'z') { r->schema_start = p; @@ -724,9 +732,11 @@ r->http_major = ch - '0'; +#if !(NGX_HTTP_V2) if (r->http_major > 1) { return NGX_HTTP_PARSE_INVALID_VERSION; } +#endif state = sw_major_digit; break; @@ -744,9 +754,11 @@ r->http_major = r->http_major * 10 + (ch - '0'); +#if !(NGX_HTTP_V2) if (r->http_major > 1) { return NGX_HTTP_PARSE_INVALID_VERSION; } +#endif break; diff -r 81fae70d6cb8 -r 200955343460 src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c Thu Mar 01 20:25:50 2018 +0300 +++ b/src/http/ngx_http_request.c Fri Mar 02 15:41:38 2018 +0800 @@ -17,6 +17,10 @@ static ngx_int_t ngx_http_alloc_large_header_buffer(ngx_http_request_t *r, ngx_uint_t request_line); +#if (NGX_HTTP_V2) +static void ngx_http_process_h2_preface(ngx_event_t *rev); +#endif + 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_unique_header_line(ngx_http_request_t *r, @@ -320,7 +324,7 @@ c->write->handler = ngx_http_empty_handler; #if (NGX_HTTP_V2) - if (hc->addr_conf->http2) { + if (hc->addr_conf->http2 && !hc->addr_conf->http1) { rev->handler = ngx_http_v2_init; } #endif @@ -1033,6 +1037,20 @@ return; } +#if (NGX_HTTP_V2) + if (r->http_connection->addr_conf->http2 + && r->http_version >= NGX_HTTP_VERSION_20) { + + c->log->action = "reading client h2 preface"; + + rev->handler = ngx_http_process_h2_preface; + ngx_http_process_h2_preface(rev); + return; + } else if (r->http_version >= NGX_HTTP_VERSION_20) { + ngx_http_finalize_request(r, NGX_HTTP_VERSION_NOT_SUPPORTED); + return; + } +#endif if (ngx_list_init(&r->headers_in.headers, r->pool, 20, sizeof(ngx_table_elt_t)) @@ -1208,6 +1226,64 @@ return NGX_OK; } +#if (NGX_HTTP_V2) +static void +ngx_http_process_h2_preface(ngx_event_t *rev) +{ + size_t len; + ssize_t n; + ngx_connection_t *c; + ngx_http_request_t *r; + + c = rev->data; + r = c->data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http process h2 preface"); + + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); + c->timedout = 1; + ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT); + return; + } + + n = ngx_http_read_request_header(r); + + if (n == NGX_AGAIN || n == NGX_ERROR) { + return; + } + + len = r->header_in->last - r->header_in->start; + + if (len < sizeof("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n") - 1) { + r->header_in->pos = r->header_in->last; + return; + } + + if (ngx_strncmp(r->header_in->start, + "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", 24) == 0) { + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "h2 preface done"); + + r->header_in->pos = r->header_in->start + 24; + + c->data = r->http_connection; + + r->http_connection = NULL; + + ngx_http_v2_init_after_preface(rev, r->header_in); + return; + } + + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client sent invalid h2 preface"); + + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); +} +#endif + static void ngx_http_process_request_headers(ngx_event_t *rev) @@ -1809,7 +1885,7 @@ return NGX_ERROR; } - if (r->headers_in.host == NULL && r->http_version > NGX_HTTP_VERSION_10) { + if (r->headers_in.host == NULL && r->http_version == NGX_HTTP_VERSION_11) { ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "client sent HTTP/1.1 request without \"Host\" header"); ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); diff -r 81fae70d6cb8 -r 200955343460 src/http/v2/ngx_http_v2.c --- a/src/http/v2/ngx_http_v2.c Thu Mar 01 20:25:50 2018 +0300 +++ b/src/http/v2/ngx_http_v2.c Fri Mar 02 15:41:38 2018 +0800 @@ -231,6 +231,13 @@ void ngx_http_v2_init(ngx_event_t *rev) { + ngx_http_v2_init_after_preface(rev, NULL); +} + + +void +ngx_http_v2_init_after_preface(ngx_event_t *rev, ngx_buf_t *buf) +{ ngx_connection_t *c; ngx_pool_cleanup_t *cln; ngx_http_connection_t *hc; @@ -316,6 +323,12 @@ h2c->state.handler = hc->proxy_protocol ? ngx_http_v2_state_proxy_protocol : ngx_http_v2_state_preface; + if (buf != NULL) { + ngx_memcpy(h2mcf->recv_buffer, buf->pos, buf->last - buf->pos); + h2c->state.buffer_used = buf->last - buf->pos; + h2c->state.handler = ngx_http_v2_state_head; + } + ngx_queue_init(&h2c->waiting); ngx_queue_init(&h2c->dependencies); ngx_queue_init(&h2c->closed); @@ -381,13 +394,18 @@ h2mcf = ngx_http_get_module_main_conf(h2c->http_connection->conf_ctx, ngx_http_v2_module); - available = h2mcf->recv_buffer_size - 2 * NGX_HTTP_V2_STATE_BUFFER_SIZE; + available = h2mcf->recv_buffer_size - h2c->state.buffer_used - 2 * NGX_HTTP_V2_STATE_BUFFER_SIZE; do { p = h2mcf->recv_buffer; + end = p + h2c->state.buffer_used; + + if (h2c->state.buffer_used) { + goto do_state_handler; + } + ngx_memcpy(p, h2c->state.buffer, NGX_HTTP_V2_STATE_BUFFER_SIZE); - end = p + h2c->state.buffer_used; n = c->recv(c, end, available); @@ -410,6 +428,8 @@ end += n; +do_state_handler: + h2c->state.buffer_used = 0; h2c->state.incomplete = 0; diff -r 81fae70d6cb8 -r 200955343460 src/http/v2/ngx_http_v2.h --- a/src/http/v2/ngx_http_v2.h Thu Mar 01 20:25:50 2018 +0300 +++ b/src/http/v2/ngx_http_v2.h Fri Mar 02 15:41:38 2018 +0800 @@ -279,6 +279,7 @@ void ngx_http_v2_init(ngx_event_t *rev); +void ngx_http_v2_init_after_preface(ngx_event_t *rev, ngx_buf_t *buf); 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); From vl at nginx.com Fri Mar 2 08:45:21 2018 From: vl at nginx.com (Vladimir Homutov) Date: Fri, 02 Mar 2018 08:45:21 +0000 Subject: [nginx] Access log: support for disabling escaping (ticket #1450). Message-ID: details: http://hg.nginx.org/nginx/rev/265c29b0b8b8 branches: changeset: 7223:265c29b0b8b8 user: Vladimir Homutov date: Thu Mar 01 11:42:55 2018 +0300 description: Access log: support for disabling escaping (ticket #1450). Based on patches by Johannes Baiter and Calin Don. diffstat: src/http/modules/ngx_http_log_module.c | 68 ++++++++++++++++++++++++++++++---- src/stream/ngx_stream_log_module.c | 68 ++++++++++++++++++++++++++++++---- 2 files changed, 120 insertions(+), 16 deletions(-) diffs (277 lines): diff -r 81fae70d6cb8 -r 265c29b0b8b8 src/http/modules/ngx_http_log_module.c --- a/src/http/modules/ngx_http_log_module.c Thu Mar 01 20:25:50 2018 +0300 +++ b/src/http/modules/ngx_http_log_module.c Thu Mar 01 11:42:55 2018 +0300 @@ -90,6 +90,11 @@ typedef struct { } ngx_http_log_var_t; +#define NGX_HTTP_LOG_ESCAPE_DEFAULT 0 +#define NGX_HTTP_LOG_ESCAPE_JSON 1 +#define NGX_HTTP_LOG_ESCAPE_NONE 2 + + static void ngx_http_log_write(ngx_http_request_t *r, ngx_http_log_t *log, u_char *buf, size_t len); static ssize_t ngx_http_log_script_write(ngx_http_request_t *r, @@ -126,7 +131,7 @@ static u_char *ngx_http_log_request_leng ngx_http_log_op_t *op); static ngx_int_t ngx_http_log_variable_compile(ngx_conf_t *cf, - ngx_http_log_op_t *op, ngx_str_t *value, ngx_uint_t json); + ngx_http_log_op_t *op, ngx_str_t *value, ngx_uint_t escape); static size_t ngx_http_log_variable_getlen(ngx_http_request_t *r, uintptr_t data); static u_char *ngx_http_log_variable(ngx_http_request_t *r, u_char *buf, @@ -136,6 +141,10 @@ static size_t ngx_http_log_json_variable uintptr_t data); static u_char *ngx_http_log_json_variable(ngx_http_request_t *r, u_char *buf, ngx_http_log_op_t *op); +static size_t ngx_http_log_unescaped_variable_getlen(ngx_http_request_t *r, + uintptr_t data); +static u_char *ngx_http_log_unescaped_variable(ngx_http_request_t *r, + u_char *buf, ngx_http_log_op_t *op); static void *ngx_http_log_create_main_conf(ngx_conf_t *cf); @@ -905,7 +914,7 @@ ngx_http_log_request_length(ngx_http_req static ngx_int_t ngx_http_log_variable_compile(ngx_conf_t *cf, ngx_http_log_op_t *op, - ngx_str_t *value, ngx_uint_t json) + ngx_str_t *value, ngx_uint_t escape) { ngx_int_t index; @@ -916,11 +925,18 @@ ngx_http_log_variable_compile(ngx_conf_t op->len = 0; - if (json) { + switch (escape) { + case NGX_HTTP_LOG_ESCAPE_JSON: op->getlen = ngx_http_log_json_variable_getlen; op->run = ngx_http_log_json_variable; + break; - } else { + case NGX_HTTP_LOG_ESCAPE_NONE: + op->getlen = ngx_http_log_unescaped_variable_getlen; + op->run = ngx_http_log_unescaped_variable; + break; + + default: /* NGX_HTTP_LOG_ESCAPE_DEFAULT */ op->getlen = ngx_http_log_variable_getlen; op->run = ngx_http_log_variable; } @@ -1073,6 +1089,39 @@ ngx_http_log_json_variable(ngx_http_requ } +static size_t +ngx_http_log_unescaped_variable_getlen(ngx_http_request_t *r, uintptr_t data) +{ + ngx_http_variable_value_t *value; + + value = ngx_http_get_indexed_variable(r, data); + + if (value == NULL || value->not_found) { + return 0; + } + + value->escape = 0; + + return value->len; +} + + +static u_char * +ngx_http_log_unescaped_variable(ngx_http_request_t *r, u_char *buf, + ngx_http_log_op_t *op) +{ + ngx_http_variable_value_t *value; + + value = ngx_http_get_indexed_variable(r, op->data); + + if (value == NULL || value->not_found) { + return buf; + } + + return ngx_cpymem(buf, value->data, value->len); +} + + static void * ngx_http_log_create_main_conf(ngx_conf_t *cf) { @@ -1536,18 +1585,21 @@ ngx_http_log_compile_format(ngx_conf_t * size_t i, len; ngx_str_t *value, var; ngx_int_t *flush; - ngx_uint_t bracket, json; + ngx_uint_t bracket, escape; ngx_http_log_op_t *op; ngx_http_log_var_t *v; - json = 0; + escape = NGX_HTTP_LOG_ESCAPE_DEFAULT; value = args->elts; if (s < args->nelts && ngx_strncmp(value[s].data, "escape=", 7) == 0) { data = value[s].data + 7; if (ngx_strcmp(data, "json") == 0) { - json = 1; + escape = NGX_HTTP_LOG_ESCAPE_JSON; + + } else if (ngx_strcmp(data, "none") == 0) { + escape = NGX_HTTP_LOG_ESCAPE_NONE; } else if (ngx_strcmp(data, "default") != 0) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, @@ -1636,7 +1688,7 @@ ngx_http_log_compile_format(ngx_conf_t * } } - if (ngx_http_log_variable_compile(cf, op, &var, json) + if (ngx_http_log_variable_compile(cf, op, &var, escape) != NGX_OK) { return NGX_CONF_ERROR; diff -r 81fae70d6cb8 -r 265c29b0b8b8 src/stream/ngx_stream_log_module.c --- a/src/stream/ngx_stream_log_module.c Thu Mar 01 20:25:50 2018 +0300 +++ b/src/stream/ngx_stream_log_module.c Thu Mar 01 11:42:55 2018 +0300 @@ -89,6 +89,11 @@ typedef struct { } ngx_stream_log_var_t; +#define NGX_STREAM_LOG_ESCAPE_DEFAULT 0 +#define NGX_STREAM_LOG_ESCAPE_JSON 1 +#define NGX_STREAM_LOG_ESCAPE_NONE 2 + + static void ngx_stream_log_write(ngx_stream_session_t *s, ngx_stream_log_t *log, u_char *buf, size_t len); static ssize_t ngx_stream_log_script_write(ngx_stream_session_t *s, @@ -106,7 +111,7 @@ static void ngx_stream_log_flush(ngx_ope static void ngx_stream_log_flush_handler(ngx_event_t *ev); static ngx_int_t ngx_stream_log_variable_compile(ngx_conf_t *cf, - ngx_stream_log_op_t *op, ngx_str_t *value, ngx_uint_t json); + ngx_stream_log_op_t *op, ngx_str_t *value, ngx_uint_t escape); static size_t ngx_stream_log_variable_getlen(ngx_stream_session_t *s, uintptr_t data); static u_char *ngx_stream_log_variable(ngx_stream_session_t *s, u_char *buf, @@ -116,6 +121,10 @@ static size_t ngx_stream_log_json_variab uintptr_t data); static u_char *ngx_stream_log_json_variable(ngx_stream_session_t *s, u_char *buf, ngx_stream_log_op_t *op); +static size_t ngx_stream_log_unescaped_variable_getlen(ngx_stream_session_t *s, + uintptr_t data); +static u_char *ngx_stream_log_unescaped_variable(ngx_stream_session_t *s, + u_char *buf, ngx_stream_log_op_t *op); static void *ngx_stream_log_create_main_conf(ngx_conf_t *cf); @@ -682,7 +691,7 @@ ngx_stream_log_copy_long(ngx_stream_sess static ngx_int_t ngx_stream_log_variable_compile(ngx_conf_t *cf, ngx_stream_log_op_t *op, - ngx_str_t *value, ngx_uint_t json) + ngx_str_t *value, ngx_uint_t escape) { ngx_int_t index; @@ -693,11 +702,18 @@ ngx_stream_log_variable_compile(ngx_conf op->len = 0; - if (json) { + switch (escape) { + case NGX_STREAM_LOG_ESCAPE_JSON: op->getlen = ngx_stream_log_json_variable_getlen; op->run = ngx_stream_log_json_variable; + break; - } else { + case NGX_STREAM_LOG_ESCAPE_NONE: + op->getlen = ngx_stream_log_unescaped_variable_getlen; + op->run = ngx_stream_log_unescaped_variable; + break; + + default: /* NGX_STREAM_LOG_ESCAPE_DEFAULT */ op->getlen = ngx_stream_log_variable_getlen; op->run = ngx_stream_log_variable; } @@ -851,6 +867,39 @@ ngx_stream_log_json_variable(ngx_stream_ } +static size_t +ngx_stream_log_unescaped_variable_getlen(ngx_stream_session_t *s, uintptr_t data) +{ + ngx_stream_variable_value_t *value; + + value = ngx_stream_get_indexed_variable(s, data); + + if (value == NULL || value->not_found) { + return 0; + } + + value->escape = 0; + + return value->len; +} + + +static u_char * +ngx_stream_log_unescaped_variable(ngx_stream_session_t *s, u_char *buf, + ngx_stream_log_op_t *op) +{ + ngx_stream_variable_value_t *value; + + value = ngx_stream_get_indexed_variable(s, op->data); + + if (value == NULL || value->not_found) { + return buf; + } + + return ngx_cpymem(buf, value->data, value->len); +} + + static void * ngx_stream_log_create_main_conf(ngx_conf_t *cf) { @@ -1265,17 +1314,20 @@ ngx_stream_log_compile_format(ngx_conf_t size_t i, len; ngx_str_t *value, var; ngx_int_t *flush; - ngx_uint_t bracket, json; + ngx_uint_t bracket, escape; ngx_stream_log_op_t *op; - json = 0; + escape = NGX_STREAM_LOG_ESCAPE_DEFAULT; value = args->elts; if (s < args->nelts && ngx_strncmp(value[s].data, "escape=", 7) == 0) { data = value[s].data + 7; if (ngx_strcmp(data, "json") == 0) { - json = 1; + escape = NGX_STREAM_LOG_ESCAPE_JSON; + + } else if (ngx_strcmp(data, "none") == 0) { + escape = NGX_STREAM_LOG_ESCAPE_NONE; } else if (ngx_strcmp(data, "default") != 0) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, @@ -1350,7 +1402,7 @@ ngx_stream_log_compile_format(ngx_conf_t goto invalid; } - if (ngx_stream_log_variable_compile(cf, op, &var, json) + if (ngx_stream_log_variable_compile(cf, op, &var, escape) != NGX_OK) { return NGX_CONF_ERROR; From vbart at nginx.com Fri Mar 2 17:29:47 2018 From: vbart at nginx.com (Valentin V. Bartenev) Date: Fri, 02 Mar 2018 20:29:47 +0300 Subject: [PATCH] HTTP/2: make http2 server support http1 In-Reply-To: References: Message-ID: <1722875.9peJbJ5922@vbart-workstation> On Friday 02 March 2018 15:53:07 Haitao Lv wrote: > # HG changeset patch > # User ??? > # Date 1519976498 -28800 > # Fri Mar 02 15:41:38 2018 +0800 > # Node ID 200955343460c4726015180f20c03e31c0b35ff6 > # Parent 81fae70d6cb81c67607931ec3ecc585a609c97e0 > make http2 server support http1 > [..] It doesn't look like a useful feature. Could you please explain the use cases? > diff -r 81fae70d6cb8 -r 200955343460 src/http/ngx_http.c > --- a/src/http/ngx_http.c Thu Mar 01 20:25:50 2018 +0300 > +++ b/src/http/ngx_http.c Fri Mar 02 15:41:38 2018 +0800 [..] > +void > +ngx_http_v2_init_after_preface(ngx_event_t *rev, ngx_buf_t *buf) > +{ > ngx_connection_t *c; > ngx_pool_cleanup_t *cln; > ngx_http_connection_t *hc; > @@ -316,6 +323,12 @@ > h2c->state.handler = hc->proxy_protocol ? ngx_http_v2_state_proxy_protocol > : ngx_http_v2_state_preface; > > + if (buf != NULL) { > + ngx_memcpy(h2mcf->recv_buffer, buf->pos, buf->last - buf->pos); > + h2c->state.buffer_used = buf->last - buf->pos; > + h2c->state.handler = ngx_http_v2_state_head; > + } [..] What if the received data is bigger than h2mcf->recv_buffer? wbr, Valentin V. Bartenev From i at lvht.net Sun Mar 4 02:53:36 2018 From: i at lvht.net (Haitao Lv) Date: Sun, 4 Mar 2018 10:53:36 +0800 Subject: [PATCH] HTTP/2: make http2 server support http1 In-Reply-To: References: Message-ID: <762DEAA4-5D48-4957-9DEE-BFF82869EC0C@lvht.net> Hi, wbr, Thanks for your review. I don't know why I can't receive your email. Let me reply directly. > It doesn't look like a useful feature. > Could you please explain the use cases? The current implementation support both http/1 and http/2 over the same TLS listen port by the ALPN Extension. Nginx also support listening http/2 over plain tcp port, which is perfect for development and inner production environment. However, the current implementation cannot support http/1.1 and http/2 simultaneously. We have no choice but listen on two different ports. As a result, one same service has two ports and different clients should choose their suitable port. Besides, the http/2 is efficient, but http/1 is simple. So I see no chance that the http/2 will replace http/1 totally in the production inner environment. Will call the inner API by both http/1 and http/2. So I think support http/1 and http/2 on the plain tcp port will simplify both the production and development environment. > What if the received data is bigger than h2mcf->recv_buffer? Yes, it is a problem. However, it can be fixed easily. As we know, the http/2 preface is PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n. We could modify the ngx_http_parse_request_line and when we got A PRI method, we need to get a single * uri. If we got things other than *, we just return a invalid request response. By this, we will never got a PRI to_much_long_uri HTTP/2.0 request line, and the buffer will not be exhausted. So the ngx_http_alloc_large_header_buffer will not be called during the handshake. After the ngx_http_parse_request_line, we will ensure we got a PRI request, and the buffer size is client_header_buffer_size. And then we should the compare the r->header_in buffer's length and the h2mcf->recv_buffer_size. If we got a buffered data larger than the h2mcf->recv_buffer_size, we should send a bad request response. If this feature can be accepted, I will send a new patch. Thanks. > On Mar 2, 2018, at 15:53, Haitao Lv wrote: > > # HG changeset patch > # User ??? > # Date 1519976498 -28800 > # Fri Mar 02 15:41:38 2018 +0800 > # Node ID 200955343460c4726015180f20c03e31c0b35ff6 > # Parent 81fae70d6cb81c67607931ec3ecc585a609c97e0 > make http2 server support http1 > > diff -r 81fae70d6cb8 -r 200955343460 src/http/ngx_http.c > --- a/src/http/ngx_http.c Thu Mar 01 20:25:50 2018 +0300 > +++ b/src/http/ngx_http.c Fri Mar 02 15:41:38 2018 +0800 > @@ -1197,6 +1197,7 @@ > ngx_uint_t ssl; > #endif > #if (NGX_HTTP_V2) > + ngx_uint_t http1; > ngx_uint_t http2; > #endif > > @@ -1232,6 +1233,7 @@ > ssl = lsopt->ssl || addr[i].opt.ssl; > #endif > #if (NGX_HTTP_V2) > + http1 = lsopt->http1 || addr[i].opt.http1; > http2 = lsopt->http2 || addr[i].opt.http2; > #endif > > @@ -1266,6 +1268,7 @@ > addr[i].opt.ssl = ssl; > #endif > #if (NGX_HTTP_V2) > + addr[i].opt.http1 = http1; > addr[i].opt.http2 = http2; > #endif > > @@ -1802,6 +1805,7 @@ > addrs[i].conf.ssl = addr[i].opt.ssl; > #endif > #if (NGX_HTTP_V2) > + addrs[i].conf.http1 = addr[i].opt.http1; > addrs[i].conf.http2 = addr[i].opt.http2; > #endif > addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol; > @@ -1867,6 +1871,7 @@ > addrs6[i].conf.ssl = addr[i].opt.ssl; > #endif > #if (NGX_HTTP_V2) > + addrs6[i].conf.http1 = addr[i].opt.http1; > addrs6[i].conf.http2 = addr[i].opt.http2; > #endif > addrs6[i].conf.proxy_protocol = addr[i].opt.proxy_protocol; > diff -r 81fae70d6cb8 -r 200955343460 src/http/ngx_http_core_module.c > --- a/src/http/ngx_http_core_module.c Thu Mar 01 20:25:50 2018 +0300 > +++ b/src/http/ngx_http_core_module.c Fri Mar 02 15:41:38 2018 +0800 > @@ -3985,6 +3985,18 @@ > #endif > } > > + if (ngx_strcmp(value[n].data, "http1") == 0) { > +#if (NGX_HTTP_V2) > + lsopt.http1 = 1; > + continue; > +#else > + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, > + "the \"http1\" parameter requires " > + "ngx_http_v2_module"); > + return NGX_CONF_ERROR; > +#endif > + } > + > if (ngx_strcmp(value[n].data, "spdy") == 0) { > ngx_conf_log_error(NGX_LOG_WARN, cf, 0, > "invalid parameter \"spdy\": " > diff -r 81fae70d6cb8 -r 200955343460 src/http/ngx_http_core_module.h > --- a/src/http/ngx_http_core_module.h Thu Mar 01 20:25:50 2018 +0300 > +++ b/src/http/ngx_http_core_module.h Fri Mar 02 15:41:38 2018 +0800 > @@ -73,6 +73,7 @@ > unsigned bind:1; > unsigned wildcard:1; > unsigned ssl:1; > + unsigned http1:1; > unsigned http2:1; > #if (NGX_HAVE_INET6) > unsigned ipv6only:1; > @@ -234,6 +235,7 @@ > ngx_http_virtual_names_t *virtual_names; > > unsigned ssl:1; > + unsigned http1:1; > unsigned http2:1; > unsigned proxy_protocol:1; > }; > diff -r 81fae70d6cb8 -r 200955343460 src/http/ngx_http_parse.c > --- a/src/http/ngx_http_parse.c Thu Mar 01 20:25:50 2018 +0300 > +++ b/src/http/ngx_http_parse.c Fri Mar 02 15:41:38 2018 +0800 > @@ -285,6 +285,14 @@ > break; > } > > +#if (NGX_HTTP_V2) > + if (ch == '*') { > + r->uri_start = p; > + state = sw_uri; > + break; > + } > +#endif > + > c = (u_char) (ch | 0x20); > if (c >= 'a' && c <= 'z') { > r->schema_start = p; > @@ -724,9 +732,11 @@ > > r->http_major = ch - '0'; > > +#if !(NGX_HTTP_V2) > if (r->http_major > 1) { > return NGX_HTTP_PARSE_INVALID_VERSION; > } > +#endif > > state = sw_major_digit; > break; > @@ -744,9 +754,11 @@ > > r->http_major = r->http_major * 10 + (ch - '0'); > > +#if !(NGX_HTTP_V2) > if (r->http_major > 1) { > return NGX_HTTP_PARSE_INVALID_VERSION; > } > +#endif > > break; > > diff -r 81fae70d6cb8 -r 200955343460 src/http/ngx_http_request.c > --- a/src/http/ngx_http_request.c Thu Mar 01 20:25:50 2018 +0300 > +++ b/src/http/ngx_http_request.c Fri Mar 02 15:41:38 2018 +0800 > @@ -17,6 +17,10 @@ > static ngx_int_t ngx_http_alloc_large_header_buffer(ngx_http_request_t *r, > ngx_uint_t request_line); > > +#if (NGX_HTTP_V2) > +static void ngx_http_process_h2_preface(ngx_event_t *rev); > +#endif > + > 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_unique_header_line(ngx_http_request_t *r, > @@ -320,7 +324,7 @@ > c->write->handler = ngx_http_empty_handler; > > #if (NGX_HTTP_V2) > - if (hc->addr_conf->http2) { > + if (hc->addr_conf->http2 && !hc->addr_conf->http1) { > rev->handler = ngx_http_v2_init; > } > #endif > @@ -1033,6 +1037,20 @@ > return; > } > > +#if (NGX_HTTP_V2) > + if (r->http_connection->addr_conf->http2 > + && r->http_version >= NGX_HTTP_VERSION_20) { > + > + c->log->action = "reading client h2 preface"; > + > + rev->handler = ngx_http_process_h2_preface; > + ngx_http_process_h2_preface(rev); > + return; > + } else if (r->http_version >= NGX_HTTP_VERSION_20) { > + ngx_http_finalize_request(r, NGX_HTTP_VERSION_NOT_SUPPORTED); > + return; > + } > +#endif > > if (ngx_list_init(&r->headers_in.headers, r->pool, 20, > sizeof(ngx_table_elt_t)) > @@ -1208,6 +1226,64 @@ > return NGX_OK; > } > > +#if (NGX_HTTP_V2) > +static void > +ngx_http_process_h2_preface(ngx_event_t *rev) > +{ > + size_t len; > + ssize_t n; > + ngx_connection_t *c; > + ngx_http_request_t *r; > + > + c = rev->data; > + r = c->data; > + > + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, > + "http process h2 preface"); > + > + if (rev->timedout) { > + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); > + c->timedout = 1; > + ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT); > + return; > + } > + > + n = ngx_http_read_request_header(r); > + > + if (n == NGX_AGAIN || n == NGX_ERROR) { > + return; > + } > + > + len = r->header_in->last - r->header_in->start; > + > + if (len < sizeof("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n") - 1) { > + r->header_in->pos = r->header_in->last; > + return; > + } > + > + if (ngx_strncmp(r->header_in->start, > + "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", 24) == 0) { > + > + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, > + "h2 preface done"); > + > + r->header_in->pos = r->header_in->start + 24; > + > + c->data = r->http_connection; > + > + r->http_connection = NULL; > + > + ngx_http_v2_init_after_preface(rev, r->header_in); > + return; > + } > + > + ngx_log_error(NGX_LOG_INFO, c->log, 0, > + "client sent invalid h2 preface"); > + > + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); > +} > +#endif > + > > static void > ngx_http_process_request_headers(ngx_event_t *rev) > @@ -1809,7 +1885,7 @@ > return NGX_ERROR; > } > > - if (r->headers_in.host == NULL && r->http_version > NGX_HTTP_VERSION_10) { > + if (r->headers_in.host == NULL && r->http_version == NGX_HTTP_VERSION_11) { > ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, > "client sent HTTP/1.1 request without \"Host\" header"); > ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); > diff -r 81fae70d6cb8 -r 200955343460 src/http/v2/ngx_http_v2.c > --- a/src/http/v2/ngx_http_v2.c Thu Mar 01 20:25:50 2018 +0300 > +++ b/src/http/v2/ngx_http_v2.c Fri Mar 02 15:41:38 2018 +0800 > @@ -231,6 +231,13 @@ > void > ngx_http_v2_init(ngx_event_t *rev) > { > + ngx_http_v2_init_after_preface(rev, NULL); > +} > + > + > +void > +ngx_http_v2_init_after_preface(ngx_event_t *rev, ngx_buf_t *buf) > +{ > ngx_connection_t *c; > ngx_pool_cleanup_t *cln; > ngx_http_connection_t *hc; > @@ -316,6 +323,12 @@ > h2c->state.handler = hc->proxy_protocol ? ngx_http_v2_state_proxy_protocol > : ngx_http_v2_state_preface; > > + if (buf != NULL) { > + ngx_memcpy(h2mcf->recv_buffer, buf->pos, buf->last - buf->pos); > + h2c->state.buffer_used = buf->last - buf->pos; > + h2c->state.handler = ngx_http_v2_state_head; > + } > + > ngx_queue_init(&h2c->waiting); > ngx_queue_init(&h2c->dependencies); > ngx_queue_init(&h2c->closed); > @@ -381,13 +394,18 @@ > h2mcf = ngx_http_get_module_main_conf(h2c->http_connection->conf_ctx, > ngx_http_v2_module); > > - available = h2mcf->recv_buffer_size - 2 * NGX_HTTP_V2_STATE_BUFFER_SIZE; > + available = h2mcf->recv_buffer_size - h2c->state.buffer_used - 2 * NGX_HTTP_V2_STATE_BUFFER_SIZE; > > do { > p = h2mcf->recv_buffer; > > + end = p + h2c->state.buffer_used; > + > + if (h2c->state.buffer_used) { > + goto do_state_handler; > + } > + > ngx_memcpy(p, h2c->state.buffer, NGX_HTTP_V2_STATE_BUFFER_SIZE); > - end = p + h2c->state.buffer_used; > > n = c->recv(c, end, available); > > @@ -410,6 +428,8 @@ > > end += n; > > +do_state_handler: > + > h2c->state.buffer_used = 0; > h2c->state.incomplete = 0; > > diff -r 81fae70d6cb8 -r 200955343460 src/http/v2/ngx_http_v2.h > --- a/src/http/v2/ngx_http_v2.h Thu Mar 01 20:25:50 2018 +0300 > +++ b/src/http/v2/ngx_http_v2.h Fri Mar 02 15:41:38 2018 +0800 > @@ -279,6 +279,7 @@ > > > void ngx_http_v2_init(ngx_event_t *rev); > +void ngx_http_v2_init_after_preface(ngx_event_t *rev, ngx_buf_t *buf); > > 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); > From i at lvht.net Sun Mar 4 05:49:45 2018 From: i at lvht.net (Haitao Lv) Date: Sun, 4 Mar 2018 13:49:45 +0800 Subject: [PATCH] HTTP/2: make http2 server support http1 In-Reply-To: <762DEAA4-5D48-4957-9DEE-BFF82869EC0C@lvht.net> References: <762DEAA4-5D48-4957-9DEE-BFF82869EC0C@lvht.net> Message-ID: Here is the new patch diff --git a/src/http/ngx_http.c b/src/http/ngx_http.c index 9d8b6d79..c06ef7c7 100644 --- a/src/http/ngx_http.c +++ b/src/http/ngx_http.c @@ -1197,6 +1197,7 @@ ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, ngx_uint_t ssl; #endif #if (NGX_HTTP_V2) + ngx_uint_t http1; ngx_uint_t http2; #endif @@ -1232,6 +1233,7 @@ ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, ssl = lsopt->ssl || addr[i].opt.ssl; #endif #if (NGX_HTTP_V2) + http1 = lsopt->http1 || addr[i].opt.http1; http2 = lsopt->http2 || addr[i].opt.http2; #endif @@ -1266,6 +1268,7 @@ ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, addr[i].opt.ssl = ssl; #endif #if (NGX_HTTP_V2) + addr[i].opt.http1 = http1; addr[i].opt.http2 = http2; #endif @@ -1802,6 +1805,7 @@ ngx_http_add_addrs(ngx_conf_t *cf, ngx_http_port_t *hport, addrs[i].conf.ssl = addr[i].opt.ssl; #endif #if (NGX_HTTP_V2) + addrs[i].conf.http1 = addr[i].opt.http1; addrs[i].conf.http2 = addr[i].opt.http2; #endif addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol; @@ -1867,6 +1871,7 @@ ngx_http_add_addrs6(ngx_conf_t *cf, ngx_http_port_t *hport, addrs6[i].conf.ssl = addr[i].opt.ssl; #endif #if (NGX_HTTP_V2) + addrs6[i].conf.http1 = addr[i].opt.http1; addrs6[i].conf.http2 = addr[i].opt.http2; #endif addrs6[i].conf.proxy_protocol = addr[i].opt.proxy_protocol; diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c index 6b318dd0..741a4dd9 100644 --- a/src/http/ngx_http_core_module.c +++ b/src/http/ngx_http_core_module.c @@ -3985,6 +3985,18 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) #endif } + if (ngx_strcmp(value[n].data, "http1") == 0) { +#if (NGX_HTTP_V2) + lsopt.http1 = 1; + continue; +#else + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the \"http1\" parameter requires " + "ngx_http_v2_module"); + return NGX_CONF_ERROR; +#endif + } + if (ngx_strcmp(value[n].data, "spdy") == 0) { ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "invalid parameter \"spdy\": " diff --git a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h index d7985049..68b3e7d9 100644 --- a/src/http/ngx_http_core_module.h +++ b/src/http/ngx_http_core_module.h @@ -73,6 +73,7 @@ typedef struct { unsigned bind:1; unsigned wildcard:1; unsigned ssl:1; + unsigned http1:1; unsigned http2:1; #if (NGX_HAVE_INET6) unsigned ipv6only:1; @@ -234,6 +235,7 @@ struct ngx_http_addr_conf_s { ngx_http_virtual_names_t *virtual_names; unsigned ssl:1; + unsigned http1:1; unsigned http2:1; unsigned proxy_protocol:1; }; diff --git a/src/http/ngx_http_parse.c b/src/http/ngx_http_parse.c index 844054c9..9eb713b7 100644 --- a/src/http/ngx_http_parse.c +++ b/src/http/ngx_http_parse.c @@ -117,6 +117,7 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) sw_host_ip_literal, sw_port, sw_host_http_09, + sw_h2_preface, sw_after_slash_in_uri, sw_check_uri, sw_check_uri_http_09, @@ -134,6 +135,12 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) sw_almost_done } state; +#if (NGX_HTTP_V2) + static u_char h2_preface[] = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"; + u_char *h2_preface_end = h2_preface + 24; + u_char *n = h2_preface + 4; +#endif + state = r->state; for (p = b->pos; p < b->last; p++) { @@ -145,10 +152,6 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) case sw_start: r->request_start = p; - if (ch == CR || ch == LF) { - break; - } - if ((ch < 'A' || ch > 'Z') && ch != '_' && ch != '-') { return NGX_HTTP_PARSE_INVALID_METHOD; } @@ -174,6 +177,13 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) break; } +#if (NGX_HTTP_V2) + if (ngx_str3_cmp(m, 'P', 'R', 'I', ' ')) { + r->method = NGX_HTTP_PRI; + break; + } +#endif + break; case 4: @@ -267,6 +277,14 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) } state = sw_spaces_before_uri; + +#if (NGX_HTTP_V2) + if (r->method & NGX_HTTP_PRI) { + r->uri_start = p; + state = sw_h2_preface; + } +#endif + break; } @@ -276,6 +294,29 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) break; +#if (NGX_HTTP_V2) + case sw_h2_preface: + + if (ch == *n++) { + if (n == h2_preface_end) { + r->request_end = r->request_start + 14; + r->uri_start = r->request_start + 4; + r->uri_end = r->request_start + 5; + r->http_protocol.data = r->request_start + 6; + r->http_major = 2; + r->http_minor = 0; + + goto done; + } + + break; + } + + return NGX_HTTP_PARSE_INVALID_METHOD; + + break; +#endif + /* space* before URI */ case sw_spaces_before_uri: @@ -724,9 +765,11 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) r->http_major = ch - '0'; +#if !(NGX_HTTP_V2) if (r->http_major > 1) { return NGX_HTTP_PARSE_INVALID_VERSION; } +#endif state = sw_major_digit; break; @@ -744,9 +787,11 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) r->http_major = r->http_major * 10 + (ch - '0'); +#if !(NGX_HTTP_V2) if (r->http_major > 1) { return NGX_HTTP_PARSE_INVALID_VERSION; } +#endif break; diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 89cfe77a..3ae64569 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -8,6 +8,9 @@ #include #include #include +#if (NGX_HTTP_V2) +#include +#endif static void ngx_http_wait_request_handler(ngx_event_t *ev); @@ -17,6 +20,10 @@ static ssize_t ngx_http_read_request_header(ngx_http_request_t *r); static ngx_int_t ngx_http_alloc_large_header_buffer(ngx_http_request_t *r, ngx_uint_t request_line); +#if (NGX_HTTP_V2) +static void ngx_http_process_h2_preface(ngx_event_t *rev); +#endif + 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_unique_header_line(ngx_http_request_t *r, @@ -320,7 +327,7 @@ ngx_http_init_connection(ngx_connection_t *c) c->write->handler = ngx_http_empty_handler; #if (NGX_HTTP_V2) - if (hc->addr_conf->http2) { + if (hc->addr_conf->http2 && !hc->addr_conf->http1) { rev->handler = ngx_http_v2_init; } #endif @@ -939,14 +946,16 @@ ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg) static void ngx_http_process_request_line(ngx_event_t *rev) { - ssize_t n; - ngx_int_t rc, rv; - ngx_str_t host; - ngx_connection_t *c; - ngx_http_request_t *r; + ssize_t n; + ngx_int_t rc, rv; + ngx_str_t host; + ngx_connection_t *c; + ngx_http_connection_t *hc; + ngx_http_request_t *r; c = rev->data; r = c->data; + hc = r->http_connection; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http process request line"); @@ -1033,6 +1042,18 @@ ngx_http_process_request_line(ngx_event_t *rev) return; } +#if (NGX_HTTP_V2) + if (hc->addr_conf->http2 && (r->method & NGX_HTTP_PRI)) { + + c->log->action = "reading client h2 preface"; + + ngx_http_process_h2_preface(rev); + return; + } else if (r->method & NGX_HTTP_PRI) { + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + return; + } +#endif if (ngx_list_init(&r->headers_in.headers, r->pool, 20, sizeof(ngx_table_elt_t)) @@ -1208,6 +1229,45 @@ ngx_http_process_request_uri(ngx_http_request_t *r) return NGX_OK; } +#if (NGX_HTTP_V2) +static void +ngx_http_process_h2_preface(ngx_event_t *rev) +{ + size_t len; + ngx_connection_t *c; + ngx_http_request_t *r; + ngx_http_connection_t *hc; + ngx_http_v2_main_conf_t *h2mcf; + + c = rev->data; + r = c->data; + hc = r->http_connection; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http process h2 preface"); + + r->header_in->pos = r->header_in->start + 24; + + len = r->header_in->last - r->header_in->pos; + + h2mcf = ngx_http_get_module_main_conf(hc->conf_ctx, ngx_http_v2_module); + + if (len <= h2mcf->recv_buffer_size) { + c->data = r->http_connection; + + ngx_http_free_request(r, -1); + + ngx_http_v2_init_after_preface(rev, r->header_in); + return; + } + + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client sent invalid h2 preface"); + + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); +} +#endif + static void ngx_http_process_request_headers(ngx_event_t *rev) @@ -1809,7 +1869,7 @@ ngx_http_process_request_header(ngx_http_request_t *r) return NGX_ERROR; } - if (r->headers_in.host == NULL && r->http_version > NGX_HTTP_VERSION_10) { + if (r->headers_in.host == NULL && r->http_version == NGX_HTTP_VERSION_11) { ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "client sent HTTP/1.1 request without \"Host\" header"); ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); @@ -3506,7 +3566,9 @@ ngx_http_free_request(ngx_http_request_t *r, ngx_int_t rc) log->action = "logging request"; - ngx_http_log_request(r); + if (rc >= 0) { + ngx_http_log_request(r); + } log->action = "closing request"; diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h index 5d44c06e..34140655 100644 --- a/src/http/ngx_http_request.h +++ b/src/http/ngx_http_request.h @@ -41,6 +41,7 @@ #define NGX_HTTP_UNLOCK 0x2000 #define NGX_HTTP_PATCH 0x4000 #define NGX_HTTP_TRACE 0x8000 +#define NGX_HTTP_PRI 0x010000 #define NGX_HTTP_CONNECTION_CLOSE 1 #define NGX_HTTP_CONNECTION_KEEP_ALIVE 2 diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c index d9df0f90..ea72f129 100644 --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -230,6 +230,13 @@ static ngx_http_v2_parse_header_t ngx_http_v2_parse_headers[] = { void ngx_http_v2_init(ngx_event_t *rev) +{ + ngx_http_v2_init_after_preface(rev, NULL); +} + + +void +ngx_http_v2_init_after_preface(ngx_event_t *rev, ngx_buf_t *buf) { ngx_connection_t *c; ngx_pool_cleanup_t *cln; @@ -316,6 +323,12 @@ ngx_http_v2_init(ngx_event_t *rev) h2c->state.handler = hc->proxy_protocol ? ngx_http_v2_state_proxy_protocol : ngx_http_v2_state_preface; + if (buf != NULL) { + ngx_memcpy(h2mcf->recv_buffer, buf->pos, buf->last - buf->pos); + h2c->state.buffer_used = buf->last - buf->pos; + h2c->state.handler = ngx_http_v2_state_head; + } + ngx_queue_init(&h2c->waiting); ngx_queue_init(&h2c->dependencies); ngx_queue_init(&h2c->closed); @@ -381,13 +394,17 @@ ngx_http_v2_read_handler(ngx_event_t *rev) h2mcf = ngx_http_get_module_main_conf(h2c->http_connection->conf_ctx, ngx_http_v2_module); - available = h2mcf->recv_buffer_size - 2 * NGX_HTTP_V2_STATE_BUFFER_SIZE; + available = h2mcf->recv_buffer_size - h2c->state.buffer_used - 2 * NGX_HTTP_V2_STATE_BUFFER_SIZE; do { p = h2mcf->recv_buffer; - ngx_memcpy(p, h2c->state.buffer, NGX_HTTP_V2_STATE_BUFFER_SIZE); end = p + h2c->state.buffer_used; + if (h2c->state.buffer_used) { + goto do_state_handler; + } + + ngx_memcpy(p, h2c->state.buffer, NGX_HTTP_V2_STATE_BUFFER_SIZE); n = c->recv(c, end, available); @@ -410,6 +427,8 @@ ngx_http_v2_read_handler(ngx_event_t *rev) end += n; +do_state_handler: + h2c->state.buffer_used = 0; h2c->state.incomplete = 0; diff --git a/src/http/v2/ngx_http_v2.h b/src/http/v2/ngx_http_v2.h index d89e8fef..cc9251f0 100644 --- a/src/http/v2/ngx_http_v2.h +++ b/src/http/v2/ngx_http_v2.h @@ -279,6 +279,7 @@ ngx_http_v2_queue_ordered_frame(ngx_http_v2_connection_t *h2c, void ngx_http_v2_init(ngx_event_t *rev); +void ngx_http_v2_init_after_preface(ngx_event_t *rev, ngx_buf_t *buf); 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); > On Mar 4, 2018, at 10:53, Haitao Lv wrote: > > Hi, wbr, > > Thanks for your review. I don't know why I can't receive your email. > Let me reply directly. > >> It doesn't look like a useful feature. >> Could you please explain the use cases? > > The current implementation support both http/1 and http/2 over the > same TLS listen port by the ALPN Extension. > > Nginx also support listening http/2 over plain tcp port, which is > perfect for development and inner production environment. > > However, the current implementation cannot support http/1.1 and http/2 > simultaneously. We have no choice but listen on two different ports. > As a result, one same service has two ports and different clients > should choose their suitable port. > > Besides, the http/2 is efficient, but http/1 is simple. So I see no > chance that the http/2 will replace http/1 totally in the production > inner environment. Will call the inner API by both http/1 and http/2. > > So I think support http/1 and http/2 on the plain tcp port will simplify > both the production and development environment. > > >> What if the received data is bigger than h2mcf->recv_buffer? > > Yes, it is a problem. However, it can be fixed easily. > > As we know, the http/2 preface is PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n. > We could modify the ngx_http_parse_request_line and when we got A PRI > method, we need to get a single * uri. If we got things other than *, > we just return a invalid request response. By this, we will never got > a PRI to_much_long_uri HTTP/2.0 request line, and the buffer will not > be exhausted. So the ngx_http_alloc_large_header_buffer will not be called > during the handshake. After the ngx_http_parse_request_line, we will > ensure we got a PRI request, and the buffer size is client_header_buffer_size. > > And then we should the compare the r->header_in buffer's length and the > h2mcf->recv_buffer_size. If we got a buffered data larger than the > h2mcf->recv_buffer_size, we should send a bad request response. > > If this feature can be accepted, I will send a new patch. > > Thanks. > > >> On Mar 2, 2018, at 15:53, Haitao Lv wrote: >> >> # HG changeset patch >> # User ??? >> # Date 1519976498 -28800 >> # Fri Mar 02 15:41:38 2018 +0800 >> # Node ID 200955343460c4726015180f20c03e31c0b35ff6 >> # Parent 81fae70d6cb81c67607931ec3ecc585a609c97e0 >> make http2 server support http1 >> >> diff -r 81fae70d6cb8 -r 200955343460 src/http/ngx_http.c >> --- a/src/http/ngx_http.c Thu Mar 01 20:25:50 2018 +0300 >> +++ b/src/http/ngx_http.c Fri Mar 02 15:41:38 2018 +0800 >> @@ -1197,6 +1197,7 @@ >> ngx_uint_t ssl; >> #endif >> #if (NGX_HTTP_V2) >> + ngx_uint_t http1; >> ngx_uint_t http2; >> #endif >> >> @@ -1232,6 +1233,7 @@ >> ssl = lsopt->ssl || addr[i].opt.ssl; >> #endif >> #if (NGX_HTTP_V2) >> + http1 = lsopt->http1 || addr[i].opt.http1; >> http2 = lsopt->http2 || addr[i].opt.http2; >> #endif >> >> @@ -1266,6 +1268,7 @@ >> addr[i].opt.ssl = ssl; >> #endif >> #if (NGX_HTTP_V2) >> + addr[i].opt.http1 = http1; >> addr[i].opt.http2 = http2; >> #endif >> >> @@ -1802,6 +1805,7 @@ >> addrs[i].conf.ssl = addr[i].opt.ssl; >> #endif >> #if (NGX_HTTP_V2) >> + addrs[i].conf.http1 = addr[i].opt.http1; >> addrs[i].conf.http2 = addr[i].opt.http2; >> #endif >> addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol; >> @@ -1867,6 +1871,7 @@ >> addrs6[i].conf.ssl = addr[i].opt.ssl; >> #endif >> #if (NGX_HTTP_V2) >> + addrs6[i].conf.http1 = addr[i].opt.http1; >> addrs6[i].conf.http2 = addr[i].opt.http2; >> #endif >> addrs6[i].conf.proxy_protocol = addr[i].opt.proxy_protocol; >> diff -r 81fae70d6cb8 -r 200955343460 src/http/ngx_http_core_module.c >> --- a/src/http/ngx_http_core_module.c Thu Mar 01 20:25:50 2018 +0300 >> +++ b/src/http/ngx_http_core_module.c Fri Mar 02 15:41:38 2018 +0800 >> @@ -3985,6 +3985,18 @@ >> #endif >> } >> >> + if (ngx_strcmp(value[n].data, "http1") == 0) { >> +#if (NGX_HTTP_V2) >> + lsopt.http1 = 1; >> + continue; >> +#else >> + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, >> + "the \"http1\" parameter requires " >> + "ngx_http_v2_module"); >> + return NGX_CONF_ERROR; >> +#endif >> + } >> + >> if (ngx_strcmp(value[n].data, "spdy") == 0) { >> ngx_conf_log_error(NGX_LOG_WARN, cf, 0, >> "invalid parameter \"spdy\": " >> diff -r 81fae70d6cb8 -r 200955343460 src/http/ngx_http_core_module.h >> --- a/src/http/ngx_http_core_module.h Thu Mar 01 20:25:50 2018 +0300 >> +++ b/src/http/ngx_http_core_module.h Fri Mar 02 15:41:38 2018 +0800 >> @@ -73,6 +73,7 @@ >> unsigned bind:1; >> unsigned wildcard:1; >> unsigned ssl:1; >> + unsigned http1:1; >> unsigned http2:1; >> #if (NGX_HAVE_INET6) >> unsigned ipv6only:1; >> @@ -234,6 +235,7 @@ >> ngx_http_virtual_names_t *virtual_names; >> >> unsigned ssl:1; >> + unsigned http1:1; >> unsigned http2:1; >> unsigned proxy_protocol:1; >> }; >> diff -r 81fae70d6cb8 -r 200955343460 src/http/ngx_http_parse.c >> --- a/src/http/ngx_http_parse.c Thu Mar 01 20:25:50 2018 +0300 >> +++ b/src/http/ngx_http_parse.c Fri Mar 02 15:41:38 2018 +0800 >> @@ -285,6 +285,14 @@ >> break; >> } >> >> +#if (NGX_HTTP_V2) >> + if (ch == '*') { >> + r->uri_start = p; >> + state = sw_uri; >> + break; >> + } >> +#endif >> + >> c = (u_char) (ch | 0x20); >> if (c >= 'a' && c <= 'z') { >> r->schema_start = p; >> @@ -724,9 +732,11 @@ >> >> r->http_major = ch - '0'; >> >> +#if !(NGX_HTTP_V2) >> if (r->http_major > 1) { >> return NGX_HTTP_PARSE_INVALID_VERSION; >> } >> +#endif >> >> state = sw_major_digit; >> break; >> @@ -744,9 +754,11 @@ >> >> r->http_major = r->http_major * 10 + (ch - '0'); >> >> +#if !(NGX_HTTP_V2) >> if (r->http_major > 1) { >> return NGX_HTTP_PARSE_INVALID_VERSION; >> } >> +#endif >> >> break; >> >> diff -r 81fae70d6cb8 -r 200955343460 src/http/ngx_http_request.c >> --- a/src/http/ngx_http_request.c Thu Mar 01 20:25:50 2018 +0300 >> +++ b/src/http/ngx_http_request.c Fri Mar 02 15:41:38 2018 +0800 >> @@ -17,6 +17,10 @@ >> static ngx_int_t ngx_http_alloc_large_header_buffer(ngx_http_request_t *r, >> ngx_uint_t request_line); >> >> +#if (NGX_HTTP_V2) >> +static void ngx_http_process_h2_preface(ngx_event_t *rev); >> +#endif >> + >> 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_unique_header_line(ngx_http_request_t *r, >> @@ -320,7 +324,7 @@ >> c->write->handler = ngx_http_empty_handler; >> >> #if (NGX_HTTP_V2) >> - if (hc->addr_conf->http2) { >> + if (hc->addr_conf->http2 && !hc->addr_conf->http1) { >> rev->handler = ngx_http_v2_init; >> } >> #endif >> @@ -1033,6 +1037,20 @@ >> return; >> } >> >> +#if (NGX_HTTP_V2) >> + if (r->http_connection->addr_conf->http2 >> + && r->http_version >= NGX_HTTP_VERSION_20) { >> + >> + c->log->action = "reading client h2 preface"; >> + >> + rev->handler = ngx_http_process_h2_preface; >> + ngx_http_process_h2_preface(rev); >> + return; >> + } else if (r->http_version >= NGX_HTTP_VERSION_20) { >> + ngx_http_finalize_request(r, NGX_HTTP_VERSION_NOT_SUPPORTED); >> + return; >> + } >> +#endif >> >> if (ngx_list_init(&r->headers_in.headers, r->pool, 20, >> sizeof(ngx_table_elt_t)) >> @@ -1208,6 +1226,64 @@ >> return NGX_OK; >> } >> >> +#if (NGX_HTTP_V2) >> +static void >> +ngx_http_process_h2_preface(ngx_event_t *rev) >> +{ >> + size_t len; >> + ssize_t n; >> + ngx_connection_t *c; >> + ngx_http_request_t *r; >> + >> + c = rev->data; >> + r = c->data; >> + >> + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, >> + "http process h2 preface"); >> + >> + if (rev->timedout) { >> + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); >> + c->timedout = 1; >> + ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT); >> + return; >> + } >> + >> + n = ngx_http_read_request_header(r); >> + >> + if (n == NGX_AGAIN || n == NGX_ERROR) { >> + return; >> + } >> + >> + len = r->header_in->last - r->header_in->start; >> + >> + if (len < sizeof("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n") - 1) { >> + r->header_in->pos = r->header_in->last; >> + return; >> + } >> + >> + if (ngx_strncmp(r->header_in->start, >> + "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", 24) == 0) { >> + >> + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, >> + "h2 preface done"); >> + >> + r->header_in->pos = r->header_in->start + 24; >> + >> + c->data = r->http_connection; >> + >> + r->http_connection = NULL; >> + >> + ngx_http_v2_init_after_preface(rev, r->header_in); >> + return; >> + } >> + >> + ngx_log_error(NGX_LOG_INFO, c->log, 0, >> + "client sent invalid h2 preface"); >> + >> + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); >> +} >> +#endif >> + >> >> static void >> ngx_http_process_request_headers(ngx_event_t *rev) >> @@ -1809,7 +1885,7 @@ >> return NGX_ERROR; >> } >> >> - if (r->headers_in.host == NULL && r->http_version > NGX_HTTP_VERSION_10) { >> + if (r->headers_in.host == NULL && r->http_version == NGX_HTTP_VERSION_11) { >> ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, >> "client sent HTTP/1.1 request without \"Host\" header"); >> ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); >> diff -r 81fae70d6cb8 -r 200955343460 src/http/v2/ngx_http_v2.c >> --- a/src/http/v2/ngx_http_v2.c Thu Mar 01 20:25:50 2018 +0300 >> +++ b/src/http/v2/ngx_http_v2.c Fri Mar 02 15:41:38 2018 +0800 >> @@ -231,6 +231,13 @@ >> void >> ngx_http_v2_init(ngx_event_t *rev) >> { >> + ngx_http_v2_init_after_preface(rev, NULL); >> +} >> + >> + >> +void >> +ngx_http_v2_init_after_preface(ngx_event_t *rev, ngx_buf_t *buf) >> +{ >> ngx_connection_t *c; >> ngx_pool_cleanup_t *cln; >> ngx_http_connection_t *hc; >> @@ -316,6 +323,12 @@ >> h2c->state.handler = hc->proxy_protocol ? ngx_http_v2_state_proxy_protocol >> : ngx_http_v2_state_preface; >> >> + if (buf != NULL) { >> + ngx_memcpy(h2mcf->recv_buffer, buf->pos, buf->last - buf->pos); >> + h2c->state.buffer_used = buf->last - buf->pos; >> + h2c->state.handler = ngx_http_v2_state_head; >> + } >> + >> ngx_queue_init(&h2c->waiting); >> ngx_queue_init(&h2c->dependencies); >> ngx_queue_init(&h2c->closed); >> @@ -381,13 +394,18 @@ >> h2mcf = ngx_http_get_module_main_conf(h2c->http_connection->conf_ctx, >> ngx_http_v2_module); >> >> - available = h2mcf->recv_buffer_size - 2 * NGX_HTTP_V2_STATE_BUFFER_SIZE; >> + available = h2mcf->recv_buffer_size - h2c->state.buffer_used - 2 * NGX_HTTP_V2_STATE_BUFFER_SIZE; >> >> do { >> p = h2mcf->recv_buffer; >> >> + end = p + h2c->state.buffer_used; >> + >> + if (h2c->state.buffer_used) { >> + goto do_state_handler; >> + } >> + >> ngx_memcpy(p, h2c->state.buffer, NGX_HTTP_V2_STATE_BUFFER_SIZE); >> - end = p + h2c->state.buffer_used; >> >> n = c->recv(c, end, available); >> >> @@ -410,6 +428,8 @@ >> >> end += n; >> >> +do_state_handler: >> + >> h2c->state.buffer_used = 0; >> h2c->state.incomplete = 0; >> >> diff -r 81fae70d6cb8 -r 200955343460 src/http/v2/ngx_http_v2.h >> --- a/src/http/v2/ngx_http_v2.h Thu Mar 01 20:25:50 2018 +0300 >> +++ b/src/http/v2/ngx_http_v2.h Fri Mar 02 15:41:38 2018 +0800 >> @@ -279,6 +279,7 @@ >> >> >> void ngx_http_v2_init(ngx_event_t *rev); >> +void ngx_http_v2_init_after_preface(ngx_event_t *rev, ngx_buf_t *buf); >> >> 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); >> > From vbart at nginx.com Sun Mar 4 13:00:42 2018 From: vbart at nginx.com (Valentin V. Bartenev) Date: Sun, 04 Mar 2018 16:00:42 +0300 Subject: [PATCH] HTTP/2: make http2 server support http1 In-Reply-To: <762DEAA4-5D48-4957-9DEE-BFF82869EC0C@lvht.net> References: <762DEAA4-5D48-4957-9DEE-BFF82869EC0C@lvht.net> Message-ID: <3570629.QAof0xxDjD@vbart-laptop> On Sunday, 4 March 2018 05:53:36 MSK Haitao Lv wrote: > Hi, wbr, > > Thanks for your review. I don't know why I can't receive your email. > Let me reply directly. > > > It doesn't look like a useful feature. > > Could you please explain the use cases? > > The current implementation support both http/1 and http/2 over the > same TLS listen port by the ALPN Extension. > > Nginx also support listening http/2 over plain tcp port, which is > perfect for development and inner production environment. > > However, the current implementation cannot support http/1.1 and http/2 > simultaneously. We have no choice but listen on two different ports. > As a result, one same service has two ports and different clients > should choose their suitable port. > > Besides, the http/2 is efficient, but http/1 is simple. So I see no > chance that the http/2 will replace http/1 totally in the production > inner environment. Will call the inner API by both http/1 and http/2. > > So I think support http/1 and http/2 on the plain tcp port will simplify > both the production and development environment. > HTTP/2 was designed to save connection time between a client and a web server, which can be costly with significant RTT and full TLS handshake. Also it helps to overcome concurrency limitation in browsers, that usually limited to 6 connection per host. All these problems usually don't exist in inner environments. HTTP/2 isn't efficient. In fact, it has more overhead and may require more TCP packets to transfer the same amount of data. Moreover, more TCP connections are usually faster than one, just because they have bigger start window in total and less suffer from packet loss. Please, don't be fooled by aggressive marketing around HTTP/2. Most of such articles are written by people who barely understand how network works. HTTP/2 is neither a better version nor the "next" version of HTTP protocol (the name is misleading). It's an another protocol designed to solve some specific cases, while introducing many other problems. I don't recommend to use HTTP/2 in cases other than TLS connections between browsers and web servers in public network. Also even for this purpose, there are cases when HTTP/2 isn't recommended, e.g. unreliable networks with packet loss (as mobile networks). You can easily find on YouTube a talk by Hooman Beheshti called "HTTP/2: what no one is telling you" with some interesting research. nginx support HTTP/2 over plain TCP for a number of reasons: 1. for easy testing and debugging the protocol implementation in nginx; 2. that required almost no additional code to implement; 3. there's a use case when TLS termination is done by simple TCP proxy and then the traffic routed to HTTP/1 or HTTP/2 nginx ports according to ALPN. > > > What if the received data is bigger than h2mcf->recv_buffer? > > Yes, it is a problem. However, it can be fixed easily. > > As we know, the http/2 preface is PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n. > We could modify the ngx_http_parse_request_line and when we got A PRI > method, we need to get a single * uri. If we got things other than *, > we just return a invalid request response. By this, we will never got > a PRI to_much_long_uri HTTP/2.0 request line, and the buffer will not > be exhausted. So the ngx_http_alloc_large_header_buffer will not be called > during the handshake. After the ngx_http_parse_request_line, we will > ensure we got a PRI request, and the buffer size is client_header_buffer_size. > As far as I can see, your reasoning is based on assumption that client_header_buffer_size is always smaller than http2_recv_buffer_size. That simply isn't true as both can be easily configured by users. wbr, Valentin V. Bartenev From i at lvht.net Mon Mar 5 03:03:55 2018 From: i at lvht.net (Haitao Lv) Date: Mon, 5 Mar 2018 11:03:55 +0800 Subject: [PATCH] HTTP/2: make http2 server support http1 In-Reply-To: <3570629.QAof0xxDjD@vbart-laptop> References: <762DEAA4-5D48-4957-9DEE-BFF82869EC0C@lvht.net> <3570629.QAof0xxDjD@vbart-laptop> Message-ID: <5747AFA0-8503-48BD-88C7-4DCDB64522FD@lvht.net> > On Mar 4, 2018, at 21:00, Valentin V. Bartenev wrote: > > On Sunday, 4 March 2018 05:53:36 MSK Haitao Lv wrote: >> Hi, wbr, >> >> Thanks for your review. I don't know why I can't receive your email. >> Let me reply directly. >> >>> It doesn't look like a useful feature. >>> Could you please explain the use cases? >> >> The current implementation support both http/1 and http/2 over the >> same TLS listen port by the ALPN Extension. >> >> Nginx also support listening http/2 over plain tcp port, which is >> perfect for development and inner production environment. >> >> However, the current implementation cannot support http/1.1 and http/2 >> simultaneously. We have no choice but listen on two different ports. >> As a result, one same service has two ports and different clients >> should choose their suitable port. >> >> Besides, the http/2 is efficient, but http/1 is simple. So I see no >> chance that the http/2 will replace http/1 totally in the production >> inner environment. Will call the inner API by both http/1 and http/2. >> >> So I think support http/1 and http/2 on the plain tcp port will simplify >> both the production and development environment. >> > > HTTP/2 was designed to save connection time between a client and a web > server, which can be costly with significant RTT and full TLS handshake. > > Also it helps to overcome concurrency limitation in browsers, that usually > limited to 6 connection per host. > > All these problems usually don't exist in inner environments. > > HTTP/2 isn't efficient. In fact, it has more overhead and may require > more TCP packets to transfer the same amount of data. Moreover, more TCP > connections are usually faster than one, just because they have bigger start > window in total and less suffer from packet loss. > > Please, don't be fooled by aggressive marketing around HTTP/2. Most of > such articles are written by people who barely understand how network works. > HTTP/2 is neither a better version nor the "next" version of HTTP protocol > (the name is misleading). It's an another protocol designed to solve some > specific cases, while introducing many other problems. > > I don't recommend to use HTTP/2 in cases other than TLS connections between > browsers and web servers in public network. Also even for this purpose, > there are cases when HTTP/2 isn't recommended, e.g. unreliable networks > with packet loss (as mobile networks). > > You can easily find on YouTube a talk by Hooman Beheshti called > "HTTP/2: what no one is telling you" with some interesting research. > > nginx support HTTP/2 over plain TCP for a number of reasons: > > 1. for easy testing and debugging the protocol implementation in nginx; > > 2. that required almost no additional code to implement; > > 3. there's a use case when TLS termination is done by simple TCP proxy > and then the traffic routed to HTTP/1 or HTTP/2 nginx ports according > to ALPN. My actual use case is simple. My compony is going to introduce the gRPC. And the gRPC depends on the HTTP/2 and it's trailer headers. We have many php legacy code which expose API by HTTP/1. So we want to build gRPC server by PHP. A simple solution is to send gRPC payload by HTTP/1 and let nginx transfer it to HTTP/2. And the grpc-status, grpc-message header that should be transferred by the HTTP/2 trailer header could be transferred by the normal HTTP/1 header. By this way, we can support both HTTP/1 API and unary-call gRPC. > >> >>> What if the received data is bigger than h2mcf->recv_buffer? >> >> Yes, it is a problem. However, it can be fixed easily. >> >> As we know, the http/2 preface is PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n. >> We could modify the ngx_http_parse_request_line and when we got A PRI >> method, we need to get a single * uri. If we got things other than *, >> we just return a invalid request response. By this, we will never got >> a PRI to_much_long_uri HTTP/2.0 request line, and the buffer will not >> be exhausted. So the ngx_http_alloc_large_header_buffer will not be called >> during the handshake. After the ngx_http_parse_request_line, we will >> ensure we got a PRI request, and the buffer size is client_header_buffer_size. >> > > As far as I can see, your reasoning is based on assumption that > client_header_buffer_size is always smaller than http2_recv_buffer_size. > > That simply isn't true as both can be easily configured by users. Let me explain in the new patch. > > wbr, Valentin V. Bartenev > > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel From i at lvht.net Mon Mar 5 03:11:08 2018 From: i at lvht.net (Haitao Lv) Date: Mon, 5 Mar 2018 11:11:08 +0800 Subject: [PATCH] HTTP/2: make http2 server support http1 In-Reply-To: References: <762DEAA4-5D48-4957-9DEE-BFF82869EC0C@lvht.net> Message-ID: <8E2C1E9B-E675-49EB-9D8B-C00E94677B4A@lvht.net> @wbr > On Mar 4, 2018, at 13:49, Haitao Lv wrote: > > Here is the new patch > > diff --git a/src/http/ngx_http.c b/src/http/ngx_http.c > index 9d8b6d79..c06ef7c7 100644 > --- a/src/http/ngx_http.c > +++ b/src/http/ngx_http.c > @@ -1197,6 +1197,7 @@ ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, > ngx_uint_t ssl; > #endif > #if (NGX_HTTP_V2) > + ngx_uint_t http1; > ngx_uint_t http2; > #endif > > @@ -1232,6 +1233,7 @@ ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, > ssl = lsopt->ssl || addr[i].opt.ssl; > #endif > #if (NGX_HTTP_V2) > + http1 = lsopt->http1 || addr[i].opt.http1; > http2 = lsopt->http2 || addr[i].opt.http2; > #endif > > @@ -1266,6 +1268,7 @@ ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, > addr[i].opt.ssl = ssl; > #endif > #if (NGX_HTTP_V2) > + addr[i].opt.http1 = http1; > addr[i].opt.http2 = http2; > #endif > > @@ -1802,6 +1805,7 @@ ngx_http_add_addrs(ngx_conf_t *cf, ngx_http_port_t *hport, > addrs[i].conf.ssl = addr[i].opt.ssl; > #endif > #if (NGX_HTTP_V2) > + addrs[i].conf.http1 = addr[i].opt.http1; > addrs[i].conf.http2 = addr[i].opt.http2; > #endif > addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol; > @@ -1867,6 +1871,7 @@ ngx_http_add_addrs6(ngx_conf_t *cf, ngx_http_port_t *hport, > addrs6[i].conf.ssl = addr[i].opt.ssl; > #endif > #if (NGX_HTTP_V2) > + addrs6[i].conf.http1 = addr[i].opt.http1; > addrs6[i].conf.http2 = addr[i].opt.http2; > #endif > addrs6[i].conf.proxy_protocol = addr[i].opt.proxy_protocol; > diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c > index 6b318dd0..741a4dd9 100644 > --- a/src/http/ngx_http_core_module.c > +++ b/src/http/ngx_http_core_module.c > @@ -3985,6 +3985,18 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) > #endif > } > > + if (ngx_strcmp(value[n].data, "http1") == 0) { > +#if (NGX_HTTP_V2) > + lsopt.http1 = 1; > + continue; > +#else > + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, > + "the \"http1\" parameter requires " > + "ngx_http_v2_module"); > + return NGX_CONF_ERROR; > +#endif > + } > + > if (ngx_strcmp(value[n].data, "spdy") == 0) { > ngx_conf_log_error(NGX_LOG_WARN, cf, 0, > "invalid parameter \"spdy\": " > diff --git a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h > index d7985049..68b3e7d9 100644 > --- a/src/http/ngx_http_core_module.h > +++ b/src/http/ngx_http_core_module.h > @@ -73,6 +73,7 @@ typedef struct { > unsigned bind:1; > unsigned wildcard:1; > unsigned ssl:1; > + unsigned http1:1; > unsigned http2:1; > #if (NGX_HAVE_INET6) > unsigned ipv6only:1; > @@ -234,6 +235,7 @@ struct ngx_http_addr_conf_s { > ngx_http_virtual_names_t *virtual_names; > > unsigned ssl:1; > + unsigned http1:1; > unsigned http2:1; > unsigned proxy_protocol:1; > }; > diff --git a/src/http/ngx_http_parse.c b/src/http/ngx_http_parse.c > index 844054c9..9eb713b7 100644 > --- a/src/http/ngx_http_parse.c > +++ b/src/http/ngx_http_parse.c > @@ -117,6 +117,7 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) > sw_host_ip_literal, > sw_port, > sw_host_http_09, > + sw_h2_preface, > sw_after_slash_in_uri, > sw_check_uri, > sw_check_uri_http_09, > @@ -134,6 +135,12 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) > sw_almost_done > } state; > > +#if (NGX_HTTP_V2) > + static u_char h2_preface[] = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"; > + u_char *h2_preface_end = h2_preface + 24; > + u_char *n = h2_preface + 4; > +#endif > + > state = r->state; > > for (p = b->pos; p < b->last; p++) { > @@ -145,10 +152,6 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) > case sw_start: > r->request_start = p; > > - if (ch == CR || ch == LF) { > - break; > - } > - I think Nginx should not allow any leading \r or \n. HTTP client should never send this chars before the request line. Support this feature makes the buffer management more harder. > if ((ch < 'A' || ch > 'Z') && ch != '_' && ch != '-') { > return NGX_HTTP_PARSE_INVALID_METHOD; > } > @@ -174,6 +177,13 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) > break; > } > > +#if (NGX_HTTP_V2) > + if (ngx_str3_cmp(m, 'P', 'R', 'I', ' ')) { > + r->method = NGX_HTTP_PRI; > + break; > + } > +#endif > + > break; > > case 4: > @@ -267,6 +277,14 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) > } > > state = sw_spaces_before_uri; > + > +#if (NGX_HTTP_V2) > + if (r->method & NGX_HTTP_PRI) { > + r->uri_start = p; > + state = sw_h2_preface; > + } > +#endif > + > break; > } > > @@ -276,6 +294,29 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) > > break; > > +#if (NGX_HTTP_V2) > + case sw_h2_preface: > + > + if (ch == *n++) { I introduce a new sw_h2_preface state. In this state, the parser will check the h2 preface char by char. The parser only check at most 20 chars "* HTTP/2.0\r\n\r\nSM\r\n\r\n". So the buffer will not be fulfilled. > + if (n == h2_preface_end) { > + r->request_end = r->request_start + 14; > + r->uri_start = r->request_start + 4; > + r->uri_end = r->request_start + 5; > + r->http_protocol.data = r->request_start + 6; > + r->http_major = 2; > + r->http_minor = 0; > + > + goto done; > + } > + > + break; > + } > + > + return NGX_HTTP_PARSE_INVALID_METHOD; > + > + break; > +#endif > + > /* space* before URI */ > case sw_spaces_before_uri: > > @@ -724,9 +765,11 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) > > r->http_major = ch - '0'; > > +#if !(NGX_HTTP_V2) > if (r->http_major > 1) { > return NGX_HTTP_PARSE_INVALID_VERSION; > } > +#endif > > state = sw_major_digit; > break; > @@ -744,9 +787,11 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) > > r->http_major = r->http_major * 10 + (ch - '0'); > > +#if !(NGX_HTTP_V2) > if (r->http_major > 1) { > return NGX_HTTP_PARSE_INVALID_VERSION; > } > +#endif > > break; > > diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c > index 89cfe77a..3ae64569 100644 > --- a/src/http/ngx_http_request.c > +++ b/src/http/ngx_http_request.c > @@ -8,6 +8,9 @@ > #include > #include > #include > +#if (NGX_HTTP_V2) > +#include > +#endif > > > static void ngx_http_wait_request_handler(ngx_event_t *ev); > @@ -17,6 +20,10 @@ static ssize_t ngx_http_read_request_header(ngx_http_request_t *r); > static ngx_int_t ngx_http_alloc_large_header_buffer(ngx_http_request_t *r, > ngx_uint_t request_line); > > +#if (NGX_HTTP_V2) > +static void ngx_http_process_h2_preface(ngx_event_t *rev); > +#endif > + > 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_unique_header_line(ngx_http_request_t *r, > @@ -320,7 +327,7 @@ ngx_http_init_connection(ngx_connection_t *c) > c->write->handler = ngx_http_empty_handler; > > #if (NGX_HTTP_V2) > - if (hc->addr_conf->http2) { > + if (hc->addr_conf->http2 && !hc->addr_conf->http1) { > rev->handler = ngx_http_v2_init; > } > #endif > @@ -939,14 +946,16 @@ ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg) > static void > ngx_http_process_request_line(ngx_event_t *rev) > { > - ssize_t n; > - ngx_int_t rc, rv; > - ngx_str_t host; > - ngx_connection_t *c; > - ngx_http_request_t *r; > + ssize_t n; > + ngx_int_t rc, rv; > + ngx_str_t host; > + ngx_connection_t *c; > + ngx_http_connection_t *hc; > + ngx_http_request_t *r; > > c = rev->data; > r = c->data; > + hc = r->http_connection; > > ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, > "http process request line"); > @@ -1033,6 +1042,18 @@ ngx_http_process_request_line(ngx_event_t *rev) > return; > } > > +#if (NGX_HTTP_V2) > + if (hc->addr_conf->http2 && (r->method & NGX_HTTP_PRI)) { > + > + c->log->action = "reading client h2 preface"; > + > + ngx_http_process_h2_preface(rev); > + return; > + } else if (r->method & NGX_HTTP_PRI) { > + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); > + return; > + } > +#endif > > if (ngx_list_init(&r->headers_in.headers, r->pool, 20, > sizeof(ngx_table_elt_t)) > @@ -1208,6 +1229,45 @@ ngx_http_process_request_uri(ngx_http_request_t *r) > return NGX_OK; > } > > +#if (NGX_HTTP_V2) > +static void > +ngx_http_process_h2_preface(ngx_event_t *rev) > +{ > + size_t len; > + ngx_connection_t *c; > + ngx_http_request_t *r; > + ngx_http_connection_t *hc; > + ngx_http_v2_main_conf_t *h2mcf; > + > + c = rev->data; > + r = c->data; > + hc = r->http_connection; > + > + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, > + "http process h2 preface"); > + > + r->header_in->pos = r->header_in->start + 24; > + > + len = r->header_in->last - r->header_in->pos; > + > + h2mcf = ngx_http_get_module_main_conf(hc->conf_ctx, ngx_http_v2_module); > + > + if (len <= h2mcf->recv_buffer_size) { We check the recv buffer size before do the ngx_http_v2_init_after_preface. > + c->data = r->http_connection; > + > + ngx_http_free_request(r, -1); > + > + ngx_http_v2_init_after_preface(rev, r->header_in); > + return; > + } > + > + ngx_log_error(NGX_LOG_INFO, c->log, 0, > + "client sent invalid h2 preface"); > + > + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); > +} > +#endif > + > > static void > ngx_http_process_request_headers(ngx_event_t *rev) > @@ -1809,7 +1869,7 @@ ngx_http_process_request_header(ngx_http_request_t *r) > return NGX_ERROR; > } > > - if (r->headers_in.host == NULL && r->http_version > NGX_HTTP_VERSION_10) { > + if (r->headers_in.host == NULL && r->http_version == NGX_HTTP_VERSION_11) { > ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, > "client sent HTTP/1.1 request without \"Host\" header"); > ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); > @@ -3506,7 +3566,9 @@ ngx_http_free_request(ngx_http_request_t *r, ngx_int_t rc) > > log->action = "logging request"; > > - ngx_http_log_request(r); > + if (rc >= 0) { > + ngx_http_log_request(r); > + } > > log->action = "closing request"; > > diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h > index 5d44c06e..34140655 100644 > --- a/src/http/ngx_http_request.h > +++ b/src/http/ngx_http_request.h > @@ -41,6 +41,7 @@ > #define NGX_HTTP_UNLOCK 0x2000 > #define NGX_HTTP_PATCH 0x4000 > #define NGX_HTTP_TRACE 0x8000 > +#define NGX_HTTP_PRI 0x010000 > > #define NGX_HTTP_CONNECTION_CLOSE 1 > #define NGX_HTTP_CONNECTION_KEEP_ALIVE 2 > diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c > index d9df0f90..ea72f129 100644 > --- a/src/http/v2/ngx_http_v2.c > +++ b/src/http/v2/ngx_http_v2.c > @@ -230,6 +230,13 @@ static ngx_http_v2_parse_header_t ngx_http_v2_parse_headers[] = { > > void > ngx_http_v2_init(ngx_event_t *rev) > +{ > + ngx_http_v2_init_after_preface(rev, NULL); > +} > + > + > +void > +ngx_http_v2_init_after_preface(ngx_event_t *rev, ngx_buf_t *buf) > { > ngx_connection_t *c; > ngx_pool_cleanup_t *cln; > @@ -316,6 +323,12 @@ ngx_http_v2_init(ngx_event_t *rev) > h2c->state.handler = hc->proxy_protocol ? ngx_http_v2_state_proxy_protocol > : ngx_http_v2_state_preface; > > + if (buf != NULL) { > + ngx_memcpy(h2mcf->recv_buffer, buf->pos, buf->last - buf->pos); > + h2c->state.buffer_used = buf->last - buf->pos; > + h2c->state.handler = ngx_http_v2_state_head; > + } > + > ngx_queue_init(&h2c->waiting); > ngx_queue_init(&h2c->dependencies); > ngx_queue_init(&h2c->closed); > @@ -381,13 +394,17 @@ ngx_http_v2_read_handler(ngx_event_t *rev) > h2mcf = ngx_http_get_module_main_conf(h2c->http_connection->conf_ctx, > ngx_http_v2_module); > > - available = h2mcf->recv_buffer_size - 2 * NGX_HTTP_V2_STATE_BUFFER_SIZE; > + available = h2mcf->recv_buffer_size - h2c->state.buffer_used - 2 * NGX_HTTP_V2_STATE_BUFFER_SIZE; > > do { > p = h2mcf->recv_buffer; > > - ngx_memcpy(p, h2c->state.buffer, NGX_HTTP_V2_STATE_BUFFER_SIZE); > end = p + h2c->state.buffer_used; > + if (h2c->state.buffer_used) { > + goto do_state_handler; > + } > + > + ngx_memcpy(p, h2c->state.buffer, NGX_HTTP_V2_STATE_BUFFER_SIZE); > > n = c->recv(c, end, available); > > @@ -410,6 +427,8 @@ ngx_http_v2_read_handler(ngx_event_t *rev) > > end += n; > > +do_state_handler: > + > h2c->state.buffer_used = 0; > h2c->state.incomplete = 0; > > diff --git a/src/http/v2/ngx_http_v2.h b/src/http/v2/ngx_http_v2.h > index d89e8fef..cc9251f0 100644 > --- a/src/http/v2/ngx_http_v2.h > +++ b/src/http/v2/ngx_http_v2.h > @@ -279,6 +279,7 @@ ngx_http_v2_queue_ordered_frame(ngx_http_v2_connection_t *h2c, > > > void ngx_http_v2_init(ngx_event_t *rev); > +void ngx_http_v2_init_after_preface(ngx_event_t *rev, ngx_buf_t *buf); > > 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); > > >> On Mar 4, 2018, at 10:53, Haitao Lv wrote: >> >> Hi, wbr, >> >> Thanks for your review. I don't know why I can't receive your email. >> Let me reply directly. >> >>> It doesn't look like a useful feature. >>> Could you please explain the use cases? >> >> The current implementation support both http/1 and http/2 over the >> same TLS listen port by the ALPN Extension. >> >> Nginx also support listening http/2 over plain tcp port, which is >> perfect for development and inner production environment. >> >> However, the current implementation cannot support http/1.1 and http/2 >> simultaneously. We have no choice but listen on two different ports. >> As a result, one same service has two ports and different clients >> should choose their suitable port. >> >> Besides, the http/2 is efficient, but http/1 is simple. So I see no >> chance that the http/2 will replace http/1 totally in the production >> inner environment. Will call the inner API by both http/1 and http/2. >> >> So I think support http/1 and http/2 on the plain tcp port will simplify >> both the production and development environment. >> >> >>> What if the received data is bigger than h2mcf->recv_buffer? >> >> Yes, it is a problem. However, it can be fixed easily. >> >> As we know, the http/2 preface is PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n. >> We could modify the ngx_http_parse_request_line and when we got A PRI >> method, we need to get a single * uri. If we got things other than *, >> we just return a invalid request response. By this, we will never got >> a PRI to_much_long_uri HTTP/2.0 request line, and the buffer will not >> be exhausted. So the ngx_http_alloc_large_header_buffer will not be called >> during the handshake. After the ngx_http_parse_request_line, we will >> ensure we got a PRI request, and the buffer size is client_header_buffer_size. >> >> And then we should the compare the r->header_in buffer's length and the >> h2mcf->recv_buffer_size. If we got a buffered data larger than the >> h2mcf->recv_buffer_size, we should send a bad request response. >> >> If this feature can be accepted, I will send a new patch. >> >> Thanks. >> >> >>> On Mar 2, 2018, at 15:53, Haitao Lv wrote: >>> >>> # HG changeset patch >>> # User ??? >>> # Date 1519976498 -28800 >>> # Fri Mar 02 15:41:38 2018 +0800 >>> # Node ID 200955343460c4726015180f20c03e31c0b35ff6 >>> # Parent 81fae70d6cb81c67607931ec3ecc585a609c97e0 >>> make http2 server support http1 >>> >>> diff -r 81fae70d6cb8 -r 200955343460 src/http/ngx_http.c >>> --- a/src/http/ngx_http.c Thu Mar 01 20:25:50 2018 +0300 >>> +++ b/src/http/ngx_http.c Fri Mar 02 15:41:38 2018 +0800 >>> @@ -1197,6 +1197,7 @@ >>> ngx_uint_t ssl; >>> #endif >>> #if (NGX_HTTP_V2) >>> + ngx_uint_t http1; >>> ngx_uint_t http2; >>> #endif >>> >>> @@ -1232,6 +1233,7 @@ >>> ssl = lsopt->ssl || addr[i].opt.ssl; >>> #endif >>> #if (NGX_HTTP_V2) >>> + http1 = lsopt->http1 || addr[i].opt.http1; >>> http2 = lsopt->http2 || addr[i].opt.http2; >>> #endif >>> >>> @@ -1266,6 +1268,7 @@ >>> addr[i].opt.ssl = ssl; >>> #endif >>> #if (NGX_HTTP_V2) >>> + addr[i].opt.http1 = http1; >>> addr[i].opt.http2 = http2; >>> #endif >>> >>> @@ -1802,6 +1805,7 @@ >>> addrs[i].conf.ssl = addr[i].opt.ssl; >>> #endif >>> #if (NGX_HTTP_V2) >>> + addrs[i].conf.http1 = addr[i].opt.http1; >>> addrs[i].conf.http2 = addr[i].opt.http2; >>> #endif >>> addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol; >>> @@ -1867,6 +1871,7 @@ >>> addrs6[i].conf.ssl = addr[i].opt.ssl; >>> #endif >>> #if (NGX_HTTP_V2) >>> + addrs6[i].conf.http1 = addr[i].opt.http1; >>> addrs6[i].conf.http2 = addr[i].opt.http2; >>> #endif >>> addrs6[i].conf.proxy_protocol = addr[i].opt.proxy_protocol; >>> diff -r 81fae70d6cb8 -r 200955343460 src/http/ngx_http_core_module.c >>> --- a/src/http/ngx_http_core_module.c Thu Mar 01 20:25:50 2018 +0300 >>> +++ b/src/http/ngx_http_core_module.c Fri Mar 02 15:41:38 2018 +0800 >>> @@ -3985,6 +3985,18 @@ >>> #endif >>> } >>> >>> + if (ngx_strcmp(value[n].data, "http1") == 0) { >>> +#if (NGX_HTTP_V2) >>> + lsopt.http1 = 1; >>> + continue; >>> +#else >>> + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, >>> + "the \"http1\" parameter requires " >>> + "ngx_http_v2_module"); >>> + return NGX_CONF_ERROR; >>> +#endif >>> + } >>> + >>> if (ngx_strcmp(value[n].data, "spdy") == 0) { >>> ngx_conf_log_error(NGX_LOG_WARN, cf, 0, >>> "invalid parameter \"spdy\": " >>> diff -r 81fae70d6cb8 -r 200955343460 src/http/ngx_http_core_module.h >>> --- a/src/http/ngx_http_core_module.h Thu Mar 01 20:25:50 2018 +0300 >>> +++ b/src/http/ngx_http_core_module.h Fri Mar 02 15:41:38 2018 +0800 >>> @@ -73,6 +73,7 @@ >>> unsigned bind:1; >>> unsigned wildcard:1; >>> unsigned ssl:1; >>> + unsigned http1:1; >>> unsigned http2:1; >>> #if (NGX_HAVE_INET6) >>> unsigned ipv6only:1; >>> @@ -234,6 +235,7 @@ >>> ngx_http_virtual_names_t *virtual_names; >>> >>> unsigned ssl:1; >>> + unsigned http1:1; >>> unsigned http2:1; >>> unsigned proxy_protocol:1; >>> }; >>> diff -r 81fae70d6cb8 -r 200955343460 src/http/ngx_http_parse.c >>> --- a/src/http/ngx_http_parse.c Thu Mar 01 20:25:50 2018 +0300 >>> +++ b/src/http/ngx_http_parse.c Fri Mar 02 15:41:38 2018 +0800 >>> @@ -285,6 +285,14 @@ >>> break; >>> } >>> >>> +#if (NGX_HTTP_V2) >>> + if (ch == '*') { >>> + r->uri_start = p; >>> + state = sw_uri; >>> + break; >>> + } >>> +#endif >>> + >>> c = (u_char) (ch | 0x20); >>> if (c >= 'a' && c <= 'z') { >>> r->schema_start = p; >>> @@ -724,9 +732,11 @@ >>> >>> r->http_major = ch - '0'; >>> >>> +#if !(NGX_HTTP_V2) >>> if (r->http_major > 1) { >>> return NGX_HTTP_PARSE_INVALID_VERSION; >>> } >>> +#endif >>> >>> state = sw_major_digit; >>> break; >>> @@ -744,9 +754,11 @@ >>> >>> r->http_major = r->http_major * 10 + (ch - '0'); >>> >>> +#if !(NGX_HTTP_V2) >>> if (r->http_major > 1) { >>> return NGX_HTTP_PARSE_INVALID_VERSION; >>> } >>> +#endif >>> >>> break; >>> >>> diff -r 81fae70d6cb8 -r 200955343460 src/http/ngx_http_request.c >>> --- a/src/http/ngx_http_request.c Thu Mar 01 20:25:50 2018 +0300 >>> +++ b/src/http/ngx_http_request.c Fri Mar 02 15:41:38 2018 +0800 >>> @@ -17,6 +17,10 @@ >>> static ngx_int_t ngx_http_alloc_large_header_buffer(ngx_http_request_t *r, >>> ngx_uint_t request_line); >>> >>> +#if (NGX_HTTP_V2) >>> +static void ngx_http_process_h2_preface(ngx_event_t *rev); >>> +#endif >>> + >>> 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_unique_header_line(ngx_http_request_t *r, >>> @@ -320,7 +324,7 @@ >>> c->write->handler = ngx_http_empty_handler; >>> >>> #if (NGX_HTTP_V2) >>> - if (hc->addr_conf->http2) { >>> + if (hc->addr_conf->http2 && !hc->addr_conf->http1) { >>> rev->handler = ngx_http_v2_init; >>> } >>> #endif >>> @@ -1033,6 +1037,20 @@ >>> return; >>> } >>> >>> +#if (NGX_HTTP_V2) >>> + if (r->http_connection->addr_conf->http2 >>> + && r->http_version >= NGX_HTTP_VERSION_20) { >>> + >>> + c->log->action = "reading client h2 preface"; >>> + >>> + rev->handler = ngx_http_process_h2_preface; >>> + ngx_http_process_h2_preface(rev); >>> + return; >>> + } else if (r->http_version >= NGX_HTTP_VERSION_20) { >>> + ngx_http_finalize_request(r, NGX_HTTP_VERSION_NOT_SUPPORTED); >>> + return; >>> + } >>> +#endif >>> >>> if (ngx_list_init(&r->headers_in.headers, r->pool, 20, >>> sizeof(ngx_table_elt_t)) >>> @@ -1208,6 +1226,64 @@ >>> return NGX_OK; >>> } >>> >>> +#if (NGX_HTTP_V2) >>> +static void >>> +ngx_http_process_h2_preface(ngx_event_t *rev) >>> +{ >>> + size_t len; >>> + ssize_t n; >>> + ngx_connection_t *c; >>> + ngx_http_request_t *r; >>> + >>> + c = rev->data; >>> + r = c->data; >>> + >>> + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, >>> + "http process h2 preface"); >>> + >>> + if (rev->timedout) { >>> + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); >>> + c->timedout = 1; >>> + ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT); >>> + return; >>> + } >>> + >>> + n = ngx_http_read_request_header(r); >>> + >>> + if (n == NGX_AGAIN || n == NGX_ERROR) { >>> + return; >>> + } >>> + >>> + len = r->header_in->last - r->header_in->start; >>> + >>> + if (len < sizeof("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n") - 1) { >>> + r->header_in->pos = r->header_in->last; >>> + return; >>> + } >>> + >>> + if (ngx_strncmp(r->header_in->start, >>> + "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", 24) == 0) { >>> + >>> + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, >>> + "h2 preface done"); >>> + >>> + r->header_in->pos = r->header_in->start + 24; >>> + >>> + c->data = r->http_connection; >>> + >>> + r->http_connection = NULL; >>> + >>> + ngx_http_v2_init_after_preface(rev, r->header_in); >>> + return; >>> + } >>> + >>> + ngx_log_error(NGX_LOG_INFO, c->log, 0, >>> + "client sent invalid h2 preface"); >>> + >>> + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); >>> +} >>> +#endif >>> + >>> >>> static void >>> ngx_http_process_request_headers(ngx_event_t *rev) >>> @@ -1809,7 +1885,7 @@ >>> return NGX_ERROR; >>> } >>> >>> - if (r->headers_in.host == NULL && r->http_version > NGX_HTTP_VERSION_10) { >>> + if (r->headers_in.host == NULL && r->http_version == NGX_HTTP_VERSION_11) { >>> ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, >>> "client sent HTTP/1.1 request without \"Host\" header"); >>> ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); >>> diff -r 81fae70d6cb8 -r 200955343460 src/http/v2/ngx_http_v2.c >>> --- a/src/http/v2/ngx_http_v2.c Thu Mar 01 20:25:50 2018 +0300 >>> +++ b/src/http/v2/ngx_http_v2.c Fri Mar 02 15:41:38 2018 +0800 >>> @@ -231,6 +231,13 @@ >>> void >>> ngx_http_v2_init(ngx_event_t *rev) >>> { >>> + ngx_http_v2_init_after_preface(rev, NULL); >>> +} >>> + >>> + >>> +void >>> +ngx_http_v2_init_after_preface(ngx_event_t *rev, ngx_buf_t *buf) >>> +{ >>> ngx_connection_t *c; >>> ngx_pool_cleanup_t *cln; >>> ngx_http_connection_t *hc; >>> @@ -316,6 +323,12 @@ >>> h2c->state.handler = hc->proxy_protocol ? ngx_http_v2_state_proxy_protocol >>> : ngx_http_v2_state_preface; >>> >>> + if (buf != NULL) { >>> + ngx_memcpy(h2mcf->recv_buffer, buf->pos, buf->last - buf->pos); >>> + h2c->state.buffer_used = buf->last - buf->pos; >>> + h2c->state.handler = ngx_http_v2_state_head; >>> + } >>> + >>> ngx_queue_init(&h2c->waiting); >>> ngx_queue_init(&h2c->dependencies); >>> ngx_queue_init(&h2c->closed); >>> @@ -381,13 +394,18 @@ >>> h2mcf = ngx_http_get_module_main_conf(h2c->http_connection->conf_ctx, >>> ngx_http_v2_module); >>> >>> - available = h2mcf->recv_buffer_size - 2 * NGX_HTTP_V2_STATE_BUFFER_SIZE; >>> + available = h2mcf->recv_buffer_size - h2c->state.buffer_used - 2 * NGX_HTTP_V2_STATE_BUFFER_SIZE; >>> >>> do { >>> p = h2mcf->recv_buffer; >>> >>> + end = p + h2c->state.buffer_used; >>> + >>> + if (h2c->state.buffer_used) { >>> + goto do_state_handler; >>> + } >>> + >>> ngx_memcpy(p, h2c->state.buffer, NGX_HTTP_V2_STATE_BUFFER_SIZE); >>> - end = p + h2c->state.buffer_used; >>> >>> n = c->recv(c, end, available); >>> >>> @@ -410,6 +428,8 @@ >>> >>> end += n; >>> >>> +do_state_handler: >>> + >>> h2c->state.buffer_used = 0; >>> h2c->state.incomplete = 0; >>> >>> diff -r 81fae70d6cb8 -r 200955343460 src/http/v2/ngx_http_v2.h >>> --- a/src/http/v2/ngx_http_v2.h Thu Mar 01 20:25:50 2018 +0300 >>> +++ b/src/http/v2/ngx_http_v2.h Fri Mar 02 15:41:38 2018 +0800 >>> @@ -279,6 +279,7 @@ >>> >>> >>> void ngx_http_v2_init(ngx_event_t *rev); >>> +void ngx_http_v2_init_after_preface(ngx_event_t *rev, ngx_buf_t *buf); >>> >>> 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); >>> >> > > > > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel From zchao1995 at gmail.com Mon Mar 5 06:36:43 2018 From: zchao1995 at gmail.com (tokers) Date: Sun, 4 Mar 2018 22:36:43 -0800 Subject: HTTP/2: used bitwise operation to replace division and modulo. Message-ID: # HG changeset patch # User Alex Zhang # Date 1520229178 -28800 # Mon Mar 05 13:52:58 2018 +0800 # Node ID 03ecef37a93d541e55802393636c101a4da14550 # Parent 265c29b0b8b8c54b1c623268481ed85324ce3c79 HTTP/2: used bitwise operation to replace division and modulo. Signed-off-by: Alex Zhang diff -r 265c29b0b8b8 -r 03ecef37a93d src/http/v2/ngx_http_v2.c --- a/src/http/v2/ngx_http_v2.c Thu Mar 01 11:42:55 2018 +0300 +++ b/src/http/v2/ngx_http_v2.c Mon Mar 05 13:52:58 2018 +0800 @@ -1094,7 +1094,7 @@ "depends 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 & 0x1 == 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); diff -r 265c29b0b8b8 -r 03ecef37a93d src/http/v2/ngx_http_v2_filter_module.c --- a/src/http/v2/ngx_http_v2_filter_module.c Thu Mar 01 11:42:55 2018 +0300 +++ b/src/http/v2/ngx_http_v2_filter_module.c Mon Mar 05 13:52:58 2018 +0800 @@ -1149,8 +1149,8 @@ value -= prefix; while (value >= 128) { - *pos++ = value % 128 + 128; - value /= 128; + *pos++ = (value & 0x7f) + 128; + value >>= 7; } *pos++ = (u_char) value; -------------- next part -------------- An HTML attachment was scrubbed... URL: From ru at nginx.com Mon Mar 5 08:28:29 2018 From: ru at nginx.com (Ruslan Ermilov) Date: Mon, 5 Mar 2018 11:28:29 +0300 Subject: HTTP/2: used bitwise operation to replace division and modulo. In-Reply-To: References: Message-ID: <20180305082829.GE91736@lo0.su> On Sun, Mar 04, 2018 at 10:36:43PM -0800, tokers wrote: > # HG changeset patch > # User Alex Zhang > # Date 1520229178 -28800 > # Mon Mar 05 13:52:58 2018 +0800 > # Node ID 03ecef37a93d541e55802393636c101a4da14550 > # Parent 265c29b0b8b8c54b1c623268481ed85324ce3c79 > HTTP/2: used bitwise operation to replace division and modulo. > > Signed-off-by: Alex Zhang No, thanks. - there is no difference in assembler output even with -O0, with either GCC or Clang, and the author likes it the way it's written; - this would require parenthesis around the & operator on the LHS of ==. - the patch is incomplete. Also, please fix your MUA so that it doesn't break patches by wrapping lines. > diff -r 265c29b0b8b8 -r 03ecef37a93d src/http/v2/ngx_http_v2.c > --- a/src/http/v2/ngx_http_v2.c Thu Mar 01 11:42:55 2018 +0300 > +++ b/src/http/v2/ngx_http_v2.c Mon Mar 05 13:52:58 2018 +0800 > @@ -1094,7 +1094,7 @@ > "depends 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 & 0x1 == 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); > diff -r 265c29b0b8b8 -r 03ecef37a93d src/http/v2/ngx_http_v2_filter_module.c > --- a/src/http/v2/ngx_http_v2_filter_module.c Thu Mar 01 11:42:55 2018 > +0300 > +++ b/src/http/v2/ngx_http_v2_filter_module.c Mon Mar 05 13:52:58 2018 > +0800 > @@ -1149,8 +1149,8 @@ > value -= prefix; > > while (value >= 128) { > - *pos++ = value % 128 + 128; > - value /= 128; > + *pos++ = (value & 0x7f) + 128; > + value >>= 7; > } > > *pos++ = (u_char) value; > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel -- Ruslan Ermilov Assume stupidity not malice From ru at nginx.com Mon Mar 5 09:02:47 2018 From: ru at nginx.com (Ruslan Ermilov) Date: Mon, 5 Mar 2018 12:02:47 +0300 Subject: [PATCH] HTTP/2: make http2 server support http1 In-Reply-To: <8E2C1E9B-E675-49EB-9D8B-C00E94677B4A@lvht.net> References: <762DEAA4-5D48-4957-9DEE-BFF82869EC0C@lvht.net> <8E2C1E9B-E675-49EB-9D8B-C00E94677B4A@lvht.net> Message-ID: <20180305090247.GF91736@lo0.su> On Mon, Mar 05, 2018 at 11:11:08AM +0800, Haitao Lv wrote: [...] > > @@ -145,10 +152,6 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) > > case sw_start: > > r->request_start = p; > > > > - if (ch == CR || ch == LF) { > > - break; > > - } > > - > > I think Nginx should not allow any leading \r or \n. HTTP client should never send this chars > before the request line. Support this feature makes the buffer management more harder. https://tools.ietf.org/html/rfc2616#section-4.1 In the interest of robustness, servers SHOULD ignore any empty line(s) received where a Request-Line is expected. In other words, if the server is reading the protocol stream at the beginning of a message and receives a CRLF first, it should ignore the CRLF. https://tools.ietf.org/html/rfc7230#section-3.5 In the interest of robustness, a server that is expecting to receive and parse a request-line SHOULD ignore at least one empty line (CRLF) received prior to the request-line. From i at lvht.net Mon Mar 5 14:36:11 2018 From: i at lvht.net (Haitao Lv) Date: Mon, 5 Mar 2018 22:36:11 +0800 Subject: [PATCH] HTTP/2: make http2 server support http1 In-Reply-To: <20180305090247.GF91736@lo0.su> References: <762DEAA4-5D48-4957-9DEE-BFF82869EC0C@lvht.net> <8E2C1E9B-E675-49EB-9D8B-C00E94677B4A@lvht.net> <20180305090247.GF91736@lo0.su> Message-ID: <9591F65C-4618-4539-AA46-E3C648B1FC2A@lvht.net> > On Mar 5, 2018, at 17:02, Ruslan Ermilov wrote: > > On Mon, Mar 05, 2018 at 11:11:08AM +0800, Haitao Lv wrote: > [...] >>> @@ -145,10 +152,6 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) >>> case sw_start: >>> r->request_start = p; >>> >>> - if (ch == CR || ch == LF) { >>> - break; >>> - } >>> - >> >> I think Nginx should not allow any leading \r or \n. HTTP client should never send this chars >> before the request line. Support this feature makes the buffer management more harder. > > https://tools.ietf.org/html/rfc2616#section-4.1 > > In the interest of robustness, servers SHOULD ignore any empty > line(s) received where a Request-Line is expected. In other words, if > the server is reading the protocol stream at the beginning of a > message and receives a CRLF first, it should ignore the CRLF. > > > https://tools.ietf.org/html/rfc7230#section-3.5 > > In the interest of robustness, a server that is expecting to receive > and parse a request-line SHOULD ignore at least one empty line (CRLF) > received prior to the request-line. Thank you, it is a mistake. > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel From vbart at nginx.com Mon Mar 5 15:06:00 2018 From: vbart at nginx.com (Valentin V. Bartenev) Date: Mon, 05 Mar 2018 18:06 +0300 Subject: [PATCH] HTTP/2: make http2 server support http1 In-Reply-To: <8E2C1E9B-E675-49EB-9D8B-C00E94677B4A@lvht.net> References: <8E2C1E9B-E675-49EB-9D8B-C00E94677B4A@lvht.net> Message-ID: <1715412.WZbLpgGAny@vbart-workstation> On Monday 05 March 2018 11:11:08 Haitao Lv wrote: [..] > > @@ -145,10 +152,6 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) > > case sw_start: > > r->request_start = p; > > > > - if (ch == CR || ch == LF) { > > - break; > > - } > > - > > I think Nginx should not allow any leading \r or \n. HTTP client should never send this chars > before the request line. Support this feature makes the buffer management more harder. Support for this feature is required. See the notes from Ruslan. Some clients send additional empty line between keepalive requests. [..] > > +#if (NGX_HTTP_V2) > > + case sw_h2_preface: > > + > > + if (ch == *n++) { > > I introduce a new sw_h2_preface state. In this state, the parser will check the h2 preface char by char. > > The parser only check at most 20 chars "* HTTP/2.0\r\n\r\nSM\r\n\r\n". So the buffer will not be fulfilled. > The recv() operation gets buffer size as an argument. Thus, the buffer can be fully filled with all the data copied from a socket buffer. HTTP/2 clients usually don't send preface alone. They can send other data with the preface. [..] > > +#if (NGX_HTTP_V2) > > +static void > > +ngx_http_process_h2_preface(ngx_event_t *rev) > > +{ > > + size_t len; > > + ngx_connection_t *c; > > + ngx_http_request_t *r; > > + ngx_http_connection_t *hc; > > + ngx_http_v2_main_conf_t *h2mcf; > > + > > + c = rev->data; > > + r = c->data; > > + hc = r->http_connection; > > + > > + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, > > + "http process h2 preface"); > > + > > + r->header_in->pos = r->header_in->start + 24; > > + > > + len = r->header_in->last - r->header_in->pos; > > + > > + h2mcf = ngx_http_get_module_main_conf(hc->conf_ctx, ngx_http_v2_module); > > + > > + if (len <= h2mcf->recv_buffer_size) { > > We check the recv buffer size before do the ngx_http_v2_init_after_preface. > [..] See above. It's a bad idea to decline request with a "client sent invalid h2 preface" message in error log just because client has sent more HTTP/2 frames right after the preface. Overall, the patch looks like a hack and introduces too much complexity for this feature. While I understand the reasoning, the proposed implementation cannot be accepted. Protocol detection before starting processing HTTP/1 or HTTP/2 could be a better way to go. wbr, Valentin V. Bartenev From i at lvht.net Mon Mar 5 15:52:57 2018 From: i at lvht.net (Haitao Lv) Date: Mon, 5 Mar 2018 23:52:57 +0800 Subject: [PATCH] HTTP/2: make http2 server support http1 In-Reply-To: <1715412.WZbLpgGAny@vbart-workstation> References: <8E2C1E9B-E675-49EB-9D8B-C00E94677B4A@lvht.net> <1715412.WZbLpgGAny@vbart-workstation> Message-ID: Hello wbr, Thank you for reviewing this patch. > On Mar 5, 2018, at 23:06, Valentin V. Bartenev wrote: > > On Monday 05 March 2018 11:11:08 Haitao Lv wrote: > [..] >>> @@ -145,10 +152,6 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) >>> case sw_start: >>> r->request_start = p; >>> >>> - if (ch == CR || ch == LF) { >>> - break; >>> - } >>> - >> >> I think Nginx should not allow any leading \r or \n. HTTP client should never send this chars >> before the request line. Support this feature makes the buffer management more harder. > > Support for this feature is required. See the notes from Ruslan. > Some clients send additional empty line between keepalive requests. Yes, this is a mistake. I did not read the RFC carefully. Sorry again. But this change isn't needed. > > [..] >>> +#if (NGX_HTTP_V2) >>> + case sw_h2_preface: >>> + >>> + if (ch == *n++) { >> >> I introduce a new sw_h2_preface state. In this state, the parser will check the h2 preface char by char. >> >> The parser only check at most 20 chars "* HTTP/2.0\r\n\r\nSM\r\n\r\n". So the buffer will not be fulfilled. >> > > The recv() operation gets buffer size as an argument. > > Thus, the buffer can be fully filled with all the data copied from > a socket buffer. HTTP/2 clients usually don't send preface alone. > They can send other data with the preface. Yes, the buffer could be full filled. HTTP/2 client will send more frames after the h2 preface, this is why I introduce the ngx_http_v2_init_after_preface(ngx_event_t *rev, ngx_buf_t *buf). All the remaining data in the buffer will be copied to the h2mcf->recv_buffer and be processed by the HTTP/2 ngx_http_v2_state_head handler. So this will not breaks the HTTP/2 handshake. However, if we received data is longer than the h2mcf->recv_buffer_size, the current patch will yield a bad request response. A bad case is the client_header_buffer_size(default 1k) set larger than the http2_recv_buffer_size(default 265k), we will may fully filled the header_in buffer but cannot be copied into the h2mcf->recv_buffer, which will cause the HTTP/2 handshake failed. But simple way to workaround this issue is to change the http2_recv_buffer_size to or larger than client_header_buffer_size. It is a hack. > > [..] >>> +#if (NGX_HTTP_V2) >>> +static void >>> +ngx_http_process_h2_preface(ngx_event_t *rev) >>> +{ >>> + size_t len; >>> + ngx_connection_t *c; >>> + ngx_http_request_t *r; >>> + ngx_http_connection_t *hc; >>> + ngx_http_v2_main_conf_t *h2mcf; >>> + >>> + c = rev->data; >>> + r = c->data; >>> + hc = r->http_connection; >>> + >>> + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, >>> + "http process h2 preface"); >>> + >>> + r->header_in->pos = r->header_in->start + 24; >>> + >>> + len = r->header_in->last - r->header_in->pos; >>> + >>> + h2mcf = ngx_http_get_module_main_conf(hc->conf_ctx, ngx_http_v2_module); >>> + >>> + if (len <= h2mcf->recv_buffer_size) { >> >> We check the recv buffer size before do the ngx_http_v2_init_after_preface. >> > [..] > > See above. It's a bad idea to decline request with a "client sent > invalid h2 preface" message in error log just because client has > sent more HTTP/2 frames right after the preface. In this path, all the valid HTTP/2 handshake will never generate a error log. > > Overall, the patch looks like a hack and introduces too much > complexity for this feature. While I understand the reasoning, > the proposed implementation cannot be accepted. Could you clarify that whether is this feature not accepted or this patch? If this feature is not needed, I will terminate this thread. If this patch only looks like a hack, would you like offer any advice to write code with good smell? Thank you. > > Protocol detection before starting processing HTTP/1 or HTTP/2 > could be a better way to go. > > wbr, Valentin V. Bartenev > > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel From mdounin at mdounin.ru Mon Mar 5 19:14:59 2018 From: mdounin at mdounin.ru (Maxim Dounin) Date: Mon, 5 Mar 2018 22:14:59 +0300 Subject: [PATCH] HTTP/2: make http2 server support http1 In-Reply-To: References: <8E2C1E9B-E675-49EB-9D8B-C00E94677B4A@lvht.net> <1715412.WZbLpgGAny@vbart-workstation> Message-ID: <20180305191459.GR89840@mdounin.ru> Hello! On Mon, Mar 05, 2018 at 11:52:57PM +0800, Haitao Lv wrote: [...] > > Overall, the patch looks like a hack and introduces too much > > complexity for this feature. While I understand the reasoning, > > the proposed implementation cannot be accepted. > > Could you clarify that whether is this feature not accepted or this patch? > > If this feature is not needed, I will terminate this thread. > > If this patch only looks like a hack, would you like offer any advice to write > code with good smell? We've previously discussed this with Valentin, and our position is as follows: - The feature itself (autodetection between HTTP/2 and HTTP/1.x protocols) might be usable, and we can consider adding it if there will be a good and simple enough patch. (Moreover, we think that this probably should be the default if "listen ... http2" is configured - that is, no "http1" option.) - The patch suggested certainly doesn't meet the above criteria, and it does not look like it can be fixed. We don't know if a good and simple enough implementation is at all possible though. One of the possible approaches was already proposed by Valentin (detect HTTP/2 or HTTP/1.x before starting processing, may be similar to how we handle http-to-https requests), but it's now immediately clear if it will work or not. Sorry, but please don't expect any of us to provide further guidance. -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Mon Mar 5 19:28:42 2018 From: mdounin at mdounin.ru (Maxim Dounin) Date: Mon, 05 Mar 2018 19:28:42 +0000 Subject: [nginx] HTTP/2: unknown frames now logged at info level. Message-ID: details: http://hg.nginx.org/nginx/rev/e80930e5e422 branches: changeset: 7225:e80930e5e422 user: Maxim Dounin date: Mon Mar 05 21:35:13 2018 +0300 description: HTTP/2: unknown frames now logged at info level. diffstat: src/http/v2/ngx_http_v2.c | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diffs (14 lines): diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -783,8 +783,8 @@ ngx_http_v2_state_head(ngx_http_v2_conne type, h2c->state.flags, h2c->state.length, h2c->state.sid); if (type >= NGX_HTTP_V2_FRAME_STATES) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, - "http2 frame with unknown type %ui", type); + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent frame with unknown type %ui", type); return ngx_http_v2_state_skip(h2c, pos, end); } From mdounin at mdounin.ru Mon Mar 5 19:28:40 2018 From: mdounin at mdounin.ru (Maxim Dounin) Date: Mon, 05 Mar 2018 19:28:40 +0000 Subject: [nginx] Style. Message-ID: details: http://hg.nginx.org/nginx/rev/1ab290cf5267 branches: changeset: 7224:1ab290cf5267 user: Maxim Dounin date: Mon Mar 05 21:35:08 2018 +0300 description: Style. diffstat: src/stream/ngx_stream_log_module.c | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-) diffs (13 lines): diff --git a/src/stream/ngx_stream_log_module.c b/src/stream/ngx_stream_log_module.c --- a/src/stream/ngx_stream_log_module.c +++ b/src/stream/ngx_stream_log_module.c @@ -868,7 +868,8 @@ ngx_stream_log_json_variable(ngx_stream_ static size_t -ngx_stream_log_unescaped_variable_getlen(ngx_stream_session_t *s, uintptr_t data) +ngx_stream_log_unescaped_variable_getlen(ngx_stream_session_t *s, + uintptr_t data) { ngx_stream_variable_value_t *value; From i at lvht.net Tue Mar 6 02:19:50 2018 From: i at lvht.net (Haitao Lv) Date: Tue, 6 Mar 2018 10:19:50 +0800 Subject: [PATCH] HTTP/2: make http2 server support http1 In-Reply-To: <20180305191459.GR89840@mdounin.ru> References: <8E2C1E9B-E675-49EB-9D8B-C00E94677B4A@lvht.net> <1715412.WZbLpgGAny@vbart-workstation> <20180305191459.GR89840@mdounin.ru> Message-ID: Hello, here is another patch(more sample) according Maxim Dounin advice. Please offer your comment. Thanks. diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 89cfe77a..71bc7b59 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -17,6 +17,10 @@ static ssize_t ngx_http_read_request_header(ngx_http_request_t *r); static ngx_int_t ngx_http_alloc_large_header_buffer(ngx_http_request_t *r, ngx_uint_t request_line); +#if (NGX_HTTP_V2) +static void ngx_http_wait_v2_preface_handler(ngx_event_t *rev); +#endif + 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_unique_header_line(ngx_http_request_t *r, @@ -321,7 +325,7 @@ ngx_http_init_connection(ngx_connection_t *c) #if (NGX_HTTP_V2) if (hc->addr_conf->http2) { - rev->handler = ngx_http_v2_init; + rev->handler = ngx_http_wait_v2_preface_handler; } #endif @@ -377,6 +381,108 @@ ngx_http_init_connection(ngx_connection_t *c) } +#if (NGX_HTTP_V2) +static void +ngx_http_wait_v2_preface_handler(ngx_event_t *rev) +{ + size_t size; + ssize_t n; + ngx_buf_t *b; + ngx_connection_t *c; + + c = rev->data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http wait h2 preface handler"); + + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); + ngx_http_close_connection(c); + return; + } + + if (c->close) { + ngx_http_close_connection(c); + return; + } + + size = 5 /* strlen("PRI *") */; + + b = c->buffer; + + if (b == NULL) { + b = ngx_create_temp_buf(c->pool, size); + if (b == NULL) { + ngx_http_close_connection(c); + return; + } + + c->buffer = b; + + } else if (b->start == NULL) { + + b->start = ngx_palloc(c->pool, size); + if (b->start == NULL) { + ngx_http_close_connection(c); + return; + } + + b->pos = b->start; + b->last = b->start; + b->end = b->last + size; + } + + n = c->recv(c, b->last, size); + + if (n == NGX_AGAIN) { + + if (!rev->timer_set) { + ngx_add_timer(rev, c->listening->post_accept_timeout); + ngx_reusable_connection(c, 1); + } + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + ngx_http_close_connection(c); + return; + } + + /* + * We are trying to not hold c->buffer's memory for an idle connection. + */ + + if (ngx_pfree(c->pool, b->start) == NGX_OK) { + b->start = NULL; + } + + return; + } + + if (n == NGX_ERROR) { + ngx_http_close_connection(c); + return; + } + + if (n == 0) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client closed connection"); + ngx_http_close_connection(c); + return; + } + + b->last += n; + + if (b->last == b->end) { + if (ngx_strncmp(b->start, "PRI *", 5) == 0) { + ngx_http_v2_init_with_buf(rev, b); + } else { + rev->handler = ngx_http_wait_request_handler; + ngx_http_wait_request_handler(rev); + } + } +} +#endif + + static void ngx_http_wait_request_handler(ngx_event_t *rev) { @@ -430,6 +536,21 @@ ngx_http_wait_request_handler(ngx_event_t *rev) b->pos = b->start; b->last = b->start; b->end = b->last + size; + } else { + + p = ngx_palloc(c->pool, size); + if (p == NULL) { + ngx_http_close_connection(c); + return; + } + + n = b->last - b->start; + ngx_memcpy(p, b->start, n); + + b->start = p; + b->pos = b->start; + b->last = b->start + n; + b->end = b->last + size; } n = c->recv(c, b->last, size); diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c index d9df0f90..a990c96f 100644 --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -231,6 +231,14 @@ static ngx_http_v2_parse_header_t ngx_http_v2_parse_headers[] = { void ngx_http_v2_init(ngx_event_t *rev) { + ngx_http_v2_init_with_buf(rev, NULL); +} + + +void +ngx_http_v2_init_with_buf(ngx_event_t *rev, ngx_buf_t *buf) +{ + size_t size; ngx_connection_t *c; ngx_pool_cleanup_t *cln; ngx_http_connection_t *hc; @@ -262,6 +270,17 @@ ngx_http_v2_init(ngx_event_t *rev) return; } + if (buf != NULL) { + size = buf->last - buf->start; + + if (size > h2mcf->recv_buffer_size) { + size = h2mcf->recv_buffer_size; + } + + ngx_memcpy(h2mcf->recv_buffer, buf->start, size); + h2c->state.buffer_used = size; + } + h2c->connection = c; h2c->http_connection = hc; @@ -381,13 +400,16 @@ ngx_http_v2_read_handler(ngx_event_t *rev) h2mcf = ngx_http_get_module_main_conf(h2c->http_connection->conf_ctx, ngx_http_v2_module); - available = h2mcf->recv_buffer_size - 2 * NGX_HTTP_V2_STATE_BUFFER_SIZE; + available = h2mcf->recv_buffer_size - h2c->state.buffer_used - 2 * NGX_HTTP_V2_STATE_BUFFER_SIZE; do { p = h2mcf->recv_buffer; - ngx_memcpy(p, h2c->state.buffer, NGX_HTTP_V2_STATE_BUFFER_SIZE); end = p + h2c->state.buffer_used; + if (h2c->state.buffer_used == 0) { + ngx_memcpy(p, h2c->state.buffer, NGX_HTTP_V2_STATE_BUFFER_SIZE); + } + n = c->recv(c, end, available); diff --git a/src/http/v2/ngx_http_v2.h b/src/http/v2/ngx_http_v2.h index d89e8fef..7b223b29 100644 --- a/src/http/v2/ngx_http_v2.h +++ b/src/http/v2/ngx_http_v2.h @@ -279,6 +279,7 @@ ngx_http_v2_queue_ordered_frame(ngx_http_v2_connection_t *h2c, void ngx_http_v2_init(ngx_event_t *rev); +void ngx_http_v2_init_with_buf(ngx_event_t *rev, ngx_buf_t *buf); 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); From ru at nginx.com Wed Mar 7 15:29:28 2018 From: ru at nginx.com (Ruslan Ermilov) Date: Wed, 07 Mar 2018 15:29:28 +0000 Subject: [nginx] Improved code readablity. Message-ID: details: http://hg.nginx.org/nginx/rev/0b1eb40de6da branches: changeset: 7226:0b1eb40de6da user: Ruslan Ermilov date: Wed Mar 07 18:28:12 2018 +0300 description: Improved code readablity. No functional changes. diffstat: src/http/ngx_http_variables.c | 8 ++++++-- src/stream/ngx_stream_variables.c | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diffs (50 lines): diff -r e80930e5e422 -r 0b1eb40de6da src/http/ngx_http_variables.c --- a/src/http/ngx_http_variables.c Mon Mar 05 21:35:13 2018 +0300 +++ b/src/http/ngx_http_variables.c Wed Mar 07 18:28:12 2018 +0300 @@ -429,7 +429,9 @@ ngx_http_add_variable(ngx_conf_t *cf, ng return NULL; } - v->flags &= flags | ~NGX_HTTP_VAR_WEAK; + if (!(flags & NGX_HTTP_VAR_WEAK)) { + v->flags &= ~NGX_HTTP_VAR_WEAK; + } return v; } @@ -494,7 +496,9 @@ ngx_http_add_prefix_variable(ngx_conf_t return NULL; } - v->flags &= flags | ~NGX_HTTP_VAR_WEAK; + if (!(flags & NGX_HTTP_VAR_WEAK)) { + v->flags &= ~NGX_HTTP_VAR_WEAK; + } return v; } diff -r e80930e5e422 -r 0b1eb40de6da src/stream/ngx_stream_variables.c --- a/src/stream/ngx_stream_variables.c Mon Mar 05 21:35:13 2018 +0300 +++ b/src/stream/ngx_stream_variables.c Wed Mar 07 18:28:12 2018 +0300 @@ -161,7 +161,9 @@ ngx_stream_add_variable(ngx_conf_t *cf, return NULL; } - v->flags &= flags | ~NGX_STREAM_VAR_WEAK; + if (!(flags & NGX_STREAM_VAR_WEAK)) { + v->flags &= ~NGX_STREAM_VAR_WEAK; + } return v; } @@ -227,7 +229,9 @@ ngx_stream_add_prefix_variable(ngx_conf_ return NULL; } - v->flags &= flags | ~NGX_STREAM_VAR_WEAK; + if (!(flags & NGX_STREAM_VAR_WEAK)) { + v->flags &= ~NGX_STREAM_VAR_WEAK; + } return v; } From i at lvht.net Thu Mar 8 00:29:02 2018 From: i at lvht.net (Haitao Lv) Date: Thu, 8 Mar 2018 08:29:02 +0800 Subject: [PATCH] HTTP/2: make http2 server support http1 In-Reply-To: References: <8E2C1E9B-E675-49EB-9D8B-C00E94677B4A@lvht.net> <1715412.WZbLpgGAny@vbart-workstation> <20180305191459.GR89840@mdounin.ru> Message-ID: <9DC349AF-496D-4901-98DD-7C14D2560150@lvht.net> Here is a more simple patch. And the temp buffer has also been freed. Please make your comments. Thanks. diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 89cfe77a..d97952bc 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -17,6 +17,10 @@ static ssize_t ngx_http_read_request_header(ngx_http_request_t *r); static ngx_int_t ngx_http_alloc_large_header_buffer(ngx_http_request_t *r, ngx_uint_t request_line); +#if (NGX_HTTP_V2) +static void ngx_http_wait_v2_preface_handler(ngx_event_t *rev); +#endif + 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_unique_header_line(ngx_http_request_t *r, @@ -321,7 +325,7 @@ ngx_http_init_connection(ngx_connection_t *c) #if (NGX_HTTP_V2) if (hc->addr_conf->http2) { - rev->handler = ngx_http_v2_init; + rev->handler = ngx_http_wait_v2_preface_handler; } #endif @@ -377,6 +381,110 @@ ngx_http_init_connection(ngx_connection_t *c) } +#if (NGX_HTTP_V2) +static void +ngx_http_wait_v2_preface_handler(ngx_event_t *rev) +{ + size_t size; + ssize_t n; + ngx_buf_t *b; + ngx_connection_t *c; + static const u_char preface[] = "PRI"; + + c = rev->data; + size = sizeof(preface) - 1; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http wait h2 preface handler"); + + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); + ngx_http_close_connection(c); + return; + } + + if (c->close) { + ngx_http_close_connection(c); + return; + } + + b = c->buffer; + + if (b == NULL) { + b = ngx_create_temp_buf(c->pool, size); + if (b == NULL) { + ngx_http_close_connection(c); + return; + } + + c->buffer = b; + + } else if (b->start == NULL) { + + b->start = ngx_palloc(c->pool, size); + if (b->start == NULL) { + ngx_http_close_connection(c); + return; + } + + b->pos = b->start; + b->last = b->start; + b->end = b->last + size; + } + + n = c->recv(c, b->last, size); + + if (n == NGX_AGAIN) { + + if (!rev->timer_set) { + ngx_add_timer(rev, c->listening->post_accept_timeout); + ngx_reusable_connection(c, 1); + } + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + ngx_http_close_connection(c); + return; + } + + /* + * We are trying to not hold c->buffer's memory for an idle connection. + */ + + if (ngx_pfree(c->pool, b->start) == NGX_OK) { + b->start = NULL; + } + + return; + } + + if (n == NGX_ERROR) { + ngx_http_close_connection(c); + return; + } + + if (n == 0) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client closed connection"); + ngx_http_close_connection(c); + return; + } + + b->last += n; + + if (b->last == b->end) { + /* b will be freed in ngx_http_v2_init/ngx_http_wait_request_handler */ + + if (ngx_strncmp(b->start, preface, size) == 0) { + ngx_http_v2_init(rev); + } else { + rev->handler = ngx_http_wait_request_handler; + ngx_http_wait_request_handler(rev); + } + } +} +#endif + + static void ngx_http_wait_request_handler(ngx_event_t *rev) { @@ -430,6 +538,22 @@ ngx_http_wait_request_handler(ngx_event_t *rev) b->pos = b->start; b->last = b->start; b->end = b->last + size; + } else { + + p = ngx_palloc(c->pool, size); + if (p == NULL) { + ngx_http_close_connection(c); + return; + } + + n = b->last - b->start; + ngx_memcpy(p, b->start, n); + ngx_pfree(c->pool, b->start); + + b->start = p; + b->pos = b->start; + b->last = b->start + n; + b->end = b->last + size; } n = c->recv(c, b->last, size); diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c index d9df0f90..e36bf382 100644 --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -231,6 +231,8 @@ static ngx_http_v2_parse_header_t ngx_http_v2_parse_headers[] = { void ngx_http_v2_init(ngx_event_t *rev) { + size_t size; + ngx_buf_t *b; ngx_connection_t *c; ngx_pool_cleanup_t *cln; ngx_http_connection_t *hc; @@ -262,6 +264,23 @@ ngx_http_v2_init(ngx_event_t *rev) return; } + b = c->buffer; + + if (b != NULL) { + size = b->last - b->start; + + if (size > h2mcf->recv_buffer_size) { + size = h2mcf->recv_buffer_size; + } + + ngx_memcpy(h2mcf->recv_buffer, b->start, size); + h2c->state.buffer_used = size; + + ngx_pfree(c->pool, b->start); + ngx_pfree(c->pool, b); + c->buffer = NULL; + } + h2c->connection = c; h2c->http_connection = hc; @@ -381,13 +400,15 @@ ngx_http_v2_read_handler(ngx_event_t *rev) h2mcf = ngx_http_get_module_main_conf(h2c->http_connection->conf_ctx, ngx_http_v2_module); - available = h2mcf->recv_buffer_size - 2 * NGX_HTTP_V2_STATE_BUFFER_SIZE; + available = h2mcf->recv_buffer_size - h2c->state.buffer_used - 2 * NGX_HTTP_V2_STATE_BUFFER_SIZE; do { p = h2mcf->recv_buffer; - ngx_memcpy(p, h2c->state.buffer, NGX_HTTP_V2_STATE_BUFFER_SIZE); end = p + h2c->state.buffer_used; + if (h2c->state.buffer_used == 0) { + ngx_memcpy(p, h2c->state.buffer, NGX_HTTP_V2_STATE_BUFFER_SIZE); + } n = c->recv(c, end, available); > On Mar 6, 2018, at 10:19, Haitao Lv wrote: > > Hello, here is another patch(more sample) according Maxim Dounin advice. > > Please offer your comment. Thanks. > > diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c > index 89cfe77a..71bc7b59 100644 > --- a/src/http/ngx_http_request.c > +++ b/src/http/ngx_http_request.c > @@ -17,6 +17,10 @@ static ssize_t ngx_http_read_request_header(ngx_http_request_t *r); > static ngx_int_t ngx_http_alloc_large_header_buffer(ngx_http_request_t *r, > ngx_uint_t request_line); > > +#if (NGX_HTTP_V2) > +static void ngx_http_wait_v2_preface_handler(ngx_event_t *rev); > +#endif > + > 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_unique_header_line(ngx_http_request_t *r, > @@ -321,7 +325,7 @@ ngx_http_init_connection(ngx_connection_t *c) > > #if (NGX_HTTP_V2) > if (hc->addr_conf->http2) { > - rev->handler = ngx_http_v2_init; > + rev->handler = ngx_http_wait_v2_preface_handler; > } > #endif > > @@ -377,6 +381,108 @@ ngx_http_init_connection(ngx_connection_t *c) > } > > > +#if (NGX_HTTP_V2) > +static void > +ngx_http_wait_v2_preface_handler(ngx_event_t *rev) > +{ > + size_t size; > + ssize_t n; > + ngx_buf_t *b; > + ngx_connection_t *c; > + > + c = rev->data; > + > + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, > + "http wait h2 preface handler"); > + > + if (rev->timedout) { > + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); > + ngx_http_close_connection(c); > + return; > + } > + > + if (c->close) { > + ngx_http_close_connection(c); > + return; > + } > + > + size = 5 /* strlen("PRI *") */; > + > + b = c->buffer; > + > + if (b == NULL) { > + b = ngx_create_temp_buf(c->pool, size); > + if (b == NULL) { > + ngx_http_close_connection(c); > + return; > + } > + > + c->buffer = b; > + > + } else if (b->start == NULL) { > + > + b->start = ngx_palloc(c->pool, size); > + if (b->start == NULL) { > + ngx_http_close_connection(c); > + return; > + } > + > + b->pos = b->start; > + b->last = b->start; > + b->end = b->last + size; > + } > + > + n = c->recv(c, b->last, size); > + > + if (n == NGX_AGAIN) { > + > + if (!rev->timer_set) { > + ngx_add_timer(rev, c->listening->post_accept_timeout); > + ngx_reusable_connection(c, 1); > + } > + > + if (ngx_handle_read_event(rev, 0) != NGX_OK) { > + ngx_http_close_connection(c); > + return; > + } > + > + /* > + * We are trying to not hold c->buffer's memory for an idle connection. > + */ > + > + if (ngx_pfree(c->pool, b->start) == NGX_OK) { > + b->start = NULL; > + } > + > + return; > + } > + > + if (n == NGX_ERROR) { > + ngx_http_close_connection(c); > + return; > + } > + > + if (n == 0) { > + ngx_log_error(NGX_LOG_INFO, c->log, 0, > + "client closed connection"); > + ngx_http_close_connection(c); > + return; > + } > + > + b->last += n; > + > + if (b->last == b->end) { > + if (ngx_strncmp(b->start, "PRI *", 5) == 0) { > + ngx_http_v2_init_with_buf(rev, b); > + } else { > + rev->handler = ngx_http_wait_request_handler; > + ngx_http_wait_request_handler(rev); > + } > + } > +} > +#endif > + > + > static void > ngx_http_wait_request_handler(ngx_event_t *rev) > { > @@ -430,6 +536,21 @@ ngx_http_wait_request_handler(ngx_event_t *rev) > b->pos = b->start; > b->last = b->start; > b->end = b->last + size; > + } else { > + > + p = ngx_palloc(c->pool, size); > + if (p == NULL) { > + ngx_http_close_connection(c); > + return; > + } > + > + n = b->last - b->start; > + ngx_memcpy(p, b->start, n); > + > + b->start = p; > + b->pos = b->start; > + b->last = b->start + n; > + b->end = b->last + size; > } > > n = c->recv(c, b->last, size); > diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c > index d9df0f90..a990c96f 100644 > --- a/src/http/v2/ngx_http_v2.c > +++ b/src/http/v2/ngx_http_v2.c > @@ -231,6 +231,14 @@ static ngx_http_v2_parse_header_t ngx_http_v2_parse_headers[] = { > void > ngx_http_v2_init(ngx_event_t *rev) > { > + ngx_http_v2_init_with_buf(rev, NULL); > +} > + > + > +void > +ngx_http_v2_init_with_buf(ngx_event_t *rev, ngx_buf_t *buf) > +{ > + size_t size; > ngx_connection_t *c; > ngx_pool_cleanup_t *cln; > ngx_http_connection_t *hc; > @@ -262,6 +270,17 @@ ngx_http_v2_init(ngx_event_t *rev) > return; > } > > + if (buf != NULL) { > + size = buf->last - buf->start; > + > + if (size > h2mcf->recv_buffer_size) { > + size = h2mcf->recv_buffer_size; > + } > + > + ngx_memcpy(h2mcf->recv_buffer, buf->start, size); > + h2c->state.buffer_used = size; > + } > + > h2c->connection = c; > h2c->http_connection = hc; > > @@ -381,13 +400,16 @@ ngx_http_v2_read_handler(ngx_event_t *rev) > h2mcf = ngx_http_get_module_main_conf(h2c->http_connection->conf_ctx, > ngx_http_v2_module); > > - available = h2mcf->recv_buffer_size - 2 * NGX_HTTP_V2_STATE_BUFFER_SIZE; > + available = h2mcf->recv_buffer_size - h2c->state.buffer_used - 2 * NGX_HTTP_V2_STATE_BUFFER_SIZE; > > do { > p = h2mcf->recv_buffer; > > - ngx_memcpy(p, h2c->state.buffer, NGX_HTTP_V2_STATE_BUFFER_SIZE); > end = p + h2c->state.buffer_used; > + if (h2c->state.buffer_used == 0) { > + ngx_memcpy(p, h2c->state.buffer, NGX_HTTP_V2_STATE_BUFFER_SIZE); > + } > + > > n = c->recv(c, end, available); > > diff --git a/src/http/v2/ngx_http_v2.h b/src/http/v2/ngx_http_v2.h > index d89e8fef..7b223b29 100644 > --- a/src/http/v2/ngx_http_v2.h > +++ b/src/http/v2/ngx_http_v2.h > @@ -279,6 +279,7 @@ ngx_http_v2_queue_ordered_frame(ngx_http_v2_connection_t *h2c, > > > void ngx_http_v2_init(ngx_event_t *rev); > +void ngx_http_v2_init_with_buf(ngx_event_t *rev, ngx_buf_t *buf); > > 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); > > > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel From zhjwpku at gmail.com Thu Mar 8 00:42:06 2018 From: zhjwpku at gmail.com (Junwang Zhao) Date: Thu, 8 Mar 2018 08:42:06 +0800 Subject: [nginx] Improved code readablity. In-Reply-To: References: Message-ID: I'm a little bit confused why the diffs are equal to each other, ASAICS, the changed code removed the effect of 'flags'? Can you explain a little bit since I just began to read the code base :) 2018-03-07 23:29 GMT+08:00 Ruslan Ermilov : > details: http://hg.nginx.org/nginx/rev/0b1eb40de6da > branches: > changeset: 7226:0b1eb40de6da > user: Ruslan Ermilov > date: Wed Mar 07 18:28:12 2018 +0300 > description: > Improved code readablity. > > No functional changes. > > diffstat: > > src/http/ngx_http_variables.c | 8 ++++++-- > src/stream/ngx_stream_variables.c | 8 ++++++-- > 2 files changed, 12 insertions(+), 4 deletions(-) > > diffs (50 lines): > > diff -r e80930e5e422 -r 0b1eb40de6da src/http/ngx_http_variables.c > --- a/src/http/ngx_http_variables.c Mon Mar 05 21:35:13 2018 +0300 > +++ b/src/http/ngx_http_variables.c Wed Mar 07 18:28:12 2018 +0300 > @@ -429,7 +429,9 @@ ngx_http_add_variable(ngx_conf_t *cf, ng > return NULL; > } > > - v->flags &= flags | ~NGX_HTTP_VAR_WEAK; > + if (!(flags & NGX_HTTP_VAR_WEAK)) { > + v->flags &= ~NGX_HTTP_VAR_WEAK; > + } > > return v; > } > @@ -494,7 +496,9 @@ ngx_http_add_prefix_variable(ngx_conf_t > return NULL; > } > > - v->flags &= flags | ~NGX_HTTP_VAR_WEAK; > + if (!(flags & NGX_HTTP_VAR_WEAK)) { > + v->flags &= ~NGX_HTTP_VAR_WEAK; > + } > > return v; > } > diff -r e80930e5e422 -r 0b1eb40de6da src/stream/ngx_stream_variables.c > --- a/src/stream/ngx_stream_variables.c Mon Mar 05 21:35:13 2018 +0300 > +++ b/src/stream/ngx_stream_variables.c Wed Mar 07 18:28:12 2018 +0300 > @@ -161,7 +161,9 @@ ngx_stream_add_variable(ngx_conf_t *cf, > return NULL; > } > > - v->flags &= flags | ~NGX_STREAM_VAR_WEAK; > + if (!(flags & NGX_STREAM_VAR_WEAK)) { > + v->flags &= ~NGX_STREAM_VAR_WEAK; > + } > > return v; > } > @@ -227,7 +229,9 @@ ngx_stream_add_prefix_variable(ngx_conf_ > return NULL; > } > > - v->flags &= flags | ~NGX_STREAM_VAR_WEAK; > + if (!(flags & NGX_STREAM_VAR_WEAK)) { > + v->flags &= ~NGX_STREAM_VAR_WEAK; > + } > > return v; > } > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel From i at lvht.net Thu Mar 8 00:42:27 2018 From: i at lvht.net (Haitao Lv) Date: Thu, 8 Mar 2018 08:42:27 +0800 Subject: [PATCH] HTTP/2: make http2 server support http1 In-Reply-To: <20180305191459.GR89840@mdounin.ru> References: <8E2C1E9B-E675-49EB-9D8B-C00E94677B4A@lvht.net> <1715412.WZbLpgGAny@vbart-workstation> <20180305191459.GR89840@mdounin.ru> Message-ID: <9195D529-BCBA-4D20-8CB0-28683145D1E3@lvht.net> Sorry for disturbing. But I have to fix a buffer overflow bug. Here is the latest patch. Sorry. But please make your comments. Thank you. diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 89cfe77a..c51d8ace 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -17,6 +17,10 @@ static ssize_t ngx_http_read_request_header(ngx_http_request_t *r); static ngx_int_t ngx_http_alloc_large_header_buffer(ngx_http_request_t *r, ngx_uint_t request_line); +#if (NGX_HTTP_V2) +static void ngx_http_wait_v2_preface_handler(ngx_event_t *rev); +#endif + 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_unique_header_line(ngx_http_request_t *r, @@ -321,7 +325,7 @@ ngx_http_init_connection(ngx_connection_t *c) #if (NGX_HTTP_V2) if (hc->addr_conf->http2) { - rev->handler = ngx_http_v2_init; + rev->handler = ngx_http_wait_v2_preface_handler; } #endif @@ -377,6 +381,110 @@ ngx_http_init_connection(ngx_connection_t *c) } +#if (NGX_HTTP_V2) +static void +ngx_http_wait_v2_preface_handler(ngx_event_t *rev) +{ + size_t size; + ssize_t n; + ngx_buf_t *b; + ngx_connection_t *c; + static const u_char preface[] = "PRI"; + + c = rev->data; + size = sizeof(preface) - 1; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http wait h2 preface handler"); + + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); + ngx_http_close_connection(c); + return; + } + + if (c->close) { + ngx_http_close_connection(c); + return; + } + + b = c->buffer; + + if (b == NULL) { + b = ngx_create_temp_buf(c->pool, size); + if (b == NULL) { + ngx_http_close_connection(c); + return; + } + + c->buffer = b; + + } else if (b->start == NULL) { + + b->start = ngx_palloc(c->pool, size); + if (b->start == NULL) { + ngx_http_close_connection(c); + return; + } + + b->pos = b->start; + b->last = b->start; + b->end = b->last + size; + } + + n = c->recv(c, b->last, b->end - b->last); + + if (n == NGX_AGAIN) { + + if (!rev->timer_set) { + ngx_add_timer(rev, c->listening->post_accept_timeout); + ngx_reusable_connection(c, 1); + } + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + ngx_http_close_connection(c); + return; + } + + /* + * We are trying to not hold c->buffer's memory for an idle connection. + */ + + if (ngx_pfree(c->pool, b->start) == NGX_OK) { + b->start = NULL; + } + + return; + } + + if (n == NGX_ERROR) { + ngx_http_close_connection(c); + return; + } + + if (n == 0) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client closed connection"); + ngx_http_close_connection(c); + return; + } + + b->last += n; + + if (b->last == b->end) { + /* b will be freed in ngx_http_v2_init/ngx_http_wait_request_handler */ + + if (ngx_strncmp(b->start, preface, size) == 0) { + ngx_http_v2_init(rev); + } else { + rev->handler = ngx_http_wait_request_handler; + ngx_http_wait_request_handler(rev); + } + } +} +#endif + + static void ngx_http_wait_request_handler(ngx_event_t *rev) { @@ -430,6 +538,22 @@ ngx_http_wait_request_handler(ngx_event_t *rev) b->pos = b->start; b->last = b->start; b->end = b->last + size; + } else { + + p = ngx_palloc(c->pool, size); + if (p == NULL) { + ngx_http_close_connection(c); + return; + } + + n = b->last - b->start; + ngx_memcpy(p, b->start, n); + ngx_pfree(c->pool, b->start); + + b->start = p; + b->pos = b->start; + b->last = b->start + n; + b->end = b->last + size; } n = c->recv(c, b->last, size); diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c index d9df0f90..e36bf382 100644 --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -231,6 +231,8 @@ static ngx_http_v2_parse_header_t ngx_http_v2_parse_headers[] = { void ngx_http_v2_init(ngx_event_t *rev) { + size_t size; + ngx_buf_t *b; ngx_connection_t *c; ngx_pool_cleanup_t *cln; ngx_http_connection_t *hc; @@ -262,6 +264,23 @@ ngx_http_v2_init(ngx_event_t *rev) return; } + b = c->buffer; + + if (b != NULL) { + size = b->last - b->start; + + if (size > h2mcf->recv_buffer_size) { + size = h2mcf->recv_buffer_size; + } + + ngx_memcpy(h2mcf->recv_buffer, b->start, size); + h2c->state.buffer_used = size; + + ngx_pfree(c->pool, b->start); + ngx_pfree(c->pool, b); + c->buffer = NULL; + } + h2c->connection = c; h2c->http_connection = hc; @@ -381,13 +400,15 @@ ngx_http_v2_read_handler(ngx_event_t *rev) h2mcf = ngx_http_get_module_main_conf(h2c->http_connection->conf_ctx, ngx_http_v2_module); - available = h2mcf->recv_buffer_size - 2 * NGX_HTTP_V2_STATE_BUFFER_SIZE; + available = h2mcf->recv_buffer_size - h2c->state.buffer_used - 2 * NGX_HTTP_V2_STATE_BUFFER_SIZE; do { p = h2mcf->recv_buffer; - ngx_memcpy(p, h2c->state.buffer, NGX_HTTP_V2_STATE_BUFFER_SIZE); end = p + h2c->state.buffer_used; + if (h2c->state.buffer_used == 0) { + ngx_memcpy(p, h2c->state.buffer, NGX_HTTP_V2_STATE_BUFFER_SIZE); + } n = c->recv(c, end, available); > On Mar 6, 2018, at 03:14, Maxim Dounin wrote: > > Hello! > > On Mon, Mar 05, 2018 at 11:52:57PM +0800, Haitao Lv wrote: > > [...] > >>> Overall, the patch looks like a hack and introduces too much >>> complexity for this feature. While I understand the reasoning, >>> the proposed implementation cannot be accepted. >> >> Could you clarify that whether is this feature not accepted or this patch? >> >> If this feature is not needed, I will terminate this thread. >> >> If this patch only looks like a hack, would you like offer any advice to write >> code with good smell? > > We've previously discussed this with Valentin, and our position is > as follows: > > - The feature itself (autodetection between HTTP/2 and HTTP/1.x > protocols) might be usable, and we can consider adding it if > there will be a good and simple enough patch. (Moreover, we > think that this probably should be the default if "listen ... > http2" is configured - that is, no "http1" option.) > > - The patch suggested certainly doesn't meet the above criteria, > and it does not look like it can be fixed. > > We don't know if a good and simple enough implementation is at all > possible though. One of the possible approaches was already > proposed by Valentin (detect HTTP/2 or HTTP/1.x before starting > processing, may be similar to how we handle http-to-https > requests), but it's now immediately clear if it will work or not. > Sorry, but please don't expect any of us to provide further > guidance. > > -- > Maxim Dounin > http://mdounin.ru/ > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel From ru at nginx.com Thu Mar 8 05:13:15 2018 From: ru at nginx.com (Ruslan Ermilov) Date: Thu, 8 Mar 2018 08:13:15 +0300 Subject: [nginx] Improved code readablity. In-Reply-To: References: Message-ID: <20180308051315.GH65689@lo0.su> On Thu, Mar 08, 2018 at 08:42:06AM +0800, Junwang Zhao wrote: > I'm a little bit confused why the diffs are equal to each other, > ASAICS, the changed code removed the effect of 'flags'? > > Can you explain a little bit since I just began to read the code base :) "flags | ~NGX_HTTP_VAR_WEAK" would either evaluate to "all bits set" or "all bits set except NGX_HTTP_VAR_WEAK" if this bit is not set in "flags". > 2018-03-07 23:29 GMT+08:00 Ruslan Ermilov : > > details: http://hg.nginx.org/nginx/rev/0b1eb40de6da > > branches: > > changeset: 7226:0b1eb40de6da > > user: Ruslan Ermilov > > date: Wed Mar 07 18:28:12 2018 +0300 > > description: > > Improved code readablity. > > > > No functional changes. > > > > diffstat: > > > > src/http/ngx_http_variables.c | 8 ++++++-- > > src/stream/ngx_stream_variables.c | 8 ++++++-- > > 2 files changed, 12 insertions(+), 4 deletions(-) > > > > diffs (50 lines): > > > > diff -r e80930e5e422 -r 0b1eb40de6da src/http/ngx_http_variables.c > > --- a/src/http/ngx_http_variables.c Mon Mar 05 21:35:13 2018 +0300 > > +++ b/src/http/ngx_http_variables.c Wed Mar 07 18:28:12 2018 +0300 > > @@ -429,7 +429,9 @@ ngx_http_add_variable(ngx_conf_t *cf, ng > > return NULL; > > } > > > > - v->flags &= flags | ~NGX_HTTP_VAR_WEAK; > > + if (!(flags & NGX_HTTP_VAR_WEAK)) { > > + v->flags &= ~NGX_HTTP_VAR_WEAK; > > + } > > > > return v; > > } > > @@ -494,7 +496,9 @@ ngx_http_add_prefix_variable(ngx_conf_t > > return NULL; > > } > > > > - v->flags &= flags | ~NGX_HTTP_VAR_WEAK; > > + if (!(flags & NGX_HTTP_VAR_WEAK)) { > > + v->flags &= ~NGX_HTTP_VAR_WEAK; > > + } > > > > return v; > > } > > diff -r e80930e5e422 -r 0b1eb40de6da src/stream/ngx_stream_variables.c > > --- a/src/stream/ngx_stream_variables.c Mon Mar 05 21:35:13 2018 +0300 > > +++ b/src/stream/ngx_stream_variables.c Wed Mar 07 18:28:12 2018 +0300 > > @@ -161,7 +161,9 @@ ngx_stream_add_variable(ngx_conf_t *cf, > > return NULL; > > } > > > > - v->flags &= flags | ~NGX_STREAM_VAR_WEAK; > > + if (!(flags & NGX_STREAM_VAR_WEAK)) { > > + v->flags &= ~NGX_STREAM_VAR_WEAK; > > + } > > > > return v; > > } > > @@ -227,7 +229,9 @@ ngx_stream_add_prefix_variable(ngx_conf_ > > return NULL; > > } > > > > - v->flags &= flags | ~NGX_STREAM_VAR_WEAK; > > + if (!(flags & NGX_STREAM_VAR_WEAK)) { > > + v->flags &= ~NGX_STREAM_VAR_WEAK; > > + } > > > > return v; > > } > > _______________________________________________ > > 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 > -- Ruslan Ermilov Assume stupidity not malice From zhjwpku at gmail.com Thu Mar 8 05:30:21 2018 From: zhjwpku at gmail.com (Junwang Zhao) Date: Thu, 8 Mar 2018 13:30:21 +0800 Subject: [nginx] Improved code readablity. In-Reply-To: <20180308051315.GH65689@lo0.su> References: <20180308051315.GH65689@lo0.su> Message-ID: Got it, thanks for your reply :) On Thu, Mar 8, 2018 at 1:13 PM, Ruslan Ermilov wrote: > On Thu, Mar 08, 2018 at 08:42:06AM +0800, Junwang Zhao wrote: >> I'm a little bit confused why the diffs are equal to each other, >> ASAICS, the changed code removed the effect of 'flags'? >> >> Can you explain a little bit since I just began to read the code base :) > > "flags | ~NGX_HTTP_VAR_WEAK" would either evaluate to "all bits set" > or "all bits set except NGX_HTTP_VAR_WEAK" if this bit is not set in > "flags". > >> 2018-03-07 23:29 GMT+08:00 Ruslan Ermilov : >> > details: http://hg.nginx.org/nginx/rev/0b1eb40de6da >> > branches: >> > changeset: 7226:0b1eb40de6da >> > user: Ruslan Ermilov >> > date: Wed Mar 07 18:28:12 2018 +0300 >> > description: >> > Improved code readablity. >> > >> > No functional changes. >> > >> > diffstat: >> > >> > src/http/ngx_http_variables.c | 8 ++++++-- >> > src/stream/ngx_stream_variables.c | 8 ++++++-- >> > 2 files changed, 12 insertions(+), 4 deletions(-) >> > >> > diffs (50 lines): >> > >> > diff -r e80930e5e422 -r 0b1eb40de6da src/http/ngx_http_variables.c >> > --- a/src/http/ngx_http_variables.c Mon Mar 05 21:35:13 2018 +0300 >> > +++ b/src/http/ngx_http_variables.c Wed Mar 07 18:28:12 2018 +0300 >> > @@ -429,7 +429,9 @@ ngx_http_add_variable(ngx_conf_t *cf, ng >> > return NULL; >> > } >> > >> > - v->flags &= flags | ~NGX_HTTP_VAR_WEAK; >> > + if (!(flags & NGX_HTTP_VAR_WEAK)) { >> > + v->flags &= ~NGX_HTTP_VAR_WEAK; >> > + } >> > >> > return v; >> > } >> > @@ -494,7 +496,9 @@ ngx_http_add_prefix_variable(ngx_conf_t >> > return NULL; >> > } >> > >> > - v->flags &= flags | ~NGX_HTTP_VAR_WEAK; >> > + if (!(flags & NGX_HTTP_VAR_WEAK)) { >> > + v->flags &= ~NGX_HTTP_VAR_WEAK; >> > + } >> > >> > return v; >> > } >> > diff -r e80930e5e422 -r 0b1eb40de6da src/stream/ngx_stream_variables.c >> > --- a/src/stream/ngx_stream_variables.c Mon Mar 05 21:35:13 2018 +0300 >> > +++ b/src/stream/ngx_stream_variables.c Wed Mar 07 18:28:12 2018 +0300 >> > @@ -161,7 +161,9 @@ ngx_stream_add_variable(ngx_conf_t *cf, >> > return NULL; >> > } >> > >> > - v->flags &= flags | ~NGX_STREAM_VAR_WEAK; >> > + if (!(flags & NGX_STREAM_VAR_WEAK)) { >> > + v->flags &= ~NGX_STREAM_VAR_WEAK; >> > + } >> > >> > return v; >> > } >> > @@ -227,7 +229,9 @@ ngx_stream_add_prefix_variable(ngx_conf_ >> > return NULL; >> > } >> > >> > - v->flags &= flags | ~NGX_STREAM_VAR_WEAK; >> > + if (!(flags & NGX_STREAM_VAR_WEAK)) { >> > + v->flags &= ~NGX_STREAM_VAR_WEAK; >> > + } >> > >> > return v; >> > } >> > _______________________________________________ >> > 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 >> > > -- > Ruslan Ermilov > Assume stupidity not malice > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel From gmm at csdoc.com Mon Mar 12 08:09:35 2018 From: gmm at csdoc.com (Gena Makhomed) Date: Mon, 12 Mar 2018 10:09:35 +0200 Subject: [PATCH] Contrib: vim syntax, update core and 3rd party module directives. In-Reply-To: References: Message-ID: <263a1d4c-e53f-e919-1d8c-731f15b12765@csdoc.com> # HG changeset patch # User Gena Makhomed # Date 1519907674 -7200 # Thu Mar 01 14:34:34 2018 +0200 # Node ID 42536cf64a89641b90bc0db7223fe60703d663e0 # Parent 20f139e9ffa84f1a1db6039bbbb547cd35fc4534 Contrib: vim syntax, update core and 3rd party module directives. diff -r 20f139e9ffa8 -r 42536cf64a89 contrib/vim/syntax/nginx.vim --- a/contrib/vim/syntax/nginx.vim Wed Feb 28 16:56:58 2018 +0300 +++ b/contrib/vim/syntax/nginx.vim Thu Mar 01 14:34:34 2018 +0200 @@ -268,11 +268,14 @@ syn keyword ngxDirective contained http2_body_preread_size syn keyword ngxDirective contained http2_chunk_size syn keyword ngxDirective contained http2_idle_timeout +syn keyword ngxDirective contained http2_max_concurrent_pushes syn keyword ngxDirective contained http2_max_concurrent_streams syn keyword ngxDirective contained http2_max_field_size syn keyword ngxDirective contained http2_max_header_size syn keyword ngxDirective contained http2_max_requests syn keyword ngxDirective contained http2_pool_size +syn keyword ngxDirective contained http2_push +syn keyword ngxDirective contained http2_push_preload syn keyword ngxDirective contained http2_recv_buffer_size syn keyword ngxDirective contained http2_recv_timeout syn keyword ngxDirective contained http2_streams_index_size @@ -574,6 +577,7 @@ syn keyword ngxDirective contained sub_filter_last_modified syn keyword ngxDirective contained sub_filter_once syn keyword ngxDirective contained sub_filter_types +syn keyword ngxDirective contained subrequest_output_buffer_size syn keyword ngxDirective contained tcp_nodelay syn keyword ngxDirective contained tcp_nopush syn keyword ngxDirective contained thread_pool @@ -2028,6 +2032,7 @@ syn keyword ngxDirectiveThirdParty contained selective_cache_purge_query syn keyword ngxDirectiveThirdParty contained selective_cache_purge_redis_database syn keyword ngxDirectiveThirdParty contained selective_cache_purge_redis_host +syn keyword ngxDirectiveThirdParty contained selective_cache_purge_redis_password syn keyword ngxDirectiveThirdParty contained selective_cache_purge_redis_port syn keyword ngxDirectiveThirdParty contained selective_cache_purge_redis_unix_socket From i at lvht.net Mon Mar 12 16:44:28 2018 From: i at lvht.net (=?utf-8?B?5ZCV5rW35rab?=) Date: Tue, 13 Mar 2018 00:44:28 +0800 Subject: [PATCH] HTTP/2: make http2 server support http1 In-Reply-To: <9195D529-BCBA-4D20-8CB0-28683145D1E3@lvht.net> References: <8E2C1E9B-E675-49EB-9D8B-C00E94677B4A@lvht.net> <1715412.WZbLpgGAny@vbart-workstation> <20180305191459.GR89840@mdounin.ru> <9195D529-BCBA-4D20-8CB0-28683145D1E3@lvht.net> Message-ID: <9614077A-2D4D-4F9A-9ADC-6DBB1486336F@lvht.net> Is there any one who would like to review this patch? ???? iPhone > ? 2018?3?8??08:42?Haitao Lv ??? > > Sorry for disturbing. But I have to fix a buffer overflow bug. > Here is the latest patch. > > Sorry. But please make your comments. Thank you. > > > diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c > index 89cfe77a..c51d8ace 100644 > --- a/src/http/ngx_http_request.c > +++ b/src/http/ngx_http_request.c > @@ -17,6 +17,10 @@ static ssize_t ngx_http_read_request_header(ngx_http_request_t *r); > static ngx_int_t ngx_http_alloc_large_header_buffer(ngx_http_request_t *r, > ngx_uint_t request_line); > > +#if (NGX_HTTP_V2) > +static void ngx_http_wait_v2_preface_handler(ngx_event_t *rev); > +#endif > + > 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_unique_header_line(ngx_http_request_t *r, > @@ -321,7 +325,7 @@ ngx_http_init_connection(ngx_connection_t *c) > > #if (NGX_HTTP_V2) > if (hc->addr_conf->http2) { > - rev->handler = ngx_http_v2_init; > + rev->handler = ngx_http_wait_v2_preface_handler; > } > #endif > > @@ -377,6 +381,110 @@ ngx_http_init_connection(ngx_connection_t *c) > } > > > +#if (NGX_HTTP_V2) > +static void > +ngx_http_wait_v2_preface_handler(ngx_event_t *rev) > +{ > + size_t size; > + ssize_t n; > + ngx_buf_t *b; > + ngx_connection_t *c; > + static const u_char preface[] = "PRI"; > + > + c = rev->data; > + size = sizeof(preface) - 1; > + > + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, > + "http wait h2 preface handler"); > + > + if (rev->timedout) { > + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); > + ngx_http_close_connection(c); > + return; > + } > + > + if (c->close) { > + ngx_http_close_connection(c); > + return; > + } > + > + b = c->buffer; > + > + if (b == NULL) { > + b = ngx_create_temp_buf(c->pool, size); > + if (b == NULL) { > + ngx_http_close_connection(c); > + return; > + } > + > + c->buffer = b; > + > + } else if (b->start == NULL) { > + > + b->start = ngx_palloc(c->pool, size); > + if (b->start == NULL) { > + ngx_http_close_connection(c); > + return; > + } > + > + b->pos = b->start; > + b->last = b->start; > + b->end = b->last + size; > + } > + > + n = c->recv(c, b->last, b->end - b->last); > + > + if (n == NGX_AGAIN) { > + > + if (!rev->timer_set) { > + ngx_add_timer(rev, c->listening->post_accept_timeout); > + ngx_reusable_connection(c, 1); > + } > + > + if (ngx_handle_read_event(rev, 0) != NGX_OK) { > + ngx_http_close_connection(c); > + return; > + } > + > + /* > + * We are trying to not hold c->buffer's memory for an idle connection. > + */ > + > + if (ngx_pfree(c->pool, b->start) == NGX_OK) { > + b->start = NULL; > + } > + > + return; > + } > + > + if (n == NGX_ERROR) { > + ngx_http_close_connection(c); > + return; > + } > + > + if (n == 0) { > + ngx_log_error(NGX_LOG_INFO, c->log, 0, > + "client closed connection"); > + ngx_http_close_connection(c); > + return; > + } > + > + b->last += n; > + > + if (b->last == b->end) { > + /* b will be freed in ngx_http_v2_init/ngx_http_wait_request_handler */ > + > + if (ngx_strncmp(b->start, preface, size) == 0) { > + ngx_http_v2_init(rev); > + } else { > + rev->handler = ngx_http_wait_request_handler; > + ngx_http_wait_request_handler(rev); > + } > + } > +} > +#endif > + > + > static void > ngx_http_wait_request_handler(ngx_event_t *rev) > { > @@ -430,6 +538,22 @@ ngx_http_wait_request_handler(ngx_event_t *rev) > b->pos = b->start; > b->last = b->start; > b->end = b->last + size; > + } else { > + > + p = ngx_palloc(c->pool, size); > + if (p == NULL) { > + ngx_http_close_connection(c); > + return; > + } > + > + n = b->last - b->start; > + ngx_memcpy(p, b->start, n); > + ngx_pfree(c->pool, b->start); > + > + b->start = p; > + b->pos = b->start; > + b->last = b->start + n; > + b->end = b->last + size; > } > > n = c->recv(c, b->last, size); > diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c > index d9df0f90..e36bf382 100644 > --- a/src/http/v2/ngx_http_v2.c > +++ b/src/http/v2/ngx_http_v2.c > @@ -231,6 +231,8 @@ static ngx_http_v2_parse_header_t ngx_http_v2_parse_headers[] = { > void > ngx_http_v2_init(ngx_event_t *rev) > { > + size_t size; > + ngx_buf_t *b; > ngx_connection_t *c; > ngx_pool_cleanup_t *cln; > ngx_http_connection_t *hc; > @@ -262,6 +264,23 @@ ngx_http_v2_init(ngx_event_t *rev) > return; > } > > + b = c->buffer; > + > + if (b != NULL) { > + size = b->last - b->start; > + > + if (size > h2mcf->recv_buffer_size) { > + size = h2mcf->recv_buffer_size; > + } > + > + ngx_memcpy(h2mcf->recv_buffer, b->start, size); > + h2c->state.buffer_used = size; > + > + ngx_pfree(c->pool, b->start); > + ngx_pfree(c->pool, b); > + c->buffer = NULL; > + } > + > h2c->connection = c; > h2c->http_connection = hc; > > @@ -381,13 +400,15 @@ ngx_http_v2_read_handler(ngx_event_t *rev) > h2mcf = ngx_http_get_module_main_conf(h2c->http_connection->conf_ctx, > ngx_http_v2_module); > > - available = h2mcf->recv_buffer_size - 2 * NGX_HTTP_V2_STATE_BUFFER_SIZE; > + available = h2mcf->recv_buffer_size - h2c->state.buffer_used - 2 * NGX_HTTP_V2_STATE_BUFFER_SIZE; > > do { > p = h2mcf->recv_buffer; > > - ngx_memcpy(p, h2c->state.buffer, NGX_HTTP_V2_STATE_BUFFER_SIZE); > end = p + h2c->state.buffer_used; > + if (h2c->state.buffer_used == 0) { > + ngx_memcpy(p, h2c->state.buffer, NGX_HTTP_V2_STATE_BUFFER_SIZE); > + } > > n = c->recv(c, end, available); > > > > >> On Mar 6, 2018, at 03:14, Maxim Dounin wrote: >> >> Hello! >> >> On Mon, Mar 05, 2018 at 11:52:57PM +0800, Haitao Lv wrote: >> >> [...] >> >>>> Overall, the patch looks like a hack and introduces too much >>>> complexity for this feature. While I understand the reasoning, >>>> the proposed implementation cannot be accepted. >>> >>> Could you clarify that whether is this feature not accepted or this patch? >>> >>> If this feature is not needed, I will terminate this thread. >>> >>> If this patch only looks like a hack, would you like offer any advice to write >>> code with good smell? >> >> We've previously discussed this with Valentin, and our position is >> as follows: >> >> - The feature itself (autodetection between HTTP/2 and HTTP/1.x >> protocols) might be usable, and we can consider adding it if >> there will be a good and simple enough patch. (Moreover, we >> think that this probably should be the default if "listen ... >> http2" is configured - that is, no "http1" option.) >> >> - The patch suggested certainly doesn't meet the above criteria, >> and it does not look like it can be fixed. >> >> We don't know if a good and simple enough implementation is at all >> possible though. One of the possible approaches was already >> proposed by Valentin (detect HTTP/2 or HTTP/1.x before starting >> processing, may be similar to how we handle http-to-https >> requests), but it's now immediately clear if it will work or not. >> Sorry, but please don't expect any of us to provide further >> guidance. >> >> -- >> Maxim Dounin >> http://mdounin.ru/ >> _______________________________________________ >> nginx-devel mailing list >> nginx-devel at nginx.org >> http://mailman.nginx.org/mailman/listinfo/nginx-devel From vbart at nginx.com Mon Mar 12 17:11:25 2018 From: vbart at nginx.com (Valentin V. Bartenev) Date: Mon, 12 Mar 2018 20:11:25 +0300 Subject: [PATCH] HTTP/2: make http2 server support http1 In-Reply-To: <9614077A-2D4D-4F9A-9ADC-6DBB1486336F@lvht.net> References: <9195D529-BCBA-4D20-8CB0-28683145D1E3@lvht.net> <9614077A-2D4D-4F9A-9ADC-6DBB1486336F@lvht.net> Message-ID: <8269027.MWoiFJsfk2@vbart-workstation> I will look when time permits. But at the first glance the patch still look too complicated than it should be. wbr, Valentin V. Bartenev On Tuesday 13 March 2018 00:44:28 ??? wrote: > Is there any one who would like to review this patch? > > ???? iPhone > > > ? 2018?3?8??08:42?Haitao Lv ??? > > > > Sorry for disturbing. But I have to fix a buffer overflow bug. > > Here is the latest patch. > > > > Sorry. But please make your comments. Thank you. > > > > > > diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c > > index 89cfe77a..c51d8ace 100644 > > --- a/src/http/ngx_http_request.c > > +++ b/src/http/ngx_http_request.c > > @@ -17,6 +17,10 @@ static ssize_t ngx_http_read_request_header(ngx_http_request_t *r); > > static ngx_int_t ngx_http_alloc_large_header_buffer(ngx_http_request_t *r, > > ngx_uint_t request_line); > > > > +#if (NGX_HTTP_V2) > > +static void ngx_http_wait_v2_preface_handler(ngx_event_t *rev); > > +#endif > > + > > 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_unique_header_line(ngx_http_request_t *r, > > @@ -321,7 +325,7 @@ ngx_http_init_connection(ngx_connection_t *c) > > > > #if (NGX_HTTP_V2) > > if (hc->addr_conf->http2) { > > - rev->handler = ngx_http_v2_init; > > + rev->handler = ngx_http_wait_v2_preface_handler; > > } > > #endif > > > > @@ -377,6 +381,110 @@ ngx_http_init_connection(ngx_connection_t *c) > > } > > > > > > +#if (NGX_HTTP_V2) > > +static void > > +ngx_http_wait_v2_preface_handler(ngx_event_t *rev) > > +{ > > + size_t size; > > + ssize_t n; > > + ngx_buf_t *b; > > + ngx_connection_t *c; > > + static const u_char preface[] = "PRI"; > > + > > + c = rev->data; > > + size = sizeof(preface) - 1; > > + > > + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, > > + "http wait h2 preface handler"); > > + > > + if (rev->timedout) { > > + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); > > + ngx_http_close_connection(c); > > + return; > > + } > > + > > + if (c->close) { > > + ngx_http_close_connection(c); > > + return; > > + } > > + > > + b = c->buffer; > > + > > + if (b == NULL) { > > + b = ngx_create_temp_buf(c->pool, size); > > + if (b == NULL) { > > + ngx_http_close_connection(c); > > + return; > > + } > > + > > + c->buffer = b; > > + > > + } else if (b->start == NULL) { > > + > > + b->start = ngx_palloc(c->pool, size); > > + if (b->start == NULL) { > > + ngx_http_close_connection(c); > > + return; > > + } > > + > > + b->pos = b->start; > > + b->last = b->start; > > + b->end = b->last + size; > > + } > > + > > + n = c->recv(c, b->last, b->end - b->last); > > + > > + if (n == NGX_AGAIN) { > > + > > + if (!rev->timer_set) { > > + ngx_add_timer(rev, c->listening->post_accept_timeout); > > + ngx_reusable_connection(c, 1); > > + } > > + > > + if (ngx_handle_read_event(rev, 0) != NGX_OK) { > > + ngx_http_close_connection(c); > > + return; > > + } > > + > > + /* > > + * We are trying to not hold c->buffer's memory for an idle connection. > > + */ > > + > > + if (ngx_pfree(c->pool, b->start) == NGX_OK) { > > + b->start = NULL; > > + } > > + > > + return; > > + } > > + > > + if (n == NGX_ERROR) { > > + ngx_http_close_connection(c); > > + return; > > + } > > + > > + if (n == 0) { > > + ngx_log_error(NGX_LOG_INFO, c->log, 0, > > + "client closed connection"); > > + ngx_http_close_connection(c); > > + return; > > + } > > + > > + b->last += n; > > + > > + if (b->last == b->end) { > > + /* b will be freed in ngx_http_v2_init/ngx_http_wait_request_handler */ > > + > > + if (ngx_strncmp(b->start, preface, size) == 0) { > > + ngx_http_v2_init(rev); > > + } else { > > + rev->handler = ngx_http_wait_request_handler; > > + ngx_http_wait_request_handler(rev); > > + } > > + } > > +} > > +#endif > > + > > + > > static void > > ngx_http_wait_request_handler(ngx_event_t *rev) > > { > > @@ -430,6 +538,22 @@ ngx_http_wait_request_handler(ngx_event_t *rev) > > b->pos = b->start; > > b->last = b->start; > > b->end = b->last + size; > > + } else { > > + > > + p = ngx_palloc(c->pool, size); > > + if (p == NULL) { > > + ngx_http_close_connection(c); > > + return; > > + } > > + > > + n = b->last - b->start; > > + ngx_memcpy(p, b->start, n); > > + ngx_pfree(c->pool, b->start); > > + > > + b->start = p; > > + b->pos = b->start; > > + b->last = b->start + n; > > + b->end = b->last + size; > > } > > > > n = c->recv(c, b->last, size); > > diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c > > index d9df0f90..e36bf382 100644 > > --- a/src/http/v2/ngx_http_v2.c > > +++ b/src/http/v2/ngx_http_v2.c > > @@ -231,6 +231,8 @@ static ngx_http_v2_parse_header_t ngx_http_v2_parse_headers[] = { > > void > > ngx_http_v2_init(ngx_event_t *rev) > > { > > + size_t size; > > + ngx_buf_t *b; > > ngx_connection_t *c; > > ngx_pool_cleanup_t *cln; > > ngx_http_connection_t *hc; > > @@ -262,6 +264,23 @@ ngx_http_v2_init(ngx_event_t *rev) > > return; > > } > > > > + b = c->buffer; > > + > > + if (b != NULL) { > > + size = b->last - b->start; > > + > > + if (size > h2mcf->recv_buffer_size) { > > + size = h2mcf->recv_buffer_size; > > + } > > + > > + ngx_memcpy(h2mcf->recv_buffer, b->start, size); > > + h2c->state.buffer_used = size; > > + > > + ngx_pfree(c->pool, b->start); > > + ngx_pfree(c->pool, b); > > + c->buffer = NULL; > > + } > > + > > h2c->connection = c; > > h2c->http_connection = hc; > > > > @@ -381,13 +400,15 @@ ngx_http_v2_read_handler(ngx_event_t *rev) > > h2mcf = ngx_http_get_module_main_conf(h2c->http_connection->conf_ctx, > > ngx_http_v2_module); > > > > - available = h2mcf->recv_buffer_size - 2 * NGX_HTTP_V2_STATE_BUFFER_SIZE; > > + available = h2mcf->recv_buffer_size - h2c->state.buffer_used - 2 * NGX_HTTP_V2_STATE_BUFFER_SIZE; > > > > do { > > p = h2mcf->recv_buffer; > > > > - ngx_memcpy(p, h2c->state.buffer, NGX_HTTP_V2_STATE_BUFFER_SIZE); > > end = p + h2c->state.buffer_used; > > + if (h2c->state.buffer_used == 0) { > > + ngx_memcpy(p, h2c->state.buffer, NGX_HTTP_V2_STATE_BUFFER_SIZE); > > + } > > > > n = c->recv(c, end, available); > > > > > > > > > >> On Mar 6, 2018, at 03:14, Maxim Dounin wrote: > >> > >> Hello! > >> > >> On Mon, Mar 05, 2018 at 11:52:57PM +0800, Haitao Lv wrote: > >> > >> [...] > >> > >>>> Overall, the patch looks like a hack and introduces too much > >>>> complexity for this feature. While I understand the reasoning, > >>>> the proposed implementation cannot be accepted. > >>> > >>> Could you clarify that whether is this feature not accepted or this patch? > >>> > >>> If this feature is not needed, I will terminate this thread. > >>> > >>> If this patch only looks like a hack, would you like offer any advice to write > >>> code with good smell? > >> > >> We've previously discussed this with Valentin, and our position is > >> as follows: > >> > >> - The feature itself (autodetection between HTTP/2 and HTTP/1.x > >> protocols) might be usable, and we can consider adding it if > >> there will be a good and simple enough patch. (Moreover, we > >> think that this probably should be the default if "listen ... > >> http2" is configured - that is, no "http1" option.) > >> > >> - The patch suggested certainly doesn't meet the above criteria, > >> and it does not look like it can be fixed. > >> > >> We don't know if a good and simple enough implementation is at all > >> possible though. One of the possible approaches was already > >> proposed by Valentin (detect HTTP/2 or HTTP/1.x before starting > >> processing, may be similar to how we handle http-to-https > >> requests), but it's now immediately clear if it will work or not. > >> Sorry, but please don't expect any of us to provide further > >> guidance. > >> > > > > > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel From piotrsikora at google.com Mon Mar 12 20:37:34 2018 From: piotrsikora at google.com (Piotr Sikora) Date: Mon, 12 Mar 2018 13:37:34 -0700 Subject: [PATCH] HTTP/2: don't limit number of requests per HTTP/2 connection In-Reply-To: References: <49b677bf2ae7ab924997.1504130121@piotrsikora.sfo.corp.google.com> <5946921.3WlCo78QHa@vbart-laptop> Message-ID: Hey, just as reminder, limiting HTTP/2 connections to 1000 requests without graceful shutdown via 2-stage GOAWAY is still an issue and while this might work with browsers, you're going to break gRPC-based microservices proxied via NGINX pretty badly, so you should either implement graceful shutdown or stop limiting number of requests by default. Best regards, Piotr Sikora On Wed, Aug 30, 2017 at 4:14 PM, Piotr Sikora wrote: > Hey Valentin, > >> This opens a vector for dos attack. There are some configurations >> when memory can be allocated from connection pool for each request. >> Removing a reasonable enough limit for requests per connection >> potentially allow an attacker to grow this pool until a worker >> process will be killed due to OOM. >> >> The problem should be solved by introducing "lingering close", >> similar to HTTP/1.x. > > Yes, the proper solution is graceful shutdown via 2-stage GOAWAY, > as defined in RFC7540, Section 6.8, but I don't have capacity to > work on it now, and above patch is IMHO better than lost requests. > > Best regards, > Piotr Sikora From arut at nginx.com Tue Mar 13 12:01:16 2018 From: arut at nginx.com (Roman Arutyunyan) Date: Tue, 13 Mar 2018 12:01:16 +0000 Subject: [nginx] Stream ssl_preread: $ssl_preread_alpn_protocols variable. Message-ID: details: http://hg.nginx.org/nginx/rev/79eb4f7b6725 branches: changeset: 7227:79eb4f7b6725 user: Roman Arutyunyan date: Mon Mar 12 16:03:08 2018 +0300 description: Stream ssl_preread: $ssl_preread_alpn_protocols variable. The variable keeps a comma-separated list of protocol names from ALPN TLS extension defined by RFC 7301. diffstat: src/stream/ngx_stream_ssl_preread_module.c | 128 +++++++++++++++++++++++++++- 1 files changed, 122 insertions(+), 6 deletions(-) diffs (248 lines): diff -r 0b1eb40de6da -r 79eb4f7b6725 src/stream/ngx_stream_ssl_preread_module.c --- a/src/stream/ngx_stream_ssl_preread_module.c Wed Mar 07 18:28:12 2018 +0300 +++ b/src/stream/ngx_stream_ssl_preread_module.c Mon Mar 12 16:03:08 2018 +0300 @@ -17,10 +17,12 @@ typedef struct { typedef struct { size_t left; size_t size; + size_t ext; u_char *pos; u_char *dst; u_char buf[4]; ngx_str_t host; + ngx_str_t alpn; ngx_log_t *log; ngx_pool_t *pool; ngx_uint_t state; @@ -32,6 +34,8 @@ static ngx_int_t ngx_stream_ssl_preread_ ngx_stream_ssl_preread_ctx_t *ctx, u_char *pos, u_char *last); static ngx_int_t ngx_stream_ssl_preread_server_name_variable( ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_ssl_preread_alpn_protocols_variable( + ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_stream_ssl_preread_add_variables(ngx_conf_t *cf); static void *ngx_stream_ssl_preread_create_srv_conf(ngx_conf_t *cf); static char *ngx_stream_ssl_preread_merge_srv_conf(ngx_conf_t *cf, void *parent, @@ -85,6 +89,9 @@ static ngx_stream_variable_t ngx_stream { ngx_string("ssl_preread_server_name"), NULL, ngx_stream_ssl_preread_server_name_variable, 0, 0, 0 }, + { ngx_string("ssl_preread_alpn_protocols"), NULL, + ngx_stream_ssl_preread_alpn_protocols_variable, 0, 0, 0 }, + ngx_stream_null_variable }; @@ -139,12 +146,14 @@ ngx_stream_ssl_preread_handler(ngx_strea if (p[0] != 0x16) { ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, "ssl preread: not a handshake"); + ngx_stream_set_ctx(s, NULL, ngx_stream_ssl_preread_module); return NGX_DECLINED; } if (p[1] != 3) { ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, "ssl preread: unsupported SSL version"); + ngx_stream_set_ctx(s, NULL, ngx_stream_ssl_preread_module); return NGX_DECLINED; } @@ -158,6 +167,12 @@ ngx_stream_ssl_preread_handler(ngx_strea p += 5; rc = ngx_stream_ssl_preread_parse_record(ctx, p, p + len); + + if (rc == NGX_DECLINED) { + ngx_stream_set_ctx(s, NULL, ngx_stream_ssl_preread_module); + return NGX_DECLINED; + } + if (rc != NGX_AGAIN) { return rc; } @@ -175,7 +190,7 @@ static ngx_int_t ngx_stream_ssl_preread_parse_record(ngx_stream_ssl_preread_ctx_t *ctx, u_char *pos, u_char *last) { - size_t left, n, size; + size_t left, n, size, ext; u_char *dst, *p; enum { @@ -192,7 +207,10 @@ ngx_stream_ssl_preread_parse_record(ngx_ sw_ext_header, /* extension_type, extension_data length */ sw_sni_len, /* SNI length */ sw_sni_host_head, /* SNI name_type, host_name length */ - sw_sni_host /* SNI host_name */ + sw_sni_host, /* SNI host_name */ + sw_alpn_len, /* ALPN length */ + sw_alpn_proto_len, /* ALPN protocol_name length */ + sw_alpn_proto_data /* ALPN protocol_name */ } state; ngx_log_debug2(NGX_LOG_DEBUG_STREAM, ctx->log, 0, @@ -201,6 +219,7 @@ ngx_stream_ssl_preread_parse_record(ngx_ state = ctx->state; size = ctx->size; left = ctx->left; + ext = ctx->ext; dst = ctx->dst; p = ctx->buf; @@ -299,10 +318,18 @@ ngx_stream_ssl_preread_parse_record(ngx_ break; case sw_ext_header: - if (p[0] == 0 && p[1] == 0) { + if (p[0] == 0 && p[1] == 0 && ctx->host.data == NULL) { /* SNI extension */ state = sw_sni_len; - dst = NULL; + dst = p; + size = 2; + break; + } + + if (p[0] == 0 && p[1] == 16 && ctx->alpn.data == NULL) { + /* ALPN extension */ + state = sw_alpn_len; + dst = p; size = 2; break; } @@ -313,6 +340,7 @@ ngx_stream_ssl_preread_parse_record(ngx_ break; case sw_sni_len: + ext = (p[0] << 8) + p[1]; state = sw_sni_host_head; dst = p; size = 3; @@ -325,14 +353,21 @@ ngx_stream_ssl_preread_parse_record(ngx_ return NGX_DECLINED; } - state = sw_sni_host; size = (p[1] << 8) + p[2]; + if (ext < 3 + size) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, + "ssl preread: SNI format error"); + return NGX_DECLINED; + } + ext -= 3 + size; + ctx->host.data = ngx_pnalloc(ctx->pool, size); if (ctx->host.data == NULL) { return NGX_ERROR; } + state = sw_sni_host; dst = ctx->host.data; break; @@ -341,7 +376,64 @@ ngx_stream_ssl_preread_parse_record(ngx_ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ctx->log, 0, "ssl preread: SNI hostname \"%V\"", &ctx->host); - return NGX_OK; + + state = sw_ext; + dst = NULL; + size = ext; + break; + + case sw_alpn_len: + ext = (p[0] << 8) + p[1]; + + ctx->alpn.data = ngx_pnalloc(ctx->pool, ext); + if (ctx->alpn.data == NULL) { + return NGX_ERROR; + } + + state = sw_alpn_proto_len; + dst = p; + size = 1; + break; + + case sw_alpn_proto_len: + size = p[0]; + + if (size == 0) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, + "ssl preread: ALPN empty protocol"); + return NGX_DECLINED; + } + + if (ext < 1 + size) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, + "ssl preread: ALPN format error"); + return NGX_DECLINED; + } + ext -= 1 + size; + + state = sw_alpn_proto_data; + dst = ctx->alpn.data + ctx->alpn.len; + break; + + case sw_alpn_proto_data: + ctx->alpn.len += p[0]; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ctx->log, 0, + "ssl preread: ALPN protocols \"%V\"", &ctx->alpn); + + if (ext) { + ctx->alpn.data[ctx->alpn.len++] = ','; + + state = sw_alpn_proto_len; + dst = p; + size = 1; + break; + } + + state = sw_ext; + dst = NULL; + size = 0; + break; } if (left < size) { @@ -354,6 +446,7 @@ ngx_stream_ssl_preread_parse_record(ngx_ ctx->state = state; ctx->size = size; ctx->left = left; + ctx->ext = ext; ctx->dst = dst; return NGX_AGAIN; @@ -384,6 +477,29 @@ ngx_stream_ssl_preread_server_name_varia static ngx_int_t +ngx_stream_ssl_preread_alpn_protocols_variable(ngx_stream_session_t *s, + ngx_variable_value_t *v, uintptr_t data) +{ + ngx_stream_ssl_preread_ctx_t *ctx; + + ctx = ngx_stream_get_module_ctx(s, ngx_stream_ssl_preread_module); + + if (ctx == NULL) { + v->not_found = 1; + return NGX_OK; + } + + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->len = ctx->alpn.len; + v->data = ctx->alpn.data; + + return NGX_OK; +} + + +static ngx_int_t ngx_stream_ssl_preread_add_variables(ngx_conf_t *cf) { ngx_stream_variable_t *var, *v; From arut at nginx.com Tue Mar 13 12:01:17 2018 From: arut at nginx.com (Roman Arutyunyan) Date: Tue, 13 Mar 2018 12:01:17 +0000 Subject: [nginx] Style. Message-ID: details: http://hg.nginx.org/nginx/rev/0f811890f2f0 branches: changeset: 7228:0f811890f2f0 user: Roman Arutyunyan date: Mon Mar 12 18:38:53 2018 +0300 description: Style. diffstat: src/stream/ngx_stream_ssl_preread_module.c | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-) diffs (16 lines): diff -r 79eb4f7b6725 -r 0f811890f2f0 src/stream/ngx_stream_ssl_preread_module.c --- a/src/stream/ngx_stream_ssl_preread_module.c Mon Mar 12 16:03:08 2018 +0300 +++ b/src/stream/ngx_stream_ssl_preread_module.c Mon Mar 12 18:38:53 2018 +0300 @@ -437,9 +437,9 @@ ngx_stream_ssl_preread_parse_record(ngx_ } if (left < size) { - ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, - "ssl preread: failed to parse handshake"); - return NGX_DECLINED; + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, + "ssl preread: failed to parse handshake"); + return NGX_DECLINED; } } From xeioex at nginx.com Tue Mar 13 15:57:37 2018 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Tue, 13 Mar 2018 15:57:37 +0000 Subject: [njs] Handling the NJS_NATIVE_GETTER properties in a single place. Message-ID: details: http://hg.nginx.org/njs/rev/9dcf4f92e32a branches: changeset: 455:9dcf4f92e32a user: Dmitry Volyntsev date: Tue Mar 13 18:55:24 2018 +0300 description: Handling the NJS_NATIVE_GETTER properties in a single place. diffstat: njs/njs_array.c | 5 +- njs/njs_error.c | 5 +- njs/njs_function.c | 5 +- njs/njs_function.h | 3 +- njs/njs_object.c | 32 +++++++++++-------- njs/njs_object.h | 9 +++- njs/njs_regexp.c | 25 +++++++++------ njs/njs_string.c | 5 +- njs/njs_vm.c | 76 ++++++++++++++++++++++++----------------------- njs/njs_vm.h | 3 +- njs/test/njs_unit_test.c | 9 +++++ 11 files changed, 103 insertions(+), 74 deletions(-) diffs (472 lines): diff -r c86a0cc40ce5 -r 9dcf4f92e32a njs/njs_array.c --- a/njs/njs_array.c Wed Feb 28 19:16:25 2018 +0300 +++ b/njs/njs_array.c Tue Mar 13 18:55:24 2018 +0300 @@ -381,9 +381,10 @@ const njs_object_init_t njs_array_const static njs_ret_t -njs_array_prototype_length(njs_vm_t *vm, njs_value_t *array) +njs_array_prototype_length(njs_vm_t *vm, njs_value_t *array, + njs_value_t *retval) { - njs_value_number_set(&vm->retval, array->data.u.array->length); + njs_value_number_set(retval, array->data.u.array->length); njs_release(vm, array); diff -r c86a0cc40ce5 -r 9dcf4f92e32a njs/njs_error.c --- a/njs/njs_error.c Wed Feb 28 19:16:25 2018 +0300 +++ b/njs/njs_error.c Tue Mar 13 18:55:24 2018 +0300 @@ -533,7 +533,8 @@ njs_memory_error_constructor(njs_vm_t *v static njs_ret_t -njs_memory_error_prototype_create(njs_vm_t *vm, njs_value_t *value) +njs_memory_error_prototype_create(njs_vm_t *vm, njs_value_t *value, + njs_value_t *retval) { int32_t index; njs_value_t *proto; @@ -550,7 +551,7 @@ njs_memory_error_prototype_create(njs_vm proto = (njs_value_t *) &njs_value_void; } - vm->retval = *proto; + *retval = *proto; return NXT_OK; } diff -r c86a0cc40ce5 -r 9dcf4f92e32a njs/njs_function.c --- a/njs/njs_function.c Wed Feb 28 19:16:25 2018 +0300 +++ b/njs/njs_function.c Tue Mar 13 18:55:24 2018 +0300 @@ -411,14 +411,15 @@ njs_function_call(njs_vm_t *vm, njs_inde */ njs_ret_t -njs_function_prototype_create(njs_vm_t *vm, njs_value_t *value) +njs_function_prototype_create(njs_vm_t *vm, njs_value_t *value, + njs_value_t *retval) { njs_value_t *proto; proto = njs_function_property_prototype_create(vm, value); if (nxt_fast_path(proto != NULL)) { - vm->retval = *proto; + *retval = *proto; return NXT_OK; } diff -r c86a0cc40ce5 -r 9dcf4f92e32a njs/njs_function.h --- a/njs/njs_function.h Wed Feb 28 19:16:25 2018 +0300 +++ b/njs/njs_function.h Tue Mar 13 18:55:24 2018 +0300 @@ -146,7 +146,8 @@ struct njs_frame_s { njs_function_t *njs_function_alloc(njs_vm_t *vm); njs_function_t *njs_function_value_copy(njs_vm_t *vm, njs_value_t *value); njs_native_frame_t *njs_function_frame_alloc(njs_vm_t *vm, size_t size); -njs_ret_t njs_function_prototype_create(njs_vm_t *vm, njs_value_t *value); +njs_ret_t njs_function_prototype_create(njs_vm_t *vm, njs_value_t *value, + njs_value_t *retval); njs_value_t *njs_function_property_prototype_create(njs_vm_t *vm, njs_value_t *value); njs_ret_t njs_function_constructor(njs_vm_t *vm, njs_value_t *args, diff -r c86a0cc40ce5 -r 9dcf4f92e32a njs/njs_object.c --- a/njs/njs_object.c Wed Feb 28 19:16:25 2018 +0300 +++ b/njs/njs_object.c Tue Mar 13 18:55:24 2018 +0300 @@ -745,7 +745,7 @@ njs_object_get_prototype_of(njs_vm_t *vm njs_index_t unused) { if (nargs > 1 && njs_is_object(&args[1])) { - njs_object_prototype_get_proto(vm, &args[1]); + njs_object_prototype_get_proto(vm, &args[1], &vm->retval); return NXT_OK; } @@ -976,7 +976,8 @@ njs_object_is_extensible(njs_vm_t *vm, n */ njs_ret_t -njs_primitive_prototype_get_proto(njs_vm_t *vm, njs_value_t *value) +njs_primitive_prototype_get_proto(njs_vm_t *vm, njs_value_t *value, + njs_value_t *retval) { nxt_uint_t index; njs_object_t *proto; @@ -993,9 +994,9 @@ njs_primitive_prototype_get_proto(njs_vm proto = &vm->prototypes[index].object; } - vm->retval.data.u.object = proto; - vm->retval.type = proto->type; - vm->retval.data.truth = 1; + retval->data.u.object = proto; + retval->type = proto->type; + retval->data.truth = 1; return NXT_OK; } @@ -1008,7 +1009,8 @@ njs_primitive_prototype_get_proto(njs_vm */ njs_ret_t -njs_object_prototype_create(njs_vm_t *vm, njs_value_t *value) +njs_object_prototype_create(njs_vm_t *vm, njs_value_t *value, + njs_value_t *retval) { int32_t index; njs_value_t *proto; @@ -1027,7 +1029,7 @@ njs_object_prototype_create(njs_vm_t *vm proto = (njs_value_t *) &njs_value_void; } - vm->retval = *proto; + *retval = *proto; return NXT_OK; } @@ -1205,19 +1207,20 @@ const njs_object_init_t njs_object_cons njs_ret_t -njs_object_prototype_get_proto(njs_vm_t *vm, njs_value_t *value) +njs_object_prototype_get_proto(njs_vm_t *vm, njs_value_t *value, + njs_value_t *retval) { njs_object_t *proto; proto = value->data.u.object->__proto__; if (nxt_fast_path(proto != NULL)) { - vm->retval.data.u.object = proto; - vm->retval.type = proto->type; - vm->retval.data.truth = 1; + retval->data.u.object = proto; + retval->type = proto->type; + retval->data.truth = 1; } else { - vm->retval = njs_value_null; + *retval = njs_value_null; } return NXT_OK; @@ -1231,7 +1234,8 @@ njs_object_prototype_get_proto(njs_vm_t */ static njs_ret_t -njs_object_prototype_create_constructor(njs_vm_t *vm, njs_value_t *value) +njs_object_prototype_create_constructor(njs_vm_t *vm, njs_value_t *value, + njs_value_t *retval) { int32_t index; njs_value_t *cons; @@ -1267,7 +1271,7 @@ found: cons = njs_property_constructor_create(vm, &prototype->object.hash, &vm->scopes[NJS_SCOPE_GLOBAL][index]); if (nxt_fast_path(cons != NULL)) { - vm->retval = *cons; + *retval = *cons; return NXT_OK; } diff -r c86a0cc40ce5 -r 9dcf4f92e32a njs/njs_object.h --- a/njs/njs_object.h Wed Feb 28 19:16:25 2018 +0300 +++ b/njs/njs_object.h Tue Mar 13 18:55:24 2018 +0300 @@ -51,11 +51,14 @@ njs_ret_t njs_object_constructor(njs_vm_ 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, 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_ret_t njs_primitive_prototype_get_proto(njs_vm_t *vm, njs_value_t *value, + njs_value_t *retval); +njs_ret_t njs_object_prototype_create(njs_vm_t *vm, njs_value_t *value, + njs_value_t *retval); njs_value_t *njs_property_prototype_create(njs_vm_t *vm, nxt_lvlhsh_t *hash, njs_object_t *prototype); -njs_ret_t njs_object_prototype_get_proto(njs_vm_t *vm, njs_value_t *value); +njs_ret_t njs_object_prototype_get_proto(njs_vm_t *vm, njs_value_t *value, + njs_value_t *retval); njs_value_t *njs_property_constructor_create(njs_vm_t *vm, nxt_lvlhsh_t *hash, njs_value_t *constructor); njs_ret_t njs_object_prototype_to_string(njs_vm_t *vm, njs_value_t *args, diff -r c86a0cc40ce5 -r 9dcf4f92e32a njs/njs_regexp.c --- a/njs/njs_regexp.c Wed Feb 28 19:16:25 2018 +0300 +++ b/njs/njs_regexp.c Tue Mar 13 18:55:24 2018 +0300 @@ -474,7 +474,8 @@ njs_regexp_alloc(njs_vm_t *vm, njs_regex static njs_ret_t -njs_regexp_prototype_last_index(njs_vm_t *vm, njs_value_t *value) +njs_regexp_prototype_last_index(njs_vm_t *vm, njs_value_t *value, + njs_value_t *retval) { uint32_t index; njs_regexp_t *regexp; @@ -487,19 +488,20 @@ njs_regexp_prototype_last_index(njs_vm_t (void) njs_string_prop(&string, ®exp->string); index = njs_string_index(&string, regexp->last_index); - njs_value_number_set(&vm->retval, index); + njs_value_number_set(retval, index); return NXT_OK; } static njs_ret_t -njs_regexp_prototype_global(njs_vm_t *vm, njs_value_t *value) +njs_regexp_prototype_global(njs_vm_t *vm, njs_value_t *value, + njs_value_t *retval) { njs_regexp_pattern_t *pattern; pattern = value->data.u.regexp->pattern; - vm->retval = pattern->global ? njs_value_true : njs_value_false; + *retval = pattern->global ? njs_value_true : njs_value_false; njs_release(vm, value); return NXT_OK; @@ -507,12 +509,13 @@ njs_regexp_prototype_global(njs_vm_t *vm static njs_ret_t -njs_regexp_prototype_ignore_case(njs_vm_t *vm, njs_value_t *value) +njs_regexp_prototype_ignore_case(njs_vm_t *vm, njs_value_t *value, + njs_value_t *retval) { njs_regexp_pattern_t *pattern; pattern = value->data.u.regexp->pattern; - vm->retval = pattern->ignore_case ? njs_value_true : njs_value_false; + *retval = pattern->ignore_case ? njs_value_true : njs_value_false; njs_release(vm, value); return NXT_OK; @@ -520,12 +523,13 @@ njs_regexp_prototype_ignore_case(njs_vm_ static njs_ret_t -njs_regexp_prototype_multiline(njs_vm_t *vm, njs_value_t *value) +njs_regexp_prototype_multiline(njs_vm_t *vm, njs_value_t *value, + njs_value_t *retval) { njs_regexp_pattern_t *pattern; pattern = value->data.u.regexp->pattern; - vm->retval = pattern->multiline ? njs_value_true : njs_value_false; + *retval = pattern->multiline ? njs_value_true : njs_value_false; njs_release(vm, value); return NXT_OK; @@ -533,7 +537,8 @@ njs_regexp_prototype_multiline(njs_vm_t static njs_ret_t -njs_regexp_prototype_source(njs_vm_t *vm, njs_value_t *value) +njs_regexp_prototype_source(njs_vm_t *vm, njs_value_t *value, + njs_value_t *retval) { u_char *source; int32_t length; @@ -547,7 +552,7 @@ njs_regexp_prototype_source(njs_vm_t *vm size = strlen((char *) source) - pattern->flags; length = nxt_utf8_length(source, size); - return njs_regexp_string_create(vm, &vm->retval, source, size, length); + return njs_regexp_string_create(vm, retval, source, size, length); } diff -r c86a0cc40ce5 -r 9dcf4f92e32a njs/njs_string.c --- a/njs/njs_string.c Wed Feb 28 19:16:25 2018 +0300 +++ b/njs/njs_string.c Tue Mar 13 18:55:24 2018 +0300 @@ -434,7 +434,8 @@ const njs_object_init_t njs_string_cons static njs_ret_t -njs_string_prototype_length(njs_vm_t *vm, njs_value_t *value) +njs_string_prototype_length(njs_vm_t *vm, njs_value_t *value, + njs_value_t *retval) { size_t size; uintptr_t length; @@ -461,7 +462,7 @@ njs_string_prototype_length(njs_vm_t *vm length = (length == 0) ? size : length; } - njs_value_number_set(&vm->retval, length); + njs_value_number_set(retval, length); njs_release(vm, value); diff -r c86a0cc40ce5 -r 9dcf4f92e32a njs/njs_vm.c --- a/njs/njs_vm.c Wed Feb 28 19:16:25 2018 +0300 +++ b/njs/njs_vm.c Tue Mar 13 18:55:24 2018 +0300 @@ -53,6 +53,10 @@ typedef struct { nxt_lvlhsh_query_t lhq; + + /* scratch is used to get the value of an NJS_NATIVE_GETTER property. */ + njs_object_prop_t scratch; + njs_value_t value; njs_object_t *prototype; uint8_t query; @@ -561,15 +565,6 @@ njs_vmcode_property_get(njs_vm_t *vm, nj retval = &prop->value; break; - case NJS_NATIVE_GETTER: - ret = prop->value.data.u.getter(vm, object); - - if (nxt_fast_path(ret == NXT_OK)) { - return sizeof(njs_vmcode_prop_get_t); - } - - return ret; - default: nxt_thread_log_alert("invalid property get type:%d", prop->type); @@ -1168,6 +1163,7 @@ njs_object_property_query(njs_vm_t *vm, if (prop->type != NJS_WHITEOUT) { pq->shared = 0; + return ret; } @@ -1183,13 +1179,31 @@ njs_object_property_query(njs_vm_t *vm, if (ret == NXT_OK) { pq->shared = 1; - - if (pq->query == NJS_PROPERTY_QUERY_IN) { + prop = pq->lhq.value; + + switch (pq->query) { + case NJS_PROPERTY_QUERY_GET: + if (prop->type == NJS_NATIVE_GETTER) { + pq->scratch = *prop; + prop = &pq->scratch; + ret = prop->value.data.u.getter(vm, value, &prop->value); + + if (nxt_fast_path(ret == NXT_OK)) { + prop->type = NJS_PROPERTY; + pq->lhq.value = prop; + } + } + + break; + + case NJS_PROPERTY_QUERY_IN: prop = pq->lhq.value; if (prop->type == NJS_WHITEOUT) { return NXT_DECLINED; } + + break; } return ret; @@ -1361,12 +1375,14 @@ njs_ret_t njs_vmcode_instance_of(njs_vm_t *vm, njs_value_t *object, njs_value_t *constructor) { - nxt_int_t ret; - njs_value_t *value; - njs_object_t *prototype, *proto; - njs_object_prop_t *prop; - const njs_value_t *retval; - nxt_lvlhsh_query_t lhq; + nxt_int_t ret; + njs_value_t *value; + njs_object_t *prototype, *proto; + njs_object_prop_t *prop; + const njs_value_t *retval; + njs_property_query_t pq; + + static njs_value_t prototype_string = njs_string("prototype"); if (!njs_is_function(constructor)) { njs_exception_type_error(vm, "right argument is not a function", NULL); @@ -1376,28 +1392,14 @@ njs_vmcode_instance_of(njs_vm_t *vm, njs retval = &njs_value_false; if (njs_is_object(object)) { - - lhq.key_hash = NJS_PROTOTYPE_HASH; - lhq.key = nxt_string_value("prototype"); - - prop = njs_object_property(vm, constructor->data.u.object, &lhq); - - if (prop != NULL) { + pq.query = NJS_PROPERTY_QUERY_GET; + + ret = njs_property_query(vm, &pq, constructor, &prototype_string); + + if (nxt_fast_path(ret == NXT_OK)) { + prop = pq.lhq.value; value = &prop->value; - if (prop->type == NJS_NATIVE_GETTER) { - /* - * STUB: getter should be called by some njs_object_property() - */ - ret = prop->value.data.u.getter(vm, constructor); - - if (nxt_slow_path(ret != NXT_OK)) { - return ret; - } - - value = &vm->retval; - } - /* TODO: test prop->value is object. */ prototype = value->data.u.object; diff -r c86a0cc40ce5 -r 9dcf4f92e32a njs/njs_vm.h --- a/njs/njs_vm.h Wed Feb 28 19:16:25 2018 +0300 +++ b/njs/njs_vm.h Tue Mar 13 18:55:24 2018 +0300 @@ -117,7 +117,8 @@ typedef enum { typedef struct njs_parser_s njs_parser_t; -typedef njs_ret_t (*njs_getter_t) (njs_vm_t *vm, njs_value_t *obj); +typedef njs_ret_t (*njs_getter_t) (njs_vm_t *vm, njs_value_t *obj, + njs_value_t *retval); typedef njs_ret_t (*njs_function_native_t) (njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, njs_index_t retval); diff -r c86a0cc40ce5 -r 9dcf4f92e32a njs/test/njs_unit_test.c --- a/njs/test/njs_unit_test.c Wed Feb 28 19:16:25 2018 +0300 +++ b/njs/test/njs_unit_test.c Tue Mar 13 18:55:24 2018 +0300 @@ -5724,9 +5724,15 @@ static njs_unit_test_t njs_test[] = { nxt_string("({}).constructor === Object"), nxt_string("true") }, + { nxt_string("({}).constructor()"), + nxt_string("[object Object]") }, + { nxt_string("var a = Object.__proto__; a()"), nxt_string("undefined") }, + { nxt_string("Object.__proto__()"), + nxt_string("undefined") }, + { nxt_string("var a = Array(3); a"), nxt_string(",,") }, @@ -5793,6 +5799,9 @@ static njs_unit_test_t njs_test[] = { nxt_string("[].constructor === Array"), nxt_string("true") }, + { nxt_string("([]).constructor()"), + nxt_string("") }, + { nxt_string("Boolean()"), nxt_string("false") }, From igor at sysoev.ru Tue Mar 13 16:51:55 2018 From: igor at sysoev.ru (Igor Sysoev) Date: Tue, 13 Mar 2018 16:51:55 +0000 Subject: [njs] Removed unused null proto hash. Message-ID: details: http://hg.nginx.org/njs/rev/6738ff52a2cb branches: changeset: 456:6738ff52a2cb user: Igor Sysoev date: Tue Mar 13 19:51:25 2018 +0300 description: Removed unused null proto hash. diffstat: njs/njs_builtin.c | 12 ------------ njs/njs_object.c | 1 - njs/njs_vm.c | 5 ----- njs/njs_vm.h | 1 - 4 files changed, 0 insertions(+), 19 deletions(-) diffs (65 lines): diff -r 9dcf4f92e32a -r 6738ff52a2cb njs/njs_builtin.c --- a/njs/njs_builtin.c Tue Mar 13 18:55:24 2018 +0300 +++ b/njs/njs_builtin.c Tue Mar 13 19:51:25 2018 +0300 @@ -222,24 +222,12 @@ njs_builtin_objects_create(njs_vm_t *vm) { njs_memory_error_constructor, { NJS_SKIP_ARG, NJS_STRING_ARG } }, }; - static const njs_object_prop_t null_proto_property = { - .type = NJS_WHITEOUT, - .name = njs_string("__proto__"), - .value = njs_value(NJS_NULL, 0, 0.0), - }; - static const njs_object_prop_t function_prototype_property = { .type = NJS_NATIVE_GETTER, .name = njs_string("prototype"), .value = njs_native_getter(njs_function_prototype_create), }; - ret = njs_object_hash_create(vm, &vm->shared->null_proto_hash, - &null_proto_property, 1); - if (nxt_slow_path(ret != NXT_OK)) { - return NXT_ERROR; - } - ret = njs_object_hash_create(vm, &vm->shared->function_prototype_hash, &function_prototype_property, 1); if (nxt_slow_path(ret != NXT_OK)) { diff -r 9dcf4f92e32a -r 6738ff52a2cb njs/njs_object.c --- a/njs/njs_object.c Tue Mar 13 18:55:24 2018 +0300 +++ b/njs/njs_object.c Tue Mar 13 19:51:25 2018 +0300 @@ -299,7 +299,6 @@ njs_object_create(njs_vm_t *vm, njs_valu object->__proto__ = args[1].data.u.object; } else { - object->shared_hash = vm->shared->null_proto_hash; object->__proto__ = NULL; } diff -r 9dcf4f92e32a -r 6738ff52a2cb njs/njs_vm.c --- a/njs/njs_vm.c Tue Mar 13 18:55:24 2018 +0300 +++ b/njs/njs_vm.c Tue Mar 13 19:51:25 2018 +0300 @@ -1198,11 +1198,6 @@ njs_object_property_query(njs_vm_t *vm, case NJS_PROPERTY_QUERY_IN: prop = pq->lhq.value; - - if (prop->type == NJS_WHITEOUT) { - return NXT_DECLINED; - } - break; } diff -r 9dcf4f92e32a -r 6738ff52a2cb njs/njs_vm.h --- a/njs/njs_vm.h Tue Mar 13 18:55:24 2018 +0300 +++ b/njs/njs_vm.h Tue Mar 13 19:51:25 2018 +0300 @@ -1022,7 +1022,6 @@ typedef struct { struct njs_vm_shared_s { nxt_lvlhsh_t keywords_hash; nxt_lvlhsh_t values_hash; - nxt_lvlhsh_t null_proto_hash; nxt_lvlhsh_t function_prototype_hash; njs_object_t objects[NJS_OBJECT_MAX]; From xeioex at nginx.com Tue Mar 13 17:37:23 2018 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Tue, 13 Mar 2018 17:37:23 +0000 Subject: [njs] Fixed 1 byte heap buffer overflow. Message-ID: details: http://hg.nginx.org/njs/rev/8a274b5f1a04 branches: changeset: 457:8a274b5f1a04 user: Dmitry Volyntsev date: Tue Mar 13 20:37:01 2018 +0300 description: Fixed 1 byte heap buffer overflow. diffstat: njs/njs_vm.c | 4 ++-- njs/test/njs_unit_test.c | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diffs (30 lines): diff -r 6738ff52a2cb -r 8a274b5f1a04 njs/njs_vm.c --- a/njs/njs_vm.c Tue Mar 13 19:51:25 2018 +0300 +++ b/njs/njs_vm.c Tue Mar 13 20:37:01 2018 +0300 @@ -3514,11 +3514,11 @@ again: for (i = 0; i < backtrace->items; i++) { if (be[i].line != 0) { - len += sizeof(" at (:)\n") - 1 + 10 + len += sizeof(" at (:)\n") + 10 + be[i].name.length; } else { - len += sizeof(" at (native)\n") - 1 + len += sizeof(" at (native)\n") + be[i].name.length; } } diff -r 6738ff52a2cb -r 8a274b5f1a04 njs/test/njs_unit_test.c --- a/njs/test/njs_unit_test.c Tue Mar 13 19:51:25 2018 +0300 +++ b/njs/test/njs_unit_test.c Tue Mar 13 20:37:01 2018 +0300 @@ -5706,6 +5706,9 @@ static njs_unit_test_t njs_test[] = { nxt_string("Object.prototype.__proto__ === null"), nxt_string("true") }, + { nxt_string("Object.prototype.__proto__.f()"), + nxt_string("InternalError: method 'f' query failed:-1") }, + { nxt_string("Object.prototype.toString.call(Object.prototype)"), nxt_string("[object Object]") }, From igor at sysoev.ru Wed Mar 14 11:34:47 2018 From: igor at sysoev.ru (Igor Sysoev) Date: Wed, 14 Mar 2018 11:34:47 +0000 Subject: [njs] Code simplification. Message-ID: details: http://hg.nginx.org/njs/rev/895355fde02c branches: changeset: 458:895355fde02c user: Igor Sysoev date: Wed Mar 14 14:32:24 2018 +0300 description: Code simplification. A dead assignment left after the changeset 6738ff52a2cb was found by Clang Static Analyzer. diffstat: njs/njs_vm.c | 14 ++++---------- 1 files changed, 4 insertions(+), 10 deletions(-) diffs (31 lines): diff -r 8a274b5f1a04 -r 895355fde02c njs/njs_vm.c --- a/njs/njs_vm.c Tue Mar 13 20:37:01 2018 +0300 +++ b/njs/njs_vm.c Wed Mar 14 14:32:24 2018 +0300 @@ -1179,10 +1179,10 @@ njs_object_property_query(njs_vm_t *vm, if (ret == NXT_OK) { pq->shared = 1; - prop = pq->lhq.value; - - switch (pq->query) { - case NJS_PROPERTY_QUERY_GET: + + if (pq->query == NJS_PROPERTY_QUERY_GET) { + prop = pq->lhq.value; + if (prop->type == NJS_NATIVE_GETTER) { pq->scratch = *prop; prop = &pq->scratch; @@ -1193,12 +1193,6 @@ njs_object_property_query(njs_vm_t *vm, pq->lhq.value = prop; } } - - break; - - case NJS_PROPERTY_QUERY_IN: - prop = pq->lhq.value; - break; } return ret; From xeioex at nginx.com Thu Mar 15 12:15:47 2018 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Thu, 15 Mar 2018 12:15:47 +0000 Subject: [njs] Making njs_exception_error_create() thread-safe. Message-ID: details: http://hg.nginx.org/njs/rev/7bf6d9ba52b9 branches: changeset: 461:7bf6d9ba52b9 user: Dmitry Volyntsev date: Thu Mar 15 15:15:24 2018 +0300 description: Making njs_exception_error_create() thread-safe. diffstat: njs/njs_error.c | 3 +-- 1 files changed, 1 insertions(+), 2 deletions(-) diffs (13 lines): diff -r 720520457bfd -r 7bf6d9ba52b9 njs/njs_error.c --- a/njs/njs_error.c Thu Mar 15 15:15:24 2018 +0300 +++ b/njs/njs_error.c Thu Mar 15 15:15:24 2018 +0300 @@ -39,8 +39,7 @@ njs_exception_error_create(njs_vm_t *vm, nxt_int_t ret; njs_value_t string; njs_object_t *error; - - static char buf[256]; + char buf[256]; if (fmt != NULL) { va_start(args, fmt); From xeioex at nginx.com Thu Mar 15 12:15:46 2018 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Thu, 15 Mar 2018 12:15:46 +0000 Subject: [njs] Fixed a dead store. Message-ID: details: http://hg.nginx.org/njs/rev/720520457bfd branches: changeset: 460:720520457bfd user: Dmitry Volyntsev date: Thu Mar 15 15:15:24 2018 +0300 description: Fixed a dead store. Found by Clang Static Analyzer. diffstat: nxt/nxt_random.c | 6 ++++-- 1 files changed, 4 insertions(+), 2 deletions(-) diffs (22 lines): diff -r 218db4540b15 -r 720520457bfd nxt/nxt_random.c --- a/nxt/nxt_random.c Thu Mar 15 15:15:23 2018 +0300 +++ b/nxt/nxt_random.c Thu Mar 15 15:15:24 2018 +0300 @@ -64,14 +64,16 @@ nxt_random_stir(nxt_random_t *r, nxt_pid r->pid = pid; - n = 0; - #if (NXT_HAVE_GETRANDOM) /* Linux 3.17 getrandom(), it is not available in Glibc. */ n = syscall(SYS_getrandom, &key, NXT_RANDOM_KEY_SIZE, 0); +#else + + n = 0; + #endif if (n != NXT_RANDOM_KEY_SIZE) { From xeioex at nginx.com Thu Mar 15 12:15:46 2018 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Thu, 15 Mar 2018 12:15:46 +0000 Subject: [njs] Fixed RegExp trace handlers. Message-ID: details: http://hg.nginx.org/njs/rev/218db4540b15 branches: changeset: 459:218db4540b15 user: Dmitry Volyntsev date: Thu Mar 15 15:15:23 2018 +0300 description: Fixed RegExp trace handlers. Found by Clang Static Analyzer. diffstat: njs/njs_regexp.c | 12 +----------- 1 files changed, 1 insertions(+), 11 deletions(-) diffs (36 lines): diff -r 895355fde02c -r 218db4540b15 njs/njs_regexp.c --- a/njs/njs_regexp.c Wed Mar 14 14:32:24 2018 +0300 +++ b/njs/njs_regexp.c Thu Mar 15 15:15:23 2018 +0300 @@ -386,13 +386,8 @@ njs_regexp_compile_trace_handler(nxt_tra u_char *start) { u_char *p; - size_t size; njs_vm_t *vm; - size = sizeof("SyntaxError: ") - 1; - memcpy(start, "SyntaxError: ", size); - p = start + size; - vm = trace->data; trace = trace->next; @@ -433,17 +428,12 @@ njs_regexp_match_trace_handler(nxt_trace u_char *start) { u_char *p; - size_t size; njs_vm_t *vm; - size = sizeof("InternalError: ") - 1; - memcpy(start, "InternalError: ", size); - p = start + size; - vm = trace->data; trace = trace->next; - p = trace->handler(trace, td, p); + p = trace->handler(trace, td, start); njs_exception_internal_error(vm, (const char *) start, NULL); From xeioex at nginx.com Thu Mar 15 12:15:47 2018 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Thu, 15 Mar 2018 12:15:47 +0000 Subject: [njs] Improved the exception handling in njs_vmcode_method_frame(). Message-ID: details: http://hg.nginx.org/njs/rev/e8c08e05d18c branches: changeset: 462:e8c08e05d18c user: Dmitry Volyntsev date: Thu Mar 15 15:15:25 2018 +0300 description: Improved the exception handling in njs_vmcode_method_frame(). diffstat: njs/njs_vm.c | 8 ++++++++ njs/test/njs_unit_test.c | 8 +++++++- 2 files changed, 15 insertions(+), 1 deletions(-) diffs (50 lines): diff -r 7bf6d9ba52b9 -r e8c08e05d18c njs/njs_vm.c --- a/njs/njs_vm.c Thu Mar 15 15:15:24 2018 +0300 +++ b/njs/njs_vm.c Thu Mar 15 15:15:25 2018 +0300 @@ -2343,6 +2343,8 @@ njs_vmcode_method_frame(njs_vm_t *vm, nj method = (njs_vmcode_method_frame_t *) vm->current; + pq.lhq.key.length = 0; + pq.lhq.key.start = NULL; pq.query = NJS_PROPERTY_QUERY_GET; ret = njs_property_query(vm, &pq, object, name); @@ -2393,6 +2395,12 @@ njs_vmcode_method_frame(njs_vm_t *vm, nj method->nargs, 0, method->code.ctor); break; + case NXT_ERROR: + + /* An exception was set in njs_property_query(). */ + + return NXT_ERROR; + default: njs_exception_internal_error(vm, "method '%.*s' query failed:%d", (int) pq.lhq.key.length, pq.lhq.key.start, diff -r 7bf6d9ba52b9 -r e8c08e05d18c njs/test/njs_unit_test.c --- a/njs/test/njs_unit_test.c Thu Mar 15 15:15:24 2018 +0300 +++ b/njs/test/njs_unit_test.c Thu Mar 15 15:15:25 2018 +0300 @@ -5543,6 +5543,12 @@ static njs_unit_test_t njs_test[] = { nxt_string("try { throw null } catch (null) { throw e }"), nxt_string("SyntaxError: Unexpected token \"null\" in 1") }, + { nxt_string("'a'.f()"), + nxt_string("InternalError: method 'f' query failed:2") }, + + { nxt_string("1..f()"), + nxt_string("InternalError: method 'f' query failed:-3") }, + { nxt_string("try {}"), nxt_string("SyntaxError: Missing catch or finally after try in 1") }, @@ -5707,7 +5713,7 @@ static njs_unit_test_t njs_test[] = nxt_string("true") }, { nxt_string("Object.prototype.__proto__.f()"), - nxt_string("InternalError: method 'f' query failed:-1") }, + nxt_string("TypeError: cannot get property 'f' of undefined") }, { nxt_string("Object.prototype.toString.call(Object.prototype)"), nxt_string("[object Object]") }, From mdounin at mdounin.ru Sat Mar 17 20:08:23 2018 From: mdounin at mdounin.ru (Maxim Dounin) Date: Sat, 17 Mar 2018 20:08:23 +0000 Subject: [nginx] Upstream: trailers support, u->conf->pass_trailers flag. Message-ID: details: http://hg.nginx.org/nginx/rev/098bbd076a2d branches: changeset: 7230:098bbd076a2d user: Maxim Dounin date: Sat Mar 17 23:04:21 2018 +0300 description: Upstream: trailers support, u->conf->pass_trailers flag. Basic trailer headers support allows one to access response trailers via the $upstream_trailer_* variables. Additionally, the u->conf->pass_trailers flag was introduced. When the flag is set, trailer headers from the upstream response are passed to the client. Like normal headers, trailer headers will be hidden if present in u->conf->hide_headers_hash. diffstat: src/http/ngx_http_upstream.c | 96 ++++++++++++++++++++++++++++++++++++++++++++ src/http/ngx_http_upstream.h | 2 + 2 files changed, 98 insertions(+), 0 deletions(-) diffs (181 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 @@ -55,6 +55,8 @@ static ngx_int_t ngx_http_upstream_inter static ngx_int_t ngx_http_upstream_test_connect(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_send_response(ngx_http_request_t *r, ngx_http_upstream_t *u); static void ngx_http_upstream_upgrade(ngx_http_request_t *r, @@ -164,6 +166,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); @@ -423,6 +427,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 }, @@ -1046,6 +1053,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) { @@ -1883,6 +1897,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; @@ -2237,6 +2258,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) { @@ -2735,6 +2765,51 @@ 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 (!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; + } + + if (ngx_hash_find(&u->conf->hide_headers_hash, h[i].hash, + h[i].lowcase_key, h[i].key.len)) + { + continue; + } + + 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_send_response(ngx_http_request_t *r, ngx_http_upstream_t *u) { @@ -4272,6 +4347,12 @@ ngx_http_upstream_finalize_request(ngx_h } if (rc == 0) { + + if (ngx_http_upstream_process_trailers(r, u) != NGX_OK) { + ngx_http_finalize_request(r, NGX_ERROR); + return; + } + rc = ngx_http_send_special(r, NGX_HTTP_LAST); } else if (flush) { @@ -5382,6 +5463,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 --git a/src/http/ngx_http_upstream.h b/src/http/ngx_http_upstream.h --- a/src/http/ngx_http_upstream.h +++ b/src/http/ngx_http_upstream.h @@ -222,6 +222,7 @@ typedef struct { signed store:2; unsigned intercept_404:1; unsigned change_buffering:1; + unsigned pass_trailers:1; #if (NGX_HTTP_SSL || NGX_COMPAT) ngx_ssl_t *ssl; @@ -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 mdounin at mdounin.ru Sat Mar 17 20:08:21 2018 From: mdounin at mdounin.ru (Maxim Dounin) Date: Sat, 17 Mar 2018 20:08:21 +0000 Subject: [nginx] HTTP/2: externalized various constants and interfaces. Message-ID: details: http://hg.nginx.org/nginx/rev/87e9e4aabf1b branches: changeset: 7229:87e9e4aabf1b user: Maxim Dounin date: Sat Mar 17 23:04:20 2018 +0300 description: HTTP/2: externalized various constants and interfaces. diffstat: auto/modules | 1 + src/http/v2/ngx_http_v2.c | 2 - src/http/v2/ngx_http_v2.h | 53 +++++++++++++++++++ src/http/v2/ngx_http_v2_encode.c | 62 ++++++++++++++++++++++ src/http/v2/ngx_http_v2_filter_module.c | 89 --------------------------------- src/http/v2/ngx_http_v2_table.c | 14 +++++ 6 files changed, 130 insertions(+), 91 deletions(-) diffs (302 lines): diff --git a/auto/modules b/auto/modules --- a/auto/modules +++ b/auto/modules @@ -428,6 +428,7 @@ if [ $HTTP = YES ]; then src/http/v2/ngx_http_v2_module.h" ngx_module_srcs="src/http/v2/ngx_http_v2.c \ src/http/v2/ngx_http_v2_table.c \ + src/http/v2/ngx_http_v2_encode.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" diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -54,8 +54,6 @@ typedef struct { #define NGX_HTTP_V2_FRAME_BUFFER_SIZE 24 -#define NGX_HTTP_V2_DEFAULT_FRAME_SIZE (1 << 14) - #define NGX_HTTP_V2_ROOT (void *) -1 diff --git a/src/http/v2/ngx_http_v2.h b/src/http/v2/ngx_http_v2.h --- a/src/http/v2/ngx_http_v2.h +++ b/src/http/v2/ngx_http_v2.h @@ -18,6 +18,7 @@ #define NGX_HTTP_V2_STATE_BUFFER_SIZE 16 +#define NGX_HTTP_V2_DEFAULT_FRAME_SIZE (1 << 14) #define NGX_HTTP_V2_MAX_FRAME_SIZE ((1 << 24) - 1) #define NGX_HTTP_V2_INT_OCTETS 4 @@ -291,6 +292,9 @@ void ngx_http_v2_close_stream(ngx_http_v ngx_int_t ngx_http_v2_send_output_queue(ngx_http_v2_connection_t *h2c); +ngx_str_t *ngx_http_v2_get_static_name(ngx_uint_t index); +ngx_str_t *ngx_http_v2_get_static_value(ngx_uint_t index); + ngx_int_t ngx_http_v2_get_indexed_header(ngx_http_v2_connection_t *h2c, ngx_uint_t index, ngx_uint_t name_only); ngx_int_t ngx_http_v2_add_header(ngx_http_v2_connection_t *h2c, @@ -357,4 +361,53 @@ 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_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 + +#define NGX_HTTP_V2_AUTHORITY_INDEX 1 + +#define NGX_HTTP_V2_METHOD_INDEX 2 +#define NGX_HTTP_V2_METHOD_GET_INDEX 2 +#define NGX_HTTP_V2_METHOD_POST_INDEX 3 + +#define NGX_HTTP_V2_PATH_INDEX 4 +#define NGX_HTTP_V2_PATH_ROOT_INDEX 4 + +#define NGX_HTTP_V2_SCHEME_HTTP_INDEX 6 +#define NGX_HTTP_V2_SCHEME_HTTPS_INDEX 7 + +#define NGX_HTTP_V2_STATUS_INDEX 8 +#define NGX_HTTP_V2_STATUS_200_INDEX 8 +#define NGX_HTTP_V2_STATUS_204_INDEX 9 +#define NGX_HTTP_V2_STATUS_206_INDEX 10 +#define NGX_HTTP_V2_STATUS_304_INDEX 11 +#define NGX_HTTP_V2_STATUS_400_INDEX 12 +#define NGX_HTTP_V2_STATUS_404_INDEX 13 +#define NGX_HTTP_V2_STATUS_500_INDEX 14 + +#define NGX_HTTP_V2_ACCEPT_ENCODING_INDEX 16 +#define NGX_HTTP_V2_ACCEPT_LANGUAGE_INDEX 17 +#define NGX_HTTP_V2_CONTENT_LENGTH_INDEX 28 +#define NGX_HTTP_V2_CONTENT_TYPE_INDEX 31 +#define NGX_HTTP_V2_DATE_INDEX 33 +#define NGX_HTTP_V2_LAST_MODIFIED_INDEX 44 +#define NGX_HTTP_V2_LOCATION_INDEX 46 +#define NGX_HTTP_V2_SERVER_INDEX 54 +#define NGX_HTTP_V2_USER_AGENT_INDEX 58 +#define NGX_HTTP_V2_VARY_INDEX 59 + + +u_char *ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len, + u_char *tmp, ngx_uint_t lower); + + #endif /* _NGX_HTTP_V2_H_INCLUDED_ */ diff --git a/src/http/v2/ngx_http_v2_encode.c b/src/http/v2/ngx_http_v2_encode.c new file mode 100644 --- /dev/null +++ b/src/http/v2/ngx_http_v2_encode.c @@ -0,0 +1,62 @@ + +/* + * Copyright (C) Nginx, Inc. + * Copyright (C) Valentin V. Bartenev + */ + + +#include +#include +#include + + +static u_char *ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix, + ngx_uint_t value); + + +u_char * +ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len, u_char *tmp, + ngx_uint_t lower) +{ + size_t hlen; + + hlen = ngx_http_v2_huff_encode(src, len, tmp, lower); + + if (hlen > 0) { + *dst = NGX_HTTP_V2_ENCODE_HUFF; + dst = ngx_http_v2_write_int(dst, ngx_http_v2_prefix(7), hlen); + return ngx_cpymem(dst, tmp, hlen); + } + + *dst = NGX_HTTP_V2_ENCODE_RAW; + dst = ngx_http_v2_write_int(dst, ngx_http_v2_prefix(7), len); + + if (lower) { + ngx_strlow(dst, src, len); + return dst + len; + } + + return ngx_cpymem(dst, src, len); +} + + +static u_char * +ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix, ngx_uint_t value) +{ + if (value < prefix) { + *pos++ |= value; + return pos; + } + + *pos++ |= prefix; + value -= prefix; + + while (value >= 128) { + *pos++ = value % 128 + 128; + value /= 128; + } + + *pos++ = (u_char) value; + + return pos; +} 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 @@ -23,43 +23,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 - -#define NGX_HTTP_V2_AUTHORITY_INDEX 1 -#define NGX_HTTP_V2_METHOD_GET_INDEX 2 -#define NGX_HTTP_V2_PATH_INDEX 4 - -#define NGX_HTTP_V2_SCHEME_HTTP_INDEX 6 -#define NGX_HTTP_V2_SCHEME_HTTPS_INDEX 7 - -#define NGX_HTTP_V2_STATUS_INDEX 8 -#define NGX_HTTP_V2_STATUS_200_INDEX 8 -#define NGX_HTTP_V2_STATUS_204_INDEX 9 -#define NGX_HTTP_V2_STATUS_206_INDEX 10 -#define NGX_HTTP_V2_STATUS_304_INDEX 11 -#define NGX_HTTP_V2_STATUS_400_INDEX 12 -#define NGX_HTTP_V2_STATUS_404_INDEX 13 -#define NGX_HTTP_V2_STATUS_500_INDEX 14 - -#define NGX_HTTP_V2_ACCEPT_ENCODING_INDEX 16 -#define NGX_HTTP_V2_ACCEPT_LANGUAGE_INDEX 17 -#define NGX_HTTP_V2_CONTENT_LENGTH_INDEX 28 -#define NGX_HTTP_V2_CONTENT_TYPE_INDEX 31 -#define NGX_HTTP_V2_DATE_INDEX 33 -#define NGX_HTTP_V2_LAST_MODIFIED_INDEX 44 -#define NGX_HTTP_V2_LOCATION_INDEX 46 -#define NGX_HTTP_V2_SERVER_INDEX 54 -#define NGX_HTTP_V2_USER_AGENT_INDEX 58 -#define NGX_HTTP_V2_VARY_INDEX 59 #define NGX_HTTP_V2_NO_TRAILERS (ngx_http_v2_out_frame_t *) -1 @@ -93,10 +56,6 @@ static ngx_int_t ngx_http_v2_push_resour static ngx_int_t ngx_http_v2_push_resource(ngx_http_request_t *r, ngx_str_t *path, ngx_str_t *binary); -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_push_frame( @@ -1111,54 +1070,6 @@ ngx_http_v2_push_resource(ngx_http_reque } -static u_char * -ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len, u_char *tmp, - ngx_uint_t lower) -{ - size_t hlen; - - hlen = ngx_http_v2_huff_encode(src, len, tmp, lower); - - if (hlen > 0) { - *dst = NGX_HTTP_V2_ENCODE_HUFF; - dst = ngx_http_v2_write_int(dst, ngx_http_v2_prefix(7), hlen); - return ngx_cpymem(dst, tmp, hlen); - } - - *dst = NGX_HTTP_V2_ENCODE_RAW; - dst = ngx_http_v2_write_int(dst, ngx_http_v2_prefix(7), len); - - if (lower) { - ngx_strlow(dst, src, len); - return dst + len; - } - - return ngx_cpymem(dst, src, len); -} - - -static u_char * -ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix, ngx_uint_t value) -{ - if (value < prefix) { - *pos++ |= value; - return pos; - } - - *pos++ |= prefix; - value -= prefix; - - while (value >= 128) { - *pos++ = value % 128 + 128; - value /= 128; - } - - *pos++ = (u_char) value; - - return pos; -} - - 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) diff --git a/src/http/v2/ngx_http_v2_table.c b/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 @@ -86,6 +86,20 @@ static ngx_http_v2_header_t ngx_http_v2 / sizeof(ngx_http_v2_header_t)) +ngx_str_t * +ngx_http_v2_get_static_name(ngx_uint_t index) +{ + return &ngx_http_v2_static_table[index - 1].name; +} + + +ngx_str_t * +ngx_http_v2_get_static_value(ngx_uint_t index) +{ + return &ngx_http_v2_static_table[index - 1].value; +} + + ngx_int_t ngx_http_v2_get_indexed_header(ngx_http_v2_connection_t *h2c, ngx_uint_t index, ngx_uint_t name_only) From mdounin at mdounin.ru Sat Mar 17 20:08:24 2018 From: mdounin at mdounin.ru (Maxim Dounin) Date: Sat, 17 Mar 2018 20:08:24 +0000 Subject: [nginx] Upstream: u->request_body_blocked flag. Message-ID: details: http://hg.nginx.org/nginx/rev/22f7bdbd96d3 branches: changeset: 7231:22f7bdbd96d3 user: Maxim Dounin date: Sat Mar 17 23:04:22 2018 +0300 description: Upstream: u->request_body_blocked flag. The flag indicates whether last ngx_output_chain() returned NGX_AGAIN or not. If the flag is set, we arm the u->conf->send_timeout timer. The flag complements c->write->ready test, and allows to stop sending the request body in an output filter due to protocol-specific flow control. diffstat: src/http/ngx_http_upstream.c | 21 +++++++++++++++++++-- src/http/ngx_http_upstream.h | 1 + 2 files changed, 20 insertions(+), 2 deletions(-) diffs (63 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 @@ -1616,6 +1616,7 @@ ngx_http_upstream_connect(ngx_http_reque u->request_sent = 0; u->request_body_sent = 0; + u->request_body_blocked = 0; if (rc == NGX_AGAIN) { ngx_add_timer(c->write, u->conf->connect_timeout); @@ -1994,7 +1995,7 @@ ngx_http_upstream_send_request(ngx_http_ } if (rc == NGX_AGAIN) { - if (!c->write->ready) { + if (!c->write->ready || u->request_body_blocked) { ngx_add_timer(c->write, u->conf->send_timeout); } else if (c->write->timer_set) { @@ -2071,7 +2072,16 @@ ngx_http_upstream_send_request_body(ngx_ out = NULL; } - return ngx_output_chain(&u->output, out); + rc = ngx_output_chain(&u->output, out); + + if (rc == NGX_AGAIN) { + u->request_body_blocked = 1; + + } else { + u->request_body_blocked = 0; + } + + return rc; } if (!u->request_sent) { @@ -2112,6 +2122,13 @@ ngx_http_upstream_send_request_body(ngx_ ngx_free_chain(r->pool, ln); } + if (rc == NGX_AGAIN) { + u->request_body_blocked = 1; + + } else { + u->request_body_blocked = 0; + } + if (rc == NGX_OK && !r->reading_body) { break; } diff --git a/src/http/ngx_http_upstream.h b/src/http/ngx_http_upstream.h --- a/src/http/ngx_http_upstream.h +++ b/src/http/ngx_http_upstream.h @@ -391,6 +391,7 @@ struct ngx_http_upstream_s { unsigned request_sent:1; unsigned request_body_sent:1; + unsigned request_body_blocked:1; unsigned header_sent:1; }; From mdounin at mdounin.ru Sat Mar 17 20:08:25 2018 From: mdounin at mdounin.ru (Maxim Dounin) Date: Sat, 17 Mar 2018 20:08:25 +0000 Subject: [nginx] Upstream: u->conf->preserve_output flag. Message-ID: details: http://hg.nginx.org/nginx/rev/a7ed15573ae9 branches: changeset: 7232:a7ed15573ae9 user: Maxim Dounin date: Sat Mar 17 23:04:23 2018 +0300 description: Upstream: u->conf->preserve_output flag. The flag can be used to continue sending request body even after we've got a response from the backend. In particular, this is needed for gRPC proxying of bidirectional streaming RPCs, and also to send control frames in other forms of RPCs. diffstat: src/http/ngx_http_upstream.c | 6 ++++-- src/http/ngx_http_upstream.h | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diffs (34 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 @@ -2031,7 +2031,9 @@ ngx_http_upstream_send_request(ngx_http_ c->tcp_nopush = NGX_TCP_NOPUSH_UNSET; } - u->write_event_handler = ngx_http_upstream_dummy_handler; + if (!u->conf->preserve_output) { + u->write_event_handler = ngx_http_upstream_dummy_handler; + } if (ngx_handle_write_event(c->write, 0) != NGX_OK) { ngx_http_upstream_finalize_request(r, u, @@ -2193,7 +2195,7 @@ ngx_http_upstream_send_request_handler(n #endif - if (u->header_sent) { + if (u->header_sent && !u->conf->preserve_output) { u->write_event_handler = ngx_http_upstream_dummy_handler; (void) ngx_handle_write_event(c->write, 0); diff --git a/src/http/ngx_http_upstream.h b/src/http/ngx_http_upstream.h --- a/src/http/ngx_http_upstream.h +++ b/src/http/ngx_http_upstream.h @@ -223,6 +223,7 @@ typedef struct { unsigned intercept_404:1; unsigned change_buffering:1; unsigned pass_trailers:1; + unsigned preserve_output:1; #if (NGX_HTTP_SSL || NGX_COMPAT) ngx_ssl_t *ssl; From mdounin at mdounin.ru Sat Mar 17 20:08:27 2018 From: mdounin at mdounin.ru (Maxim Dounin) Date: Sat, 17 Mar 2018 20:08:27 +0000 Subject: [nginx] The gRPC proxy module. Message-ID: details: http://hg.nginx.org/nginx/rev/2713b2dbf5bb branches: changeset: 7233:2713b2dbf5bb user: Maxim Dounin date: Sat Mar 17 23:04:24 2018 +0300 description: The gRPC proxy module. The module allows passing requests to upstream gRPC servers. The module is built by default as long as HTTP/2 support is compiled in. Example configuration: grpc_pass 127.0.0.1:9000; Alternatively, the "grpc://" scheme can be used: grpc_pass grpc://127.0.0.1:9000; Keepalive support is available via the upstream keepalive module. Note that keepalive connections won't currently work with grpc-go as it fails to handle SETTINGS_HEADER_TABLE_SIZE. To use with SSL: grpc_pass grpcs://127.0.0.1:9000; SSL connections use ALPN "h2" when available. At least grpc-go works fine without ALPN, so if ALPN is not available we just establish a connection without it. Tested with grpc-c++ and grpc-go. diffstat: auto/modules | 11 + auto/options | 2 + src/http/modules/ngx_http_grpc_module.c | 4571 +++++++++++++++++++++++++++++++ 3 files changed, 4584 insertions(+), 0 deletions(-) diffs (truncated from 4616 to 1000 lines): diff --git a/auto/modules b/auto/modules --- a/auto/modules +++ b/auto/modules @@ -744,6 +744,17 @@ if [ $HTTP = YES ]; then . auto/module fi + if [ $HTTP_GRPC = YES -a $HTTP_V2 = YES ]; then + ngx_module_name=ngx_http_grpc_module + ngx_module_incs= + ngx_module_deps= + ngx_module_srcs=src/http/modules/ngx_http_grpc_module.c + ngx_module_libs= + ngx_module_link=$HTTP_GRPC + + . auto/module + fi + if [ $HTTP_PERL != NO ]; then ngx_module_name=ngx_http_perl_module ngx_module_incs=src/http/modules/perl diff --git a/auto/options b/auto/options --- a/auto/options +++ b/auto/options @@ -86,6 +86,7 @@ HTTP_PROXY=YES HTTP_FASTCGI=YES HTTP_UWSGI=YES HTTP_SCGI=YES +HTTP_GRPC=YES HTTP_PERL=NO HTTP_MEMCACHED=YES HTTP_LIMIT_CONN=YES @@ -262,6 +263,7 @@ do --without-http_fastcgi_module) HTTP_FASTCGI=NO ;; --without-http_uwsgi_module) HTTP_UWSGI=NO ;; --without-http_scgi_module) HTTP_SCGI=NO ;; + --without-http_grpc_module) HTTP_GRPC=NO ;; --without-http_memcached_module) HTTP_MEMCACHED=NO ;; --without-http_limit_conn_module) HTTP_LIMIT_CONN=NO ;; --without-http_limit_req_module) HTTP_LIMIT_REQ=NO ;; diff --git a/src/http/modules/ngx_http_grpc_module.c b/src/http/modules/ngx_http_grpc_module.c new file mode 100644 --- /dev/null +++ b/src/http/modules/ngx_http_grpc_module.c @@ -0,0 +1,4571 @@ + +/* + * Copyright (C) Maxim Dounin + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +typedef struct { + ngx_array_t *flushes; + ngx_array_t *lengths; + ngx_array_t *values; + ngx_hash_t hash; +} ngx_http_grpc_headers_t; + + +typedef struct { + ngx_http_upstream_conf_t upstream; + + ngx_http_grpc_headers_t headers; + ngx_array_t *headers_source; + + ngx_str_t host; + ngx_uint_t host_set; + +#if (NGX_HTTP_SSL) + ngx_uint_t ssl; + ngx_uint_t ssl_protocols; + ngx_str_t ssl_ciphers; + ngx_uint_t ssl_verify_depth; + ngx_str_t ssl_trusted_certificate; + ngx_str_t ssl_crl; + ngx_str_t ssl_certificate; + ngx_str_t ssl_certificate_key; + ngx_array_t *ssl_passwords; +#endif +} ngx_http_grpc_loc_conf_t; + + +typedef enum { + ngx_http_grpc_st_start = 0, + ngx_http_grpc_st_length_2, + ngx_http_grpc_st_length_3, + ngx_http_grpc_st_type, + ngx_http_grpc_st_flags, + ngx_http_grpc_st_stream_id, + ngx_http_grpc_st_stream_id_2, + ngx_http_grpc_st_stream_id_3, + ngx_http_grpc_st_stream_id_4, + ngx_http_grpc_st_payload, + ngx_http_grpc_st_padding +} ngx_http_grpc_state_e; + + +typedef struct { + size_t init_window; + size_t send_window; + size_t recv_window; + ngx_uint_t last_stream_id; +} ngx_http_grpc_conn_t; + + +typedef struct { + ngx_http_grpc_state_e state; + ngx_uint_t frame_state; + ngx_uint_t fragment_state; + + ngx_chain_t *in; + ngx_chain_t *out; + ngx_chain_t *free; + ngx_chain_t *busy; + + ngx_http_grpc_conn_t *connection; + + ngx_uint_t id; + + ssize_t send_window; + size_t recv_window; + + size_t rest; + ngx_uint_t stream_id; + u_char type; + u_char flags; + u_char padding; + + ngx_uint_t error; + ngx_uint_t window_update; + + ngx_uint_t setting_id; + ngx_uint_t setting_value; + + u_char ping_data[8]; + + ngx_uint_t index; + ngx_str_t name; + ngx_str_t value; + + u_char *field_end; + size_t field_length; + size_t field_rest; + u_char field_state; + + unsigned literal:1; + unsigned field_huffman:1; + + unsigned header_sent:1; + unsigned output_closed:1; + unsigned parsing_headers:1; + unsigned end_stream:1; + unsigned status:1; + + ngx_http_request_t *request; +} ngx_http_grpc_ctx_t; + + +typedef struct { + u_char length_0; + u_char length_1; + u_char length_2; + u_char type; + u_char flags; + u_char stream_id_0; + u_char stream_id_1; + u_char stream_id_2; + u_char stream_id_3; +} ngx_http_grpc_frame_t; + + +static ngx_int_t ngx_http_grpc_create_request(ngx_http_request_t *r); +static ngx_int_t ngx_http_grpc_reinit_request(ngx_http_request_t *r); +static ngx_int_t ngx_http_grpc_body_output_filter(void *data, ngx_chain_t *in); +static ngx_int_t ngx_http_grpc_process_header(ngx_http_request_t *r); +static ngx_int_t ngx_http_grpc_filter_init(void *data); +static ngx_int_t ngx_http_grpc_filter(void *data, ssize_t bytes); + +static ngx_int_t ngx_http_grpc_parse_frame(ngx_http_request_t *r, + ngx_http_grpc_ctx_t *ctx, ngx_buf_t *b); +static ngx_int_t ngx_http_grpc_parse_header(ngx_http_request_t *r, + ngx_http_grpc_ctx_t *ctx, ngx_buf_t *b); +static ngx_int_t ngx_http_grpc_parse_fragment(ngx_http_request_t *r, + ngx_http_grpc_ctx_t *ctx, ngx_buf_t *b); +static ngx_int_t ngx_http_grpc_validate_header_name(ngx_http_request_t *r, + ngx_str_t *s); +static ngx_int_t ngx_http_grpc_validate_header_value(ngx_http_request_t *r, + ngx_str_t *s); +static ngx_int_t ngx_http_grpc_parse_rst_stream(ngx_http_request_t *r, + ngx_http_grpc_ctx_t *ctx, ngx_buf_t *b); +static ngx_int_t ngx_http_grpc_parse_goaway(ngx_http_request_t *r, + ngx_http_grpc_ctx_t *ctx, ngx_buf_t *b); +static ngx_int_t ngx_http_grpc_parse_window_update(ngx_http_request_t *r, + ngx_http_grpc_ctx_t *ctx, ngx_buf_t *b); +static ngx_int_t ngx_http_grpc_parse_settings(ngx_http_request_t *r, + ngx_http_grpc_ctx_t *ctx, ngx_buf_t *b); +static ngx_int_t ngx_http_grpc_parse_ping(ngx_http_request_t *r, + ngx_http_grpc_ctx_t *ctx, ngx_buf_t *b); + +static ngx_int_t ngx_http_grpc_send_settings_ack(ngx_http_request_t *r, + ngx_http_grpc_ctx_t *ctx); +static ngx_int_t ngx_http_grpc_send_ping_ack(ngx_http_request_t *r, + ngx_http_grpc_ctx_t *ctx); +static ngx_int_t ngx_http_grpc_send_window_update(ngx_http_request_t *r, + ngx_http_grpc_ctx_t *ctx); + +static ngx_chain_t *ngx_http_grpc_get_buf(ngx_http_request_t *r, + ngx_http_grpc_ctx_t *ctx); +static ngx_http_grpc_ctx_t *ngx_http_grpc_get_ctx(ngx_http_request_t *r); +static ngx_int_t ngx_http_grpc_get_connection_data(ngx_http_request_t *r, + ngx_http_grpc_ctx_t *ctx, ngx_peer_connection_t *pc); +static void ngx_http_grpc_cleanup(void *data); + +static void ngx_http_grpc_abort_request(ngx_http_request_t *r); +static void ngx_http_grpc_finalize_request(ngx_http_request_t *r, + ngx_int_t rc); + +static void *ngx_http_grpc_create_loc_conf(ngx_conf_t *cf); +static char *ngx_http_grpc_merge_loc_conf(ngx_conf_t *cf, + void *parent, void *child); +static ngx_int_t ngx_http_grpc_init_headers(ngx_conf_t *cf, + ngx_http_grpc_loc_conf_t *conf, ngx_http_grpc_headers_t *headers, + ngx_keyval_t *default_headers); + +static char *ngx_http_grpc_pass(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); + +#if (NGX_HTTP_SSL) +static char *ngx_http_grpc_ssl_password_file(ngx_conf_t *cf, + ngx_command_t *cmd, void *conf); +static ngx_int_t ngx_http_grpc_set_ssl(ngx_conf_t *cf, + ngx_http_grpc_loc_conf_t *glcf); +#endif + + +static ngx_conf_bitmask_t ngx_http_grpc_next_upstream_masks[] = { + { ngx_string("error"), NGX_HTTP_UPSTREAM_FT_ERROR }, + { ngx_string("timeout"), NGX_HTTP_UPSTREAM_FT_TIMEOUT }, + { ngx_string("invalid_header"), NGX_HTTP_UPSTREAM_FT_INVALID_HEADER }, + { ngx_string("non_idempotent"), NGX_HTTP_UPSTREAM_FT_NON_IDEMPOTENT }, + { ngx_string("http_500"), NGX_HTTP_UPSTREAM_FT_HTTP_500 }, + { ngx_string("http_502"), NGX_HTTP_UPSTREAM_FT_HTTP_502 }, + { ngx_string("http_503"), NGX_HTTP_UPSTREAM_FT_HTTP_503 }, + { ngx_string("http_504"), NGX_HTTP_UPSTREAM_FT_HTTP_504 }, + { ngx_string("http_403"), NGX_HTTP_UPSTREAM_FT_HTTP_403 }, + { ngx_string("http_404"), NGX_HTTP_UPSTREAM_FT_HTTP_404 }, + { ngx_string("http_429"), NGX_HTTP_UPSTREAM_FT_HTTP_429 }, + { ngx_string("off"), NGX_HTTP_UPSTREAM_FT_OFF }, + { ngx_null_string, 0 } +}; + + +#if (NGX_HTTP_SSL) + +static ngx_conf_bitmask_t ngx_http_grpc_ssl_protocols[] = { + { ngx_string("SSLv2"), NGX_SSL_SSLv2 }, + { ngx_string("SSLv3"), NGX_SSL_SSLv3 }, + { ngx_string("TLSv1"), NGX_SSL_TLSv1 }, + { ngx_string("TLSv1.1"), NGX_SSL_TLSv1_1 }, + { ngx_string("TLSv1.2"), NGX_SSL_TLSv1_2 }, + { ngx_string("TLSv1.3"), NGX_SSL_TLSv1_3 }, + { ngx_null_string, 0 } +}; + +#endif + + +static ngx_command_t ngx_http_grpc_commands[] = { + + { ngx_string("grpc_pass"), + NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1, + ngx_http_grpc_pass, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("grpc_bind"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12, + ngx_http_upstream_bind_set_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_grpc_loc_conf_t, upstream.local), + NULL }, + + { ngx_string("grpc_connect_timeout"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_grpc_loc_conf_t, upstream.connect_timeout), + NULL }, + + { ngx_string("grpc_send_timeout"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_grpc_loc_conf_t, upstream.send_timeout), + NULL }, + + { ngx_string("grpc_intercept_errors"), + 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_grpc_loc_conf_t, upstream.intercept_errors), + NULL }, + + { ngx_string("grpc_buffer_size"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_grpc_loc_conf_t, upstream.buffer_size), + NULL }, + + { ngx_string("grpc_read_timeout"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_grpc_loc_conf_t, upstream.read_timeout), + NULL }, + + { ngx_string("grpc_next_upstream"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, + ngx_conf_set_bitmask_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_grpc_loc_conf_t, upstream.next_upstream), + &ngx_http_grpc_next_upstream_masks }, + + { ngx_string("grpc_next_upstream_tries"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_grpc_loc_conf_t, upstream.next_upstream_tries), + NULL }, + + { ngx_string("grpc_next_upstream_timeout"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_grpc_loc_conf_t, upstream.next_upstream_timeout), + NULL }, + + { ngx_string("grpc_set_header"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2, + ngx_conf_set_keyval_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_grpc_loc_conf_t, headers_source), + NULL }, + + { ngx_string("grpc_pass_header"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_array_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_grpc_loc_conf_t, upstream.pass_headers), + NULL }, + + { ngx_string("grpc_hide_header"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_array_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_grpc_loc_conf_t, upstream.hide_headers), + NULL }, + + { ngx_string("grpc_ignore_headers"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, + ngx_conf_set_bitmask_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_grpc_loc_conf_t, upstream.ignore_headers), + &ngx_http_upstream_ignore_headers_masks }, + +#if (NGX_HTTP_SSL) + + { ngx_string("grpc_ssl_session_reuse"), + 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_grpc_loc_conf_t, upstream.ssl_session_reuse), + NULL }, + + { ngx_string("grpc_ssl_protocols"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, + ngx_conf_set_bitmask_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_grpc_loc_conf_t, ssl_protocols), + &ngx_http_grpc_ssl_protocols }, + + { ngx_string("grpc_ssl_ciphers"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_grpc_loc_conf_t, ssl_ciphers), + NULL }, + + { ngx_string("grpc_ssl_name"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_http_set_complex_value_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_grpc_loc_conf_t, upstream.ssl_name), + NULL }, + + { ngx_string("grpc_ssl_server_name"), + 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_grpc_loc_conf_t, upstream.ssl_server_name), + NULL }, + + { ngx_string("grpc_ssl_verify"), + 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_grpc_loc_conf_t, upstream.ssl_verify), + NULL }, + + { ngx_string("grpc_ssl_verify_depth"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_grpc_loc_conf_t, ssl_verify_depth), + NULL }, + + { ngx_string("grpc_ssl_trusted_certificate"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_grpc_loc_conf_t, ssl_trusted_certificate), + NULL }, + + { ngx_string("grpc_ssl_crl"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_grpc_loc_conf_t, ssl_crl), + NULL }, + + { ngx_string("grpc_ssl_certificate"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_grpc_loc_conf_t, ssl_certificate), + NULL }, + + { ngx_string("grpc_ssl_certificate_key"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_grpc_loc_conf_t, ssl_certificate_key), + NULL }, + + { ngx_string("grpc_ssl_password_file"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_http_grpc_ssl_password_file, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + +#endif + + ngx_null_command +}; + + +static ngx_http_module_t ngx_http_grpc_module_ctx = { + NULL, /* preconfiguration */ + NULL, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL, /* merge server configuration */ + + ngx_http_grpc_create_loc_conf, /* create location configuration */ + ngx_http_grpc_merge_loc_conf /* merge location configuration */ +}; + + +ngx_module_t ngx_http_grpc_module = { + NGX_MODULE_V1, + &ngx_http_grpc_module_ctx, /* module context */ + ngx_http_grpc_commands, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static u_char ngx_http_grpc_connection_start[] = + "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" /* connection preface */ + + "\x00\x00\x12\x04\x00\x00\x00\x00\x00" /* settings frame */ + "\x00\x01\x00\x00\x00\x00" /* header table size */ + "\x00\x02\x00\x00\x00\x00" /* disable push */ + "\x00\x04\x7f\xff\xff\xff" /* initial window */ + + "\x00\x00\x04\x08\x00\x00\x00\x00\x00" /* window update frame */ + "\x7f\xff\x00\x00"; + + +static ngx_keyval_t ngx_http_grpc_headers[] = { + { ngx_string("Content-Length"), ngx_string("$content_length") }, + { ngx_string("Host"), ngx_string("") }, + { ngx_string("Connection"), ngx_string("") }, + { ngx_string("Transfer-Encoding"), ngx_string("") }, + { ngx_string("TE"), ngx_string("") }, + { ngx_string("Keep-Alive"), ngx_string("") }, + { ngx_string("Expect"), ngx_string("") }, + { ngx_string("Upgrade"), ngx_string("") }, + { ngx_null_string, ngx_null_string } +}; + + +static ngx_str_t ngx_http_grpc_hide_headers[] = { + ngx_string("Date"), + ngx_string("Server"), + ngx_string("X-Accel-Expires"), + ngx_string("X-Accel-Redirect"), + ngx_string("X-Accel-Limit-Rate"), + ngx_string("X-Accel-Buffering"), + ngx_string("X-Accel-Charset"), + ngx_null_string +}; + + +static ngx_int_t +ngx_http_grpc_handler(ngx_http_request_t *r) +{ + ngx_int_t rc; + ngx_http_upstream_t *u; + ngx_http_grpc_ctx_t *ctx; + ngx_http_grpc_loc_conf_t *glcf; + + if (ngx_http_upstream_create(r) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + glcf = ngx_http_get_module_loc_conf(r, ngx_http_grpc_module); + + u = r->upstream; + +#if (NGX_HTTP_SSL) + u->ssl = (glcf->upstream.ssl != NULL); + + if (u->ssl) { + ngx_str_set(&u->schema, "grpcs://"); + + } else { + ngx_str_set(&u->schema, "grpc://"); + } +#else + ngx_str_set(&u->schema, "grpc://"); +#endif + + u->output.tag = (ngx_buf_tag_t) &ngx_http_grpc_module; + + u->conf = &glcf->upstream; + + u->create_request = ngx_http_grpc_create_request; + u->reinit_request = ngx_http_grpc_reinit_request; + u->process_header = ngx_http_grpc_process_header; + u->abort_request = ngx_http_grpc_abort_request; + u->finalize_request = ngx_http_grpc_finalize_request; + + ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_grpc_ctx_t)); + if (ctx == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + ctx->request = r; + + ngx_http_set_ctx(r, ctx, ngx_http_grpc_module); + + u->input_filter_init = ngx_http_grpc_filter_init; + u->input_filter = ngx_http_grpc_filter; + u->input_filter_ctx = ctx; + + r->request_body_no_buffering = 1; + + rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init); + + if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { + return rc; + } + + return NGX_DONE; +} + + +static ngx_int_t +ngx_http_grpc_create_request(ngx_http_request_t *r) +{ + u_char *p, *tmp, *key_tmp, *val_tmp, *headers_frame; + size_t len, tmp_len, key_len, val_len, uri_len; + uintptr_t escape; + ngx_buf_t *b; + ngx_uint_t i, next; + ngx_chain_t *cl, *body; + ngx_list_part_t *part; + ngx_table_elt_t *header; + ngx_http_upstream_t *u; + ngx_http_grpc_frame_t *f; + ngx_http_script_code_pt code; + ngx_http_grpc_loc_conf_t *glcf; + ngx_http_script_engine_t e, le; + ngx_http_script_len_code_pt lcode; + + u = r->upstream; + + glcf = ngx_http_get_module_loc_conf(r, ngx_http_grpc_module); + + len = sizeof(ngx_http_grpc_connection_start) - 1 + + sizeof(ngx_http_grpc_frame_t); /* headers frame */ + + /* :method header */ + + if (r->method == NGX_HTTP_GET || r->method == NGX_HTTP_POST) { + len += 1; + tmp_len = 0; + + } else { + len += 1 + NGX_HTTP_V2_INT_OCTETS + r->method_name.len; + tmp_len = r->method_name.len; + } + + /* :scheme header */ + + len += 1; + + /* :path header */ + + if (r->valid_unparsed_uri) { + escape = 0; + uri_len = r->unparsed_uri.len; + + } else { + escape = 2 * ngx_escape_uri(NULL, r->uri.data, r->uri.len, + NGX_ESCAPE_URI); + uri_len = r->uri.len + escape + sizeof("?") - 1 + r->args.len; + } + + len += 1 + NGX_HTTP_V2_INT_OCTETS + uri_len; + + if (tmp_len < uri_len) { + tmp_len = uri_len; + } + + /* :authority header */ + + if (!glcf->host_set) { + len += 1 + NGX_HTTP_V2_INT_OCTETS + glcf->host.len; + + if (tmp_len < glcf->host.len) { + tmp_len = glcf->host.len; + } + } + + /* other headers */ + + ngx_http_script_flush_no_cacheable_variables(r, glcf->headers.flushes); + ngx_memzero(&le, sizeof(ngx_http_script_engine_t)); + + le.ip = glcf->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 (tmp_len < key_len) { + tmp_len = key_len; + } + + if (tmp_len < val_len) { + tmp_len = val_len; + } + } + + if (glcf->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(&glcf->headers.hash, header[i].hash, + header[i].lowcase_key, header[i].key.len)) + { + continue; + } + + len += 1 + NGX_HTTP_V2_INT_OCTETS + header[i].key.len + + NGX_HTTP_V2_INT_OCTETS + header[i].value.len; + + if (tmp_len < header[i].key.len) { + tmp_len = header[i].key.len; + } + + if (tmp_len < header[i].value.len) { + tmp_len = header[i].value.len; + } + } + } + + /* continuation frames */ + + len += sizeof(ngx_http_grpc_frame_t) + * (len / NGX_HTTP_V2_DEFAULT_FRAME_SIZE); + + + 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; + + tmp = ngx_palloc(r->pool, tmp_len * 3); + if (tmp == NULL) { + return NGX_ERROR; + } + + key_tmp = tmp + tmp_len; + val_tmp = tmp + 2 * tmp_len; + + /* connection preface */ + + b->last = ngx_copy(b->last, ngx_http_grpc_connection_start, + sizeof(ngx_http_grpc_connection_start) - 1); + + /* headers frame */ + + headers_frame = b->last; + + f = (ngx_http_grpc_frame_t *) b->last; + b->last += sizeof(ngx_http_grpc_frame_t); + + f->length_0 = 0; + f->length_1 = 0; + f->length_2 = 0; + f->type = NGX_HTTP_V2_HEADERS_FRAME; + f->flags = 0; + f->stream_id_0 = 0; + f->stream_id_1 = 0; + f->stream_id_2 = 0; + f->stream_id_3 = 1; + + if (r->method == NGX_HTTP_GET) { + *b->last++ = ngx_http_v2_indexed(NGX_HTTP_V2_METHOD_GET_INDEX); + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "grpc header: \":method: GET\""); + + } else if (r->method == NGX_HTTP_POST) { + *b->last++ = ngx_http_v2_indexed(NGX_HTTP_V2_METHOD_POST_INDEX); + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "grpc header: \":method: POST\""); + + } else { + *b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_METHOD_INDEX); + b->last = ngx_http_v2_write_value(b->last, r->method_name.data, + r->method_name.len, tmp); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "grpc header: \":method: %V\"", &r->method_name); + } + +#if (NGX_HTTP_SSL) + if (glcf->ssl) { + *b->last++ = ngx_http_v2_indexed(NGX_HTTP_V2_SCHEME_HTTPS_INDEX); + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "grpc header: \":scheme: https\""); + } else +#endif + { + *b->last++ = ngx_http_v2_indexed(NGX_HTTP_V2_SCHEME_HTTP_INDEX); + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "grpc header: \":scheme: http\""); + } + + if (r->valid_unparsed_uri) { + + if (r->unparsed_uri.len == 1 && r->unparsed_uri.data[0] == '/') { + *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, r->unparsed_uri.data, + r->unparsed_uri.len, tmp); + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "grpc header: \":path: %V\"", &r->unparsed_uri); + + } else if (escape || r->args.len > 0) { + p = val_tmp; + + if (escape) { + p = (u_char *) ngx_escape_uri(p, r->uri.data, r->uri.len, + NGX_ESCAPE_URI); + + } else { + p = ngx_copy(p, r->uri.data, r->uri.len); + } + + if (r->args.len > 0) { + *p++ = '?'; + p = ngx_copy(p, r->args.data, r->args.len); + } + + *b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_PATH_INDEX); + b->last = ngx_http_v2_write_value(b->last, val_tmp, p - val_tmp, tmp); + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "grpc header: \":path: %*s\"", p - val_tmp, val_tmp); + + } else { + *b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_PATH_INDEX); + b->last = ngx_http_v2_write_value(b->last, r->uri.data, + r->uri.len, tmp); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "grpc header: \":path: %V\"", &r->uri); + } + + if (!glcf->host_set) { + *b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_AUTHORITY_INDEX); + b->last = ngx_http_v2_write_value(b->last, glcf->host.data, + glcf->host.len, tmp); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "grpc header: \":authority: %V\"", &glcf->host); + } + + ngx_memzero(&e, sizeof(ngx_http_script_engine_t)); + + e.ip = glcf->headers.values->elts; + e.request = r; + e.flushed = 1; + + le.ip = glcf->headers.lengths->elts; + + 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) { + 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; + } + + *b->last++ = 0; + + e.pos = key_tmp; + + code = *(ngx_http_script_code_pt *) e.ip; + code((ngx_http_script_engine_t *) &e); + + b->last = ngx_http_v2_write_name(b->last, key_tmp, key_len, tmp); + + e.pos = val_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, val_tmp, val_len, tmp); + +#if (NGX_DEBUG) + if (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP) { + ngx_strlow(key_tmp, key_tmp, key_len); + + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "grpc header: \"%*s: %*s\"", + key_len, key_tmp, val_len, val_tmp); + } +#endif + } + + if (glcf->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(&glcf->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, tmp); + + b->last = ngx_http_v2_write_value(b->last, header[i].value.data, + header[i].value.len, tmp); + +#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, + "grpc header: \"%*s: %V\"", + header[i].key.len, tmp, &header[i].value); + } +#endif + } + } + + /* update headers frame length */ + + len = b->last - headers_frame - sizeof(ngx_http_grpc_frame_t); + + if (len > NGX_HTTP_V2_DEFAULT_FRAME_SIZE) { + len = NGX_HTTP_V2_DEFAULT_FRAME_SIZE; + next = 1; + + } else { + next = 0; + } + + f = (ngx_http_grpc_frame_t *) headers_frame; + + f->length_0 = (u_char) ((len >> 16) & 0xff); + f->length_1 = (u_char) ((len >> 8) & 0xff); + f->length_2 = (u_char) (len & 0xff); + + /* create additional continuation frames */ + + p = headers_frame; + From mdounin at mdounin.ru Sat Mar 17 20:08:28 2018 From: mdounin at mdounin.ru (Maxim Dounin) Date: Sat, 17 Mar 2018 20:08:28 +0000 Subject: [nginx] gRPC: special handling of the TE request header. Message-ID: details: http://hg.nginx.org/nginx/rev/c693daca57f7 branches: changeset: 7234:c693daca57f7 user: Maxim Dounin date: Sat Mar 17 23:04:25 2018 +0300 description: gRPC: special handling of the TE request header. According to the gRPC protocol specification, the "TE" header is used to detect incompatible proxies, and at least grpc-c server rejects requests without "TE: trailers". To preserve the logic, we have to pass "TE: trailers" to the backend if and only if the original request contains "trailers" in the "TE" header. Note that no other TE values are allowed in HTTP/2, so we have to remove anything else. diffstat: src/http/modules/ngx_http_grpc_module.c | 69 ++++++++++++++++++++++++++++++++- src/http/ngx_http_request.c | 4 + src/http/ngx_http_request.h | 1 + 3 files changed, 72 insertions(+), 2 deletions(-) diffs (135 lines): diff --git a/src/http/modules/ngx_http_grpc_module.c b/src/http/modules/ngx_http_grpc_module.c --- a/src/http/modules/ngx_http_grpc_module.c +++ b/src/http/modules/ngx_http_grpc_module.c @@ -176,6 +176,10 @@ static void ngx_http_grpc_abort_request( static void ngx_http_grpc_finalize_request(ngx_http_request_t *r, ngx_int_t rc); +static ngx_int_t ngx_http_grpc_internal_trailers_variable( + ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); + +static ngx_int_t ngx_http_grpc_add_variables(ngx_conf_t *cf); static void *ngx_http_grpc_create_loc_conf(ngx_conf_t *cf); static char *ngx_http_grpc_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child); @@ -419,7 +423,7 @@ static ngx_command_t ngx_http_grpc_comm static ngx_http_module_t ngx_http_grpc_module_ctx = { - NULL, /* preconfiguration */ + ngx_http_grpc_add_variables, /* preconfiguration */ NULL, /* postconfiguration */ NULL, /* create main configuration */ @@ -463,10 +467,10 @@ static u_char ngx_http_grpc_connection_ static ngx_keyval_t ngx_http_grpc_headers[] = { { ngx_string("Content-Length"), ngx_string("$content_length") }, + { ngx_string("TE"), ngx_string("$grpc_internal_trailers") }, { ngx_string("Host"), ngx_string("") }, { ngx_string("Connection"), ngx_string("") }, { ngx_string("Transfer-Encoding"), ngx_string("") }, - { ngx_string("TE"), ngx_string("") }, { ngx_string("Keep-Alive"), ngx_string("") }, { ngx_string("Expect"), ngx_string("") }, { ngx_string("Upgrade"), ngx_string("") }, @@ -486,6 +490,16 @@ static ngx_str_t ngx_http_grpc_hide_hea }; +static ngx_http_variable_t ngx_http_grpc_vars[] = { + + { ngx_string("grpc_internal_trailers"), NULL, + ngx_http_grpc_internal_trailers_variable, 0, + NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 }, + + ngx_http_null_variable +}; + + static ngx_int_t ngx_http_grpc_handler(ngx_http_request_t *r) { @@ -3996,6 +4010,57 @@ ngx_http_grpc_finalize_request(ngx_http_ } +static ngx_int_t +ngx_http_grpc_internal_trailers_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + ngx_table_elt_t *te; + + te = r->headers_in.te; + + if (te == NULL) { + v->not_found = 1; + return NGX_OK; + } + + if (ngx_strlcasestrn(te->value.data, te->value.data + te->value.len, + (u_char *) "trailers", 8 - 1) + == NULL) + { + v->not_found = 1; + return NGX_OK; + } + + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + v->data = (u_char *) "trailers"; + v->len = sizeof("trailers") - 1; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_grpc_add_variables(ngx_conf_t *cf) +{ + ngx_http_variable_t *var, *v; + + for (v = ngx_http_grpc_vars; v->name.len; v++) { + var = ngx_http_add_variable(cf, &v->name, v->flags); + if (var == NULL) { + return NGX_ERROR; + } + + var->get_handler = v->get_handler; + var->data = v->data; + } + + return NGX_OK; +} + + static void * ngx_http_grpc_create_loc_conf(ngx_conf_t *cf) { 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 @@ -132,6 +132,10 @@ ngx_http_header_t ngx_http_headers_in[] offsetof(ngx_http_headers_in_t, transfer_encoding), ngx_http_process_header_line }, + { ngx_string("TE"), + offsetof(ngx_http_headers_in_t, te), + ngx_http_process_header_line }, + { ngx_string("Expect"), offsetof(ngx_http_headers_in_t, expect), ngx_http_process_unique_header_line }, 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 @@ -197,6 +197,7 @@ typedef struct { ngx_table_elt_t *if_range; ngx_table_elt_t *transfer_encoding; + ngx_table_elt_t *te; ngx_table_elt_t *expect; ngx_table_elt_t *upgrade; From mdounin at mdounin.ru Sat Mar 17 20:08:29 2018 From: mdounin at mdounin.ru (Maxim Dounin) Date: Sat, 17 Mar 2018 20:08:29 +0000 Subject: [nginx] gRPC: special handling of "trailer only" responses. Message-ID: details: http://hg.nginx.org/nginx/rev/c2a0a838c40f branches: changeset: 7235:c2a0a838c40f user: Maxim Dounin date: Sat Mar 17 23:04:26 2018 +0300 description: gRPC: special handling of "trailer only" responses. The gRPC protocol makes a distinction between HEADERS frame with the END_STREAM flag set, and a HEADERS frame followed by an empty DATA frame with the END_STREAM flag. The latter is not permitted, and results in errors not being propagated through nginx. Instead, gRPC clients complain that "server closed the stream without sending trailers" (seen in grpc-go) or "13: Received RST_STREAM with error code 2" (seen in grpc-c). To fix this, nginx now returns HEADERS with the END_STREAM flag if the response length is known to be 0, and we are not expecting any trailer headers to be added. And the response length is explicitly set to 0 in the gRPC proxy if we see initial HEADERS frame with the END_STREAM flag set. diffstat: src/http/modules/ngx_http_grpc_module.c | 17 ++++++++++------- src/http/v2/ngx_http_v2_filter_module.c | 9 ++++++--- 2 files changed, 16 insertions(+), 10 deletions(-) diffs (60 lines): diff --git a/src/http/modules/ngx_http_grpc_module.c b/src/http/modules/ngx_http_grpc_module.c --- a/src/http/modules/ngx_http_grpc_module.c +++ b/src/http/modules/ngx_http_grpc_module.c @@ -1743,13 +1743,16 @@ ngx_http_grpc_process_header(ngx_http_re ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "grpc header done"); - if (ctx->end_stream - && ctx->in == NULL - && ctx->out == NULL - && ctx->output_closed - && b->last == b->pos) - { - u->keepalive = 1; + if (ctx->end_stream) { + u->headers_in.content_length_n = 0; + + if (ctx->in == NULL + && ctx->out == NULL + && ctx->output_closed + && b->last == b->pos) + { + u->keepalive = 1; + } } return NGX_OK; 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 @@ -136,7 +136,7 @@ ngx_http_v2_header_filter(ngx_http_reque u_char status, *pos, *start, *p, *tmp; size_t len, tmp_len; ngx_str_t host, location; - ngx_uint_t i, port; + ngx_uint_t i, port, fin; ngx_list_part_t *part; ngx_table_elt_t *header; ngx_connection_t *fc; @@ -643,7 +643,10 @@ 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); + fin = r->header_only + || (r->headers_out.content_length_n == 0 && !r->expect_trailers); + + frame = ngx_http_v2_create_headers_frame(r, start, pos, fin); if (frame == NULL) { return NGX_ERROR; } @@ -1437,7 +1440,7 @@ ngx_http_v2_send_chain(ngx_connection_t in = in->next; } - if (in == NULL) { + if (in == NULL || stream->out_closed) { if (stream->queued) { fc->write->active = 1; From gmm at csdoc.com Sun Mar 18 09:14:50 2018 From: gmm at csdoc.com (Gena Makhomed) Date: Sun, 18 Mar 2018 11:14:50 +0200 Subject: [PATCH] Contrib: vim syntax, update core and 3rd party module directives. Message-ID: <0e98087e-b4e3-620b-47d9-fcb6423097a9@csdoc.com> # HG changeset patch # User Gena Makhomed # Date 1521364274 -7200 # Sun Mar 18 11:11:14 2018 +0200 # Node ID 169d0eb76de7ffe2b2ea3005e4268542b5fc31a5 # Parent c2a0a838c40f791efc08567c791e58c894aa5c17 Contrib: vim syntax, update core and 3rd party module directives. diff -r c2a0a838c40f -r 169d0eb76de7 contrib/vim/syntax/nginx.vim --- a/contrib/vim/syntax/nginx.vim Sat Mar 17 23:04:26 2018 +0300 +++ b/contrib/vim/syntax/nginx.vim Sun Mar 18 11:11:14 2018 +0200 @@ -241,6 +241,32 @@ syn keyword ngxDirective contained geoip_proxy syn keyword ngxDirective contained geoip_proxy_recursive syn keyword ngxDirective contained google_perftools_profiles +syn keyword ngxDirective contained grpc_bind +syn keyword ngxDirective contained grpc_buffer_size +syn keyword ngxDirective contained grpc_connect_timeout +syn keyword ngxDirective contained grpc_hide_header +syn keyword ngxDirective contained grpc_ignore_headers +syn keyword ngxDirective contained grpc_intercept_errors +syn keyword ngxDirective contained grpc_next_upstream +syn keyword ngxDirective contained grpc_next_upstream_timeout +syn keyword ngxDirective contained grpc_next_upstream_tries +syn keyword ngxDirective contained grpc_pass +syn keyword ngxDirective contained grpc_pass_header +syn keyword ngxDirective contained grpc_read_timeout +syn keyword ngxDirective contained grpc_send_timeout +syn keyword ngxDirective contained grpc_set_header +syn keyword ngxDirective contained grpc_ssl_certificate +syn keyword ngxDirective contained grpc_ssl_certificate_key +syn keyword ngxDirective contained grpc_ssl_ciphers +syn keyword ngxDirective contained grpc_ssl_crl +syn keyword ngxDirective contained grpc_ssl_name +syn keyword ngxDirective contained grpc_ssl_password_file +syn keyword ngxDirective contained grpc_ssl_protocols +syn keyword ngxDirective contained grpc_ssl_server_name +syn keyword ngxDirective contained grpc_ssl_session_reuse +syn keyword ngxDirective contained grpc_ssl_trusted_certificate +syn keyword ngxDirective contained grpc_ssl_verify +syn keyword ngxDirective contained grpc_ssl_verify_depth syn keyword ngxDirective contained gunzip syn keyword ngxDirective contained gunzip_buffers syn keyword ngxDirective contained gzip @@ -268,11 +294,14 @@ syn keyword ngxDirective contained http2_body_preread_size syn keyword ngxDirective contained http2_chunk_size syn keyword ngxDirective contained http2_idle_timeout +syn keyword ngxDirective contained http2_max_concurrent_pushes syn keyword ngxDirective contained http2_max_concurrent_streams syn keyword ngxDirective contained http2_max_field_size syn keyword ngxDirective contained http2_max_header_size syn keyword ngxDirective contained http2_max_requests syn keyword ngxDirective contained http2_pool_size +syn keyword ngxDirective contained http2_push +syn keyword ngxDirective contained http2_push_preload syn keyword ngxDirective contained http2_recv_buffer_size syn keyword ngxDirective contained http2_recv_timeout syn keyword ngxDirective contained http2_streams_index_size @@ -574,6 +603,7 @@ syn keyword ngxDirective contained sub_filter_last_modified syn keyword ngxDirective contained sub_filter_once syn keyword ngxDirective contained sub_filter_types +syn keyword ngxDirective contained subrequest_output_buffer_size syn keyword ngxDirective contained tcp_nodelay syn keyword ngxDirective contained tcp_nopush syn keyword ngxDirective contained thread_pool @@ -1323,11 +1353,8 @@ " Phusion Passenger " https://www.phusionpassenger.com/library/config/nginx/reference/ -syn keyword ngxDirectiveThirdParty contained disable_security_update_check syn keyword ngxDirectiveThirdParty contained passenger_abort_on_startup_error syn keyword ngxDirectiveThirdParty contained passenger_abort_websockets_on_process_shutdown -syn keyword ngxDirectiveThirdParty contained passenger_analytics_log_group -syn keyword ngxDirectiveThirdParty contained passenger_analytics_log_user syn keyword ngxDirectiveThirdParty contained passenger_app_env syn keyword ngxDirectiveThirdParty contained passenger_app_file_descriptor_ulimit syn keyword ngxDirectiveThirdParty contained passenger_app_group_name @@ -1369,6 +1396,7 @@ syn keyword ngxDirectiveThirdParty contained passenger_max_pool_size syn keyword ngxDirectiveThirdParty contained passenger_max_preloader_idle_time syn keyword ngxDirectiveThirdParty contained passenger_max_request_queue_size +syn keyword ngxDirectiveThirdParty contained passenger_max_request_queue_time syn keyword ngxDirectiveThirdParty contained passenger_max_request_time syn keyword ngxDirectiveThirdParty contained passenger_max_requests syn keyword ngxDirectiveThirdParty contained passenger_memory_limit @@ -1402,21 +1430,22 @@ syn keyword ngxDirectiveThirdParty contained passenger_user syn keyword ngxDirectiveThirdParty contained passenger_user_switching syn keyword ngxDirectiveThirdParty contained passenger_vary_turbocache_by_cookie -syn keyword ngxDirectiveThirdParty contained rack_env -syn keyword ngxDirectiveThirdParty contained rails_app_spawner_idle_time -syn keyword ngxDirectiveThirdParty contained rails_env -syn keyword ngxDirectiveThirdParty contained security_update_check_proxy -syn keyword ngxDirectiveThirdParty contained union_station_filter -syn keyword ngxDirectiveThirdParty contained union_station_gateway_address -syn keyword ngxDirectiveThirdParty contained union_station_gateway_cert -syn keyword ngxDirectiveThirdParty contained union_station_gateway_port -syn keyword ngxDirectiveThirdParty contained union_station_key -syn keyword ngxDirectiveThirdParty contained union_station_proxy_address -syn keyword ngxDirectiveThirdParty contained union_station_support +syn keyword ngxDirectiveThirdPartyDeprecated contained passenger_analytics_log_group +syn keyword ngxDirectiveThirdPartyDeprecated contained passenger_analytics_log_user syn keyword ngxDirectiveThirdPartyDeprecated contained passenger_debug_log_file syn keyword ngxDirectiveThirdPartyDeprecated contained passenger_use_global_queue +syn keyword ngxDirectiveThirdPartyDeprecated contained rack_env +syn keyword ngxDirectiveThirdPartyDeprecated contained rails_app_spawner_idle_time +syn keyword ngxDirectiveThirdPartyDeprecated contained rails_env syn keyword ngxDirectiveThirdPartyDeprecated contained rails_framework_spawner_idle_time syn keyword ngxDirectiveThirdPartyDeprecated contained rails_spawn_method +syn keyword ngxDirectiveThirdPartyDeprecated contained union_station_filter +syn keyword ngxDirectiveThirdPartyDeprecated contained union_station_gateway_address +syn keyword ngxDirectiveThirdPartyDeprecated contained union_station_gateway_cert +syn keyword ngxDirectiveThirdPartyDeprecated contained union_station_gateway_port +syn keyword ngxDirectiveThirdPartyDeprecated contained union_station_key +syn keyword ngxDirectiveThirdPartyDeprecated contained union_station_proxy_address +syn keyword ngxDirectiveThirdPartyDeprecated contained union_station_support " ngx_postgres is an upstream module that allows nginx to communicate directly with PostgreSQL database " https://github.com/FRiCKLE/ngx_postgres @@ -2028,6 +2057,7 @@ syn keyword ngxDirectiveThirdParty contained selective_cache_purge_query syn keyword ngxDirectiveThirdParty contained selective_cache_purge_redis_database syn keyword ngxDirectiveThirdParty contained selective_cache_purge_redis_host +syn keyword ngxDirectiveThirdParty contained selective_cache_purge_redis_password syn keyword ngxDirectiveThirdParty contained selective_cache_purge_redis_port syn keyword ngxDirectiveThirdParty contained selective_cache_purge_redis_unix_socket From i at lvht.net Sun Mar 18 16:57:23 2018 From: i at lvht.net (Haitao Lv) Date: Mon, 19 Mar 2018 00:57:23 +0800 Subject: [PATCH] HTTP/2: make http2 server support http1 In-Reply-To: <8269027.MWoiFJsfk2@vbart-workstation> References: <9195D529-BCBA-4D20-8CB0-28683145D1E3@lvht.net> <9614077A-2D4D-4F9A-9ADC-6DBB1486336F@lvht.net> <8269027.MWoiFJsfk2@vbart-workstation> Message-ID: ping @wbr > On Mar 13, 2018, at 01:11, Valentin V. Bartenev wrote: > > I will look when time permits. > > But at the first glance the patch still look too complicated than it should be. > > wbr, Valentin V. Bartenev > > > On Tuesday 13 March 2018 00:44:28 ??? wrote: >> Is there any one who would like to review this patch? >> >> ???? iPhone >> >>> ? 2018?3?8??08:42?Haitao Lv ??? >>> >>> Sorry for disturbing. But I have to fix a buffer overflow bug. >>> Here is the latest patch. >>> >>> Sorry. But please make your comments. Thank you. >>> >>> >>> diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c >>> index 89cfe77a..c51d8ace 100644 >>> --- a/src/http/ngx_http_request.c >>> +++ b/src/http/ngx_http_request.c >>> @@ -17,6 +17,10 @@ static ssize_t ngx_http_read_request_header(ngx_http_request_t *r); >>> static ngx_int_t ngx_http_alloc_large_header_buffer(ngx_http_request_t *r, >>> ngx_uint_t request_line); >>> >>> +#if (NGX_HTTP_V2) >>> +static void ngx_http_wait_v2_preface_handler(ngx_event_t *rev); >>> +#endif >>> + >>> 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_unique_header_line(ngx_http_request_t *r, >>> @@ -321,7 +325,7 @@ ngx_http_init_connection(ngx_connection_t *c) >>> >>> #if (NGX_HTTP_V2) >>> if (hc->addr_conf->http2) { >>> - rev->handler = ngx_http_v2_init; >>> + rev->handler = ngx_http_wait_v2_preface_handler; >>> } >>> #endif >>> >>> @@ -377,6 +381,110 @@ ngx_http_init_connection(ngx_connection_t *c) >>> } >>> >>> >>> +#if (NGX_HTTP_V2) >>> +static void >>> +ngx_http_wait_v2_preface_handler(ngx_event_t *rev) >>> +{ >>> + size_t size; >>> + ssize_t n; >>> + ngx_buf_t *b; >>> + ngx_connection_t *c; >>> + static const u_char preface[] = "PRI"; >>> + >>> + c = rev->data; >>> + size = sizeof(preface) - 1; >>> + >>> + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, >>> + "http wait h2 preface handler"); >>> + >>> + if (rev->timedout) { >>> + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); >>> + ngx_http_close_connection(c); >>> + return; >>> + } >>> + >>> + if (c->close) { >>> + ngx_http_close_connection(c); >>> + return; >>> + } >>> + >>> + b = c->buffer; >>> + >>> + if (b == NULL) { >>> + b = ngx_create_temp_buf(c->pool, size); >>> + if (b == NULL) { >>> + ngx_http_close_connection(c); >>> + return; >>> + } >>> + >>> + c->buffer = b; >>> + >>> + } else if (b->start == NULL) { >>> + >>> + b->start = ngx_palloc(c->pool, size); >>> + if (b->start == NULL) { >>> + ngx_http_close_connection(c); >>> + return; >>> + } >>> + >>> + b->pos = b->start; >>> + b->last = b->start; >>> + b->end = b->last + size; >>> + } >>> + >>> + n = c->recv(c, b->last, b->end - b->last); >>> + >>> + if (n == NGX_AGAIN) { >>> + >>> + if (!rev->timer_set) { >>> + ngx_add_timer(rev, c->listening->post_accept_timeout); >>> + ngx_reusable_connection(c, 1); >>> + } >>> + >>> + if (ngx_handle_read_event(rev, 0) != NGX_OK) { >>> + ngx_http_close_connection(c); >>> + return; >>> + } >>> + >>> + /* >>> + * We are trying to not hold c->buffer's memory for an idle connection. >>> + */ >>> + >>> + if (ngx_pfree(c->pool, b->start) == NGX_OK) { >>> + b->start = NULL; >>> + } >>> + >>> + return; >>> + } >>> + >>> + if (n == NGX_ERROR) { >>> + ngx_http_close_connection(c); >>> + return; >>> + } >>> + >>> + if (n == 0) { >>> + ngx_log_error(NGX_LOG_INFO, c->log, 0, >>> + "client closed connection"); >>> + ngx_http_close_connection(c); >>> + return; >>> + } >>> + >>> + b->last += n; >>> + >>> + if (b->last == b->end) { >>> + /* b will be freed in ngx_http_v2_init/ngx_http_wait_request_handler */ >>> + >>> + if (ngx_strncmp(b->start, preface, size) == 0) { >>> + ngx_http_v2_init(rev); >>> + } else { >>> + rev->handler = ngx_http_wait_request_handler; >>> + ngx_http_wait_request_handler(rev); >>> + } >>> + } >>> +} >>> +#endif >>> + >>> + >>> static void >>> ngx_http_wait_request_handler(ngx_event_t *rev) >>> { >>> @@ -430,6 +538,22 @@ ngx_http_wait_request_handler(ngx_event_t *rev) >>> b->pos = b->start; >>> b->last = b->start; >>> b->end = b->last + size; >>> + } else { >>> + >>> + p = ngx_palloc(c->pool, size); >>> + if (p == NULL) { >>> + ngx_http_close_connection(c); >>> + return; >>> + } >>> + >>> + n = b->last - b->start; >>> + ngx_memcpy(p, b->start, n); >>> + ngx_pfree(c->pool, b->start); >>> + >>> + b->start = p; >>> + b->pos = b->start; >>> + b->last = b->start + n; >>> + b->end = b->last + size; >>> } >>> >>> n = c->recv(c, b->last, size); >>> diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c >>> index d9df0f90..e36bf382 100644 >>> --- a/src/http/v2/ngx_http_v2.c >>> +++ b/src/http/v2/ngx_http_v2.c >>> @@ -231,6 +231,8 @@ static ngx_http_v2_parse_header_t ngx_http_v2_parse_headers[] = { >>> void >>> ngx_http_v2_init(ngx_event_t *rev) >>> { >>> + size_t size; >>> + ngx_buf_t *b; >>> ngx_connection_t *c; >>> ngx_pool_cleanup_t *cln; >>> ngx_http_connection_t *hc; >>> @@ -262,6 +264,23 @@ ngx_http_v2_init(ngx_event_t *rev) >>> return; >>> } >>> >>> + b = c->buffer; >>> + >>> + if (b != NULL) { >>> + size = b->last - b->start; >>> + >>> + if (size > h2mcf->recv_buffer_size) { >>> + size = h2mcf->recv_buffer_size; >>> + } >>> + >>> + ngx_memcpy(h2mcf->recv_buffer, b->start, size); >>> + h2c->state.buffer_used = size; >>> + >>> + ngx_pfree(c->pool, b->start); >>> + ngx_pfree(c->pool, b); >>> + c->buffer = NULL; >>> + } >>> + >>> h2c->connection = c; >>> h2c->http_connection = hc; >>> >>> @@ -381,13 +400,15 @@ ngx_http_v2_read_handler(ngx_event_t *rev) >>> h2mcf = ngx_http_get_module_main_conf(h2c->http_connection->conf_ctx, >>> ngx_http_v2_module); >>> >>> - available = h2mcf->recv_buffer_size - 2 * NGX_HTTP_V2_STATE_BUFFER_SIZE; >>> + available = h2mcf->recv_buffer_size - h2c->state.buffer_used - 2 * NGX_HTTP_V2_STATE_BUFFER_SIZE; >>> >>> do { >>> p = h2mcf->recv_buffer; >>> >>> - ngx_memcpy(p, h2c->state.buffer, NGX_HTTP_V2_STATE_BUFFER_SIZE); >>> end = p + h2c->state.buffer_used; >>> + if (h2c->state.buffer_used == 0) { >>> + ngx_memcpy(p, h2c->state.buffer, NGX_HTTP_V2_STATE_BUFFER_SIZE); >>> + } >>> >>> n = c->recv(c, end, available); >>> >>> >>> >>> >>>> On Mar 6, 2018, at 03:14, Maxim Dounin wrote: >>>> >>>> Hello! >>>> >>>> On Mon, Mar 05, 2018 at 11:52:57PM +0800, Haitao Lv wrote: >>>> >>>> [...] >>>> >>>>>> Overall, the patch looks like a hack and introduces too much >>>>>> complexity for this feature. While I understand the reasoning, >>>>>> the proposed implementation cannot be accepted. >>>>> >>>>> Could you clarify that whether is this feature not accepted or this patch? >>>>> >>>>> If this feature is not needed, I will terminate this thread. >>>>> >>>>> If this patch only looks like a hack, would you like offer any advice to write >>>>> code with good smell? >>>> >>>> We've previously discussed this with Valentin, and our position is >>>> as follows: >>>> >>>> - The feature itself (autodetection between HTTP/2 and HTTP/1.x >>>> protocols) might be usable, and we can consider adding it if >>>> there will be a good and simple enough patch. (Moreover, we >>>> think that this probably should be the default if "listen ... >>>> http2" is configured - that is, no "http1" option.) >>>> >>>> - The patch suggested certainly doesn't meet the above criteria, >>>> and it does not look like it can be fixed. >>>> >>>> We don't know if a good and simple enough implementation is at all >>>> possible though. One of the possible approaches was already >>>> proposed by Valentin (detect HTTP/2 or HTTP/1.x before starting >>>> processing, may be similar to how we handle http-to-https >>>> requests), but it's now immediately clear if it will work or not. >>>> Sorry, but please don't expect any of us to provide further >>>> guidance. >>>> >> >> >> >> >> _______________________________________________ >> 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 mdounin at mdounin.ru Sun Mar 18 20:30:15 2018 From: mdounin at mdounin.ru (Maxim Dounin) Date: Sun, 18 Mar 2018 20:30:15 +0000 Subject: [nginx] Contrib: vim syntax, update core and 3rd party module directives. Message-ID: details: http://hg.nginx.org/nginx/rev/169d0eb76de7 branches: changeset: 7236:169d0eb76de7 user: Gena Makhomed date: Sun Mar 18 11:11:14 2018 +0200 description: Contrib: vim syntax, update core and 3rd party module directives. diffstat: contrib/vim/syntax/nginx.vim | 58 +++++++++++++++++++++++++++++++++---------- 1 files changed, 44 insertions(+), 14 deletions(-) diffs (121 lines): diff --git a/contrib/vim/syntax/nginx.vim b/contrib/vim/syntax/nginx.vim --- a/contrib/vim/syntax/nginx.vim +++ b/contrib/vim/syntax/nginx.vim @@ -241,6 +241,32 @@ syn keyword ngxDirective contained geoip syn keyword ngxDirective contained geoip_proxy syn keyword ngxDirective contained geoip_proxy_recursive syn keyword ngxDirective contained google_perftools_profiles +syn keyword ngxDirective contained grpc_bind +syn keyword ngxDirective contained grpc_buffer_size +syn keyword ngxDirective contained grpc_connect_timeout +syn keyword ngxDirective contained grpc_hide_header +syn keyword ngxDirective contained grpc_ignore_headers +syn keyword ngxDirective contained grpc_intercept_errors +syn keyword ngxDirective contained grpc_next_upstream +syn keyword ngxDirective contained grpc_next_upstream_timeout +syn keyword ngxDirective contained grpc_next_upstream_tries +syn keyword ngxDirective contained grpc_pass +syn keyword ngxDirective contained grpc_pass_header +syn keyword ngxDirective contained grpc_read_timeout +syn keyword ngxDirective contained grpc_send_timeout +syn keyword ngxDirective contained grpc_set_header +syn keyword ngxDirective contained grpc_ssl_certificate +syn keyword ngxDirective contained grpc_ssl_certificate_key +syn keyword ngxDirective contained grpc_ssl_ciphers +syn keyword ngxDirective contained grpc_ssl_crl +syn keyword ngxDirective contained grpc_ssl_name +syn keyword ngxDirective contained grpc_ssl_password_file +syn keyword ngxDirective contained grpc_ssl_protocols +syn keyword ngxDirective contained grpc_ssl_server_name +syn keyword ngxDirective contained grpc_ssl_session_reuse +syn keyword ngxDirective contained grpc_ssl_trusted_certificate +syn keyword ngxDirective contained grpc_ssl_verify +syn keyword ngxDirective contained grpc_ssl_verify_depth syn keyword ngxDirective contained gunzip syn keyword ngxDirective contained gunzip_buffers syn keyword ngxDirective contained gzip @@ -268,11 +294,14 @@ syn keyword ngxDirective contained hls_m syn keyword ngxDirective contained http2_body_preread_size syn keyword ngxDirective contained http2_chunk_size syn keyword ngxDirective contained http2_idle_timeout +syn keyword ngxDirective contained http2_max_concurrent_pushes syn keyword ngxDirective contained http2_max_concurrent_streams syn keyword ngxDirective contained http2_max_field_size syn keyword ngxDirective contained http2_max_header_size syn keyword ngxDirective contained http2_max_requests syn keyword ngxDirective contained http2_pool_size +syn keyword ngxDirective contained http2_push +syn keyword ngxDirective contained http2_push_preload syn keyword ngxDirective contained http2_recv_buffer_size syn keyword ngxDirective contained http2_recv_timeout syn keyword ngxDirective contained http2_streams_index_size @@ -574,6 +603,7 @@ syn keyword ngxDirective contained sub_f syn keyword ngxDirective contained sub_filter_last_modified syn keyword ngxDirective contained sub_filter_once syn keyword ngxDirective contained sub_filter_types +syn keyword ngxDirective contained subrequest_output_buffer_size syn keyword ngxDirective contained tcp_nodelay syn keyword ngxDirective contained tcp_nopush syn keyword ngxDirective contained thread_pool @@ -1323,11 +1353,8 @@ syn keyword ngxDirectiveThirdParty conta " Phusion Passenger " https://www.phusionpassenger.com/library/config/nginx/reference/ -syn keyword ngxDirectiveThirdParty contained disable_security_update_check syn keyword ngxDirectiveThirdParty contained passenger_abort_on_startup_error syn keyword ngxDirectiveThirdParty contained passenger_abort_websockets_on_process_shutdown -syn keyword ngxDirectiveThirdParty contained passenger_analytics_log_group -syn keyword ngxDirectiveThirdParty contained passenger_analytics_log_user syn keyword ngxDirectiveThirdParty contained passenger_app_env syn keyword ngxDirectiveThirdParty contained passenger_app_file_descriptor_ulimit syn keyword ngxDirectiveThirdParty contained passenger_app_group_name @@ -1369,6 +1396,7 @@ syn keyword ngxDirectiveThirdParty conta syn keyword ngxDirectiveThirdParty contained passenger_max_pool_size syn keyword ngxDirectiveThirdParty contained passenger_max_preloader_idle_time syn keyword ngxDirectiveThirdParty contained passenger_max_request_queue_size +syn keyword ngxDirectiveThirdParty contained passenger_max_request_queue_time syn keyword ngxDirectiveThirdParty contained passenger_max_request_time syn keyword ngxDirectiveThirdParty contained passenger_max_requests syn keyword ngxDirectiveThirdParty contained passenger_memory_limit @@ -1402,21 +1430,22 @@ syn keyword ngxDirectiveThirdParty conta syn keyword ngxDirectiveThirdParty contained passenger_user syn keyword ngxDirectiveThirdParty contained passenger_user_switching syn keyword ngxDirectiveThirdParty contained passenger_vary_turbocache_by_cookie -syn keyword ngxDirectiveThirdParty contained rack_env -syn keyword ngxDirectiveThirdParty contained rails_app_spawner_idle_time -syn keyword ngxDirectiveThirdParty contained rails_env -syn keyword ngxDirectiveThirdParty contained security_update_check_proxy -syn keyword ngxDirectiveThirdParty contained union_station_filter -syn keyword ngxDirectiveThirdParty contained union_station_gateway_address -syn keyword ngxDirectiveThirdParty contained union_station_gateway_cert -syn keyword ngxDirectiveThirdParty contained union_station_gateway_port -syn keyword ngxDirectiveThirdParty contained union_station_key -syn keyword ngxDirectiveThirdParty contained union_station_proxy_address -syn keyword ngxDirectiveThirdParty contained union_station_support +syn keyword ngxDirectiveThirdPartyDeprecated contained passenger_analytics_log_group +syn keyword ngxDirectiveThirdPartyDeprecated contained passenger_analytics_log_user syn keyword ngxDirectiveThirdPartyDeprecated contained passenger_debug_log_file syn keyword ngxDirectiveThirdPartyDeprecated contained passenger_use_global_queue +syn keyword ngxDirectiveThirdPartyDeprecated contained rack_env +syn keyword ngxDirectiveThirdPartyDeprecated contained rails_app_spawner_idle_time +syn keyword ngxDirectiveThirdPartyDeprecated contained rails_env syn keyword ngxDirectiveThirdPartyDeprecated contained rails_framework_spawner_idle_time syn keyword ngxDirectiveThirdPartyDeprecated contained rails_spawn_method +syn keyword ngxDirectiveThirdPartyDeprecated contained union_station_filter +syn keyword ngxDirectiveThirdPartyDeprecated contained union_station_gateway_address +syn keyword ngxDirectiveThirdPartyDeprecated contained union_station_gateway_cert +syn keyword ngxDirectiveThirdPartyDeprecated contained union_station_gateway_port +syn keyword ngxDirectiveThirdPartyDeprecated contained union_station_key +syn keyword ngxDirectiveThirdPartyDeprecated contained union_station_proxy_address +syn keyword ngxDirectiveThirdPartyDeprecated contained union_station_support " ngx_postgres is an upstream module that allows nginx to communicate directly with PostgreSQL database " https://github.com/FRiCKLE/ngx_postgres @@ -2028,6 +2057,7 @@ syn keyword ngxDirectiveThirdParty conta syn keyword ngxDirectiveThirdParty contained selective_cache_purge_query syn keyword ngxDirectiveThirdParty contained selective_cache_purge_redis_database syn keyword ngxDirectiveThirdParty contained selective_cache_purge_redis_host +syn keyword ngxDirectiveThirdParty contained selective_cache_purge_redis_password syn keyword ngxDirectiveThirdParty contained selective_cache_purge_redis_port syn keyword ngxDirectiveThirdParty contained selective_cache_purge_redis_unix_socket From mdounin at mdounin.ru Sun Mar 18 20:30:38 2018 From: mdounin at mdounin.ru (Maxim Dounin) Date: Sun, 18 Mar 2018 23:30:38 +0300 Subject: [PATCH] Contrib: vim syntax, update core and 3rd party module directives. In-Reply-To: <0e98087e-b4e3-620b-47d9-fcb6423097a9@csdoc.com> References: <0e98087e-b4e3-620b-47d9-fcb6423097a9@csdoc.com> Message-ID: <20180318203038.GH77253@mdounin.ru> Hello! On Sun, Mar 18, 2018 at 11:14:50AM +0200, Gena Makhomed wrote: > # HG changeset patch > # User Gena Makhomed > # Date 1521364274 -7200 > # Sun Mar 18 11:11:14 2018 +0200 > # Node ID 169d0eb76de7ffe2b2ea3005e4268542b5fc31a5 > # Parent c2a0a838c40f791efc08567c791e58c894aa5c17 > Contrib: vim syntax, update core and 3rd party module directives. [...] Committed, thnx. -- Maxim Dounin http://mdounin.ru/ From pluknet at nginx.com Mon Mar 19 10:44:23 2018 From: pluknet at nginx.com (Sergey Kandaurov) Date: Mon, 19 Mar 2018 10:44:23 +0000 Subject: [nginx] Configure: added gRPC module help message. Message-ID: details: http://hg.nginx.org/nginx/rev/d87393919a10 branches: changeset: 7237:d87393919a10 user: Sergey Kandaurov date: Mon Mar 19 12:41:36 2018 +0300 description: Configure: added gRPC module help message. diffstat: auto/options | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diffs (11 lines): diff -r 169d0eb76de7 -r d87393919a10 auto/options --- a/auto/options Sun Mar 18 11:11:14 2018 +0200 +++ b/auto/options Mon Mar 19 12:41:36 2018 +0300 @@ -473,6 +473,7 @@ cat << END --without-http_fastcgi_module disable ngx_http_fastcgi_module --without-http_uwsgi_module disable ngx_http_uwsgi_module --without-http_scgi_module disable ngx_http_scgi_module + --without-http_grpc_module disable ngx_http_grpc_module --without-http_memcached_module disable ngx_http_memcached_module --without-http_limit_conn_module disable ngx_http_limit_conn_module --without-http_limit_req_module disable ngx_http_limit_req_module From ru at nginx.com Mon Mar 19 13:28:58 2018 From: ru at nginx.com (Ruslan Ermilov) Date: Mon, 19 Mar 2018 13:28:58 +0000 Subject: [nginx] Upstream: fixed comments after 13f8dec720b5. Message-ID: details: http://hg.nginx.org/nginx/rev/06cf0c4b8618 branches: changeset: 7238:06cf0c4b8618 user: Ruslan Ermilov date: Mon Mar 19 16:22:09 2018 +0300 description: Upstream: fixed comments after 13f8dec720b5. The fields "uri", "location", and "url" from ngx_http_upstream_conf_t moved to ngx_http_proxy_loc_conf_t and ngx_http_proxy_vars_t, reflect this change in create_loc_conf comments. diffstat: src/http/modules/ngx_http_fastcgi_module.c | 2 -- src/http/modules/ngx_http_memcached_module.c | 2 -- src/http/modules/ngx_http_proxy_module.c | 4 ++-- 3 files changed, 2 insertions(+), 6 deletions(-) diffs (43 lines): diff -r d87393919a10 -r 06cf0c4b8618 src/http/modules/ngx_http_fastcgi_module.c --- a/src/http/modules/ngx_http_fastcgi_module.c Mon Mar 19 12:41:36 2018 +0300 +++ b/src/http/modules/ngx_http_fastcgi_module.c Mon Mar 19 16:22:09 2018 +0300 @@ -2706,8 +2706,6 @@ ngx_http_fastcgi_create_loc_conf(ngx_con * conf->upstream.cache_methods = 0; * conf->upstream.temp_path = NULL; * conf->upstream.hide_headers_hash = { NULL, 0 }; - * conf->upstream.uri = { 0, NULL }; - * conf->upstream.location = NULL; * conf->upstream.store_lengths = NULL; * conf->upstream.store_values = NULL; * diff -r d87393919a10 -r 06cf0c4b8618 src/http/modules/ngx_http_memcached_module.c --- a/src/http/modules/ngx_http_memcached_module.c Mon Mar 19 12:41:36 2018 +0300 +++ b/src/http/modules/ngx_http_memcached_module.c Mon Mar 19 16:22:09 2018 +0300 @@ -592,8 +592,6 @@ ngx_http_memcached_create_loc_conf(ngx_c * conf->upstream.bufs.num = 0; * conf->upstream.next_upstream = 0; * conf->upstream.temp_path = NULL; - * conf->upstream.uri = { 0, NULL }; - * conf->upstream.location = NULL; */ conf->upstream.local = NGX_CONF_UNSET_PTR; diff -r d87393919a10 -r 06cf0c4b8618 src/http/modules/ngx_http_proxy_module.c --- a/src/http/modules/ngx_http_proxy_module.c Mon Mar 19 12:41:36 2018 +0300 +++ b/src/http/modules/ngx_http_proxy_module.c Mon Mar 19 16:22:09 2018 +0300 @@ -2797,13 +2797,13 @@ ngx_http_proxy_create_loc_conf(ngx_conf_ * conf->upstream.cache_methods = 0; * conf->upstream.temp_path = NULL; * conf->upstream.hide_headers_hash = { NULL, 0 }; - * conf->upstream.uri = { 0, NULL }; - * conf->upstream.location = NULL; * conf->upstream.store_lengths = NULL; * conf->upstream.store_values = NULL; * conf->upstream.ssl_name = NULL; * * conf->method = NULL; + * conf->location = NULL; + * conf->url = { 0, NULL }; * conf->headers_source = NULL; * conf->headers.lengths = NULL; * conf->headers.values = NULL; From ru at nginx.com Mon Mar 19 13:28:59 2018 From: ru at nginx.com (Ruslan Ermilov) Date: Mon, 19 Mar 2018 13:28:59 +0000 Subject: [nginx] Fixed checking ngx_tcp_push() and ngx_tcp_nopush() return values. Message-ID: details: http://hg.nginx.org/nginx/rev/400a3412b1e3 branches: changeset: 7239:400a3412b1e3 user: Ruslan Ermilov date: Mon Mar 19 16:28:23 2018 +0300 description: Fixed checking ngx_tcp_push() and ngx_tcp_nopush() return values. No functional changes. diffstat: src/http/ngx_http_upstream.c | 2 +- src/os/unix/ngx_freebsd_sendfile_chain.c | 2 +- src/os/unix/ngx_linux_sendfile_chain.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diffs (36 lines): diff -r 06cf0c4b8618 -r 400a3412b1e3 src/http/ngx_http_upstream.c --- a/src/http/ngx_http_upstream.c Mon Mar 19 16:22:09 2018 +0300 +++ b/src/http/ngx_http_upstream.c Mon Mar 19 16:28:23 2018 +0300 @@ -2020,7 +2020,7 @@ ngx_http_upstream_send_request(ngx_http_ } if (c->tcp_nopush == NGX_TCP_NOPUSH_SET) { - if (ngx_tcp_push(c->fd) == NGX_ERROR) { + if (ngx_tcp_push(c->fd) == -1) { ngx_log_error(NGX_LOG_CRIT, c->log, ngx_socket_errno, ngx_tcp_push_n " failed"); ngx_http_upstream_finalize_request(r, u, diff -r 06cf0c4b8618 -r 400a3412b1e3 src/os/unix/ngx_freebsd_sendfile_chain.c --- a/src/os/unix/ngx_freebsd_sendfile_chain.c Mon Mar 19 16:22:09 2018 +0300 +++ b/src/os/unix/ngx_freebsd_sendfile_chain.c Mon Mar 19 16:28:23 2018 +0300 @@ -135,7 +135,7 @@ ngx_freebsd_sendfile_chain(ngx_connectio if (ngx_freebsd_use_tcp_nopush && c->tcp_nopush == NGX_TCP_NOPUSH_UNSET) { - if (ngx_tcp_nopush(c->fd) == NGX_ERROR) { + if (ngx_tcp_nopush(c->fd) == -1) { err = ngx_socket_errno; /* diff -r 06cf0c4b8618 -r 400a3412b1e3 src/os/unix/ngx_linux_sendfile_chain.c --- a/src/os/unix/ngx_linux_sendfile_chain.c Mon Mar 19 16:22:09 2018 +0300 +++ b/src/os/unix/ngx_linux_sendfile_chain.c Mon Mar 19 16:28:23 2018 +0300 @@ -130,7 +130,7 @@ ngx_linux_sendfile_chain(ngx_connection_ if (c->tcp_nodelay == NGX_TCP_NODELAY_UNSET) { - if (ngx_tcp_nopush(c->fd) == NGX_ERROR) { + if (ngx_tcp_nopush(c->fd) == -1) { err = ngx_socket_errno; /* From pluknet at nginx.com Mon Mar 19 13:45:15 2018 From: pluknet at nginx.com (Sergey Kandaurov) Date: Mon, 19 Mar 2018 13:45:15 +0000 Subject: [nginx] gRPC: fixed parsing response headers split on CONTINUATION frames. Message-ID: details: http://hg.nginx.org/nginx/rev/413189f03c8d branches: changeset: 7240:413189f03c8d user: Sergey Kandaurov date: Mon Mar 19 16:42:56 2018 +0300 description: gRPC: fixed parsing response headers split on CONTINUATION frames. diffstat: src/http/modules/ngx_http_grpc_module.c | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diffs (21 lines): diff -r 400a3412b1e3 -r 413189f03c8d src/http/modules/ngx_http_grpc_module.c --- a/src/http/modules/ngx_http_grpc_module.c Mon Mar 19 16:28:23 2018 +0300 +++ b/src/http/modules/ngx_http_grpc_module.c Mon Mar 19 16:42:56 2018 +0300 @@ -2953,7 +2953,7 @@ ngx_http_grpc_parse_fragment(ngx_http_re ctx->name.data[ctx->name.len] = '\0'; } else { - ngx_memcpy(ctx->field_end, p, size); + ctx->field_end = ngx_cpymem(ctx->field_end, p, size); ctx->name.data[ctx->name.len] = '\0'; } @@ -3062,7 +3062,7 @@ ngx_http_grpc_parse_fragment(ngx_http_re ctx->value.data[ctx->value.len] = '\0'; } else { - ngx_memcpy(ctx->field_end, p, size); + ctx->field_end = ngx_cpymem(ctx->field_end, p, size); ctx->value.data[ctx->value.len] = '\0'; } From abiliojr at gmail.com Mon Mar 19 15:36:49 2018 From: abiliojr at gmail.com (Abilio Marques) Date: Mon, 19 Mar 2018 16:36:49 +0100 Subject: Weird HTTPS error Message-ID: Hi, I initially posted this error on the NGINX mailing list, but it looks to me it has more to do with the developers. The initial message is here: http://mailman.nginx.org/pipermail/nginx/2018-March/055808.html To make a long story short, I'm getting this message from NGINX 1.12.2: [info] 1343#0: *55 SSL_read() failed (SSL: error:1408F119:lib(20):func(143):reason(281)) while waiting for request, client: 192.168.1.250, server: 0.0.0.0:443 Even when printed as info, this is very visible on the client side, as the connection is severed before the data gets transferred. There are 3 ways to make the problem "disappear", all discovered by accident: - Disable session resumption. - Regenerate the key/certificate a number of times, until you get lucky. - LD_PRELOAD even an "empty" (as in no C code) so library. None of them are solutions, but just make the real problem hide. This is happening on a MIPS. I'm just reposting here because I think developers are the ones that might know where to start looking. I'm just reposting here because I think developers are the ones that might know where to start looking. Any ideas on how to proceed? -------------- next part -------------- An HTML attachment was scrubbed... URL: From mdounin at mdounin.ru Mon Mar 19 16:12:02 2018 From: mdounin at mdounin.ru (Maxim Dounin) Date: Mon, 19 Mar 2018 19:12:02 +0300 Subject: Weird HTTPS error In-Reply-To: References: Message-ID: <20180319161202.GR77253@mdounin.ru> Hello! On Mon, Mar 19, 2018 at 04:36:49PM +0100, Abilio Marques wrote: > I initially posted this error on the NGINX mailing list, but it looks to me > it has more to do with the developers. > > The initial message is here: > http://mailman.nginx.org/pipermail/nginx/2018-March/055808.html > > To make a long story short, I'm getting this message from NGINX 1.12.2: > > [info] 1343#0: *55 SSL_read() failed (SSL: > error:1408F119:lib(20):func(143):reason(281)) while waiting for request, > client: 192.168.1.250, server: 0.0.0.0:443 > > > > Even when printed as info, this is very visible on the client side, as the > connection is severed before the data gets transferred. > > There are 3 ways to make the problem "disappear", all discovered by > accident: > - Disable session resumption. > - Regenerate the key/certificate a number of times, until you get lucky. > - LD_PRELOAD even an "empty" (as in no C code) so library. > > None of them are solutions, but just make the real problem hide. > > This is happening on a MIPS. I'm just reposting here because I think > developers are the ones that might know where to start looking. I'm just > reposting here because I think developers are the ones that might know > where to start looking. > > Any ideas on how to proceed? http://mailman.nginx.org/pipermail/nginx/2018-March/055829.html http://mailman.nginx.org/pipermail/nginx/2018-March/055896.html -- Maxim Dounin http://mdounin.ru/ From ru at nginx.com Mon Mar 19 18:33:16 2018 From: ru at nginx.com (Ruslan Ermilov) Date: Mon, 19 Mar 2018 18:33:16 +0000 Subject: [nginx] HTTP/2: improved frame info debugging. Message-ID: details: http://hg.nginx.org/nginx/rev/190591ab0d76 branches: changeset: 7241:190591ab0d76 user: Ruslan Ermilov date: Mon Mar 19 21:32:15 2018 +0300 description: HTTP/2: improved frame info debugging. diffstat: src/http/v2/ngx_http_v2.c | 7 +++++-- src/http/v2/ngx_http_v2_filter_module.c | 6 +++--- 2 files changed, 8 insertions(+), 5 deletions(-) diffs (40 lines): diff -r 413189f03c8d -r 190591ab0d76 src/http/v2/ngx_http_v2.c --- a/src/http/v2/ngx_http_v2.c Mon Mar 19 16:42:56 2018 +0300 +++ b/src/http/v2/ngx_http_v2.c Mon Mar 19 21:32:15 2018 +0300 @@ -1990,6 +1990,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"); + return ngx_http_v2_state_settings_params(h2c, pos, end); } @@ -2130,8 +2133,8 @@ ngx_http_v2_state_ping(ngx_http_v2_conne return ngx_http_v2_state_save(h2c, pos, end, ngx_http_v2_state_ping); } - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, - "http2 PING frame, flags: %ud", h2c->state.flags); + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 PING frame"); if (h2c->state.flags & NGX_HTTP_V2_ACK_FLAG) { return ngx_http_v2_state_skip(h2c, pos, end); diff -r 413189f03c8d -r 190591ab0d76 src/http/v2/ngx_http_v2_filter_module.c --- a/src/http/v2/ngx_http_v2_filter_module.c Mon Mar 19 16:42:56 2018 +0300 +++ b/src/http/v2/ngx_http_v2_filter_module.c Mon Mar 19 21:32:15 2018 +0300 @@ -1169,9 +1169,9 @@ 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, - "http2:%ui create HEADERS frame %p: len:%uz", - stream->node->id, frame, frame->length); + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http2:%ui create HEADERS frame %p: len:%uz fin:%ui", + stream->node->id, frame, frame->length, fin); return frame; } From lucas.vacek at gmail.com Mon Mar 19 19:53:46 2018 From: lucas.vacek at gmail.com (Lukas Vacek) Date: Mon, 19 Mar 2018 20:53:46 +0100 Subject: [PATCH] Support X-Forwarded-Proto in redirects Message-ID: <0f9ed8bb5c9505f7a772.1521489226@mojito> # HG changeset patch # User Lukas Vacek # Date 1521488415 -3600 # Mon Mar 19 20:40:15 2018 +0100 # Node ID 0f9ed8bb5c9505f7a77271eed54e7b01b9b65a81 # Parent 413189f03c8d13d0d20bd5e44fa0e48e693badef Support X-Forwarded-Proto in redirects This patch adds a new configuration option x_forwarded_proto_in_redirect (default off) to fill schema from X-Forwarded-Proto HTTP header in URLs generated by nginx. This is handy when running nginx behind SSL off-loading reverse proxy. diff -r 413189f03c8d -r 0f9ed8bb5c95 src/http/ngx_http_core_module.c --- a/src/http/ngx_http_core_module.c Mon Mar 19 16:42:56 2018 +0300 +++ b/src/http/ngx_http_core_module.c Mon Mar 19 20:40:15 2018 +0100 @@ -576,6 +576,13 @@ offsetof(ngx_http_core_loc_conf_t, port_in_redirect), NULL }, + { ngx_string("x_forwarded_proto_in_redirect"), + 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_core_loc_conf_t, x_forwarded_proto_in_redirect), + NULL }, + { ngx_string("msie_padding"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, ngx_conf_set_flag_slot, @@ -3401,6 +3408,7 @@ clcf->absolute_redirect = NGX_CONF_UNSET; clcf->server_name_in_redirect = NGX_CONF_UNSET; clcf->port_in_redirect = NGX_CONF_UNSET; + clcf->x_forwarded_proto_in_redirect = NGX_CONF_UNSET; clcf->msie_padding = NGX_CONF_UNSET; clcf->msie_refresh = NGX_CONF_UNSET; clcf->log_not_found = NGX_CONF_UNSET; @@ -3669,6 +3677,8 @@ ngx_conf_merge_value(conf->server_name_in_redirect, prev->server_name_in_redirect, 0); ngx_conf_merge_value(conf->port_in_redirect, prev->port_in_redirect, 1); + ngx_conf_merge_value(conf->x_forwarded_proto_in_redirect, + prev->x_forwarded_proto_in_redirect, 0); ngx_conf_merge_value(conf->msie_padding, prev->msie_padding, 1); ngx_conf_merge_value(conf->msie_refresh, prev->msie_refresh, 0); ngx_conf_merge_value(conf->log_not_found, prev->log_not_found, 1); diff -r 413189f03c8d -r 0f9ed8bb5c95 src/http/ngx_http_core_module.h --- a/src/http/ngx_http_core_module.h Mon Mar 19 16:42:56 2018 +0300 +++ b/src/http/ngx_http_core_module.h Mon Mar 19 20:40:15 2018 +0100 @@ -385,6 +385,8 @@ ngx_flag_t absolute_redirect; /* absolute_redirect */ ngx_flag_t server_name_in_redirect; /* server_name_in_redirect */ ngx_flag_t port_in_redirect; /* port_in_redirect */ + ngx_flag_t x_forwarded_proto_in_redirect; + /* x_forwarded_proto_in_redirect */ ngx_flag_t msie_padding; /* msie_padding */ ngx_flag_t msie_refresh; /* msie_refresh */ ngx_flag_t log_not_found; /* log_not_found */ diff -r 413189f03c8d -r 0f9ed8bb5c95 src/http/ngx_http_header_filter_module.c --- a/src/http/ngx_http_header_filter_module.c Mon Mar 19 16:42:56 2018 +0300 +++ b/src/http/ngx_http_header_filter_module.c Mon Mar 19 20:40:15 2018 +0100 @@ -158,7 +158,7 @@ { u_char *p; size_t len; - ngx_str_t host, *status_line; + ngx_str_t proto, host, *status_line; ngx_buf_t *b; ngx_uint_t status, i, port; ngx_chain_t out; @@ -343,12 +343,44 @@ } } - port = ngx_inet_get_port(c->local_sockaddr); + proto.data = (u_char *)"http"; + proto.len = 4; - len += sizeof("Location: https://") - 1 +#if (NGX_HTTP_SSL) + if (c->ssl) { + proto.data = (u_char *)"https"; + proto.len = 5; + } +#endif + if (clcf->x_forwarded_proto_in_redirect) { + 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_strcasecmp(header[i].key.data, + (u_char *)"X-Forwarded-Proto") == 0) { + proto = header[i].value; + break; + } + } + } + + len += sizeof("Location: ") - 1 + proto.len + sizeof("://") - 1 + host.len + r->headers_out.location->value.len + 2; + port = ngx_inet_get_port(c->local_sockaddr); + if (clcf->port_in_redirect) { #if (NGX_HTTP_SSL) @@ -368,6 +400,7 @@ } else { ngx_str_null(&host); + ngx_str_null(&proto); port = 0; } @@ -518,14 +551,9 @@ p = b->last + sizeof("Location: ") - 1; - b->last = ngx_cpymem(b->last, "Location: http", - sizeof("Location: http") - 1); - -#if (NGX_HTTP_SSL) - if (c->ssl) { - *b->last++ ='s'; - } -#endif + b->last = ngx_cpymem(b->last, "Location: ", sizeof("Location: ") - 1); + + b->last = ngx_copy(b->last, proto.data, proto.len); *b->last++ = ':'; *b->last++ = '/'; *b->last++ = '/'; b->last = ngx_copy(b->last, host.data, host.len); diff -r 413189f03c8d -r 0f9ed8bb5c95 src/http/v2/ngx_http_v2_filter_module.c --- a/src/http/v2/ngx_http_v2_filter_module.c Mon Mar 19 16:42:56 2018 +0300 +++ b/src/http/v2/ngx_http_v2_filter_module.c Mon Mar 19 20:40:15 2018 +0100 @@ -319,11 +319,45 @@ } } + ngx_str_t proto; + proto.data = (u_char* )"http"; + proto.len = 4; + +#if (NGX_HTTP_SSL) + if (fc->ssl) { + proto.data = (u_char* )"https"; + proto.len = 5; + } +#endif + + if (clcf->x_forwarded_proto_in_redirect) { + 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_strcasecmp(header[i].key.data, + (u_char *)"X-Forwarded-Proto") == 0) { + proto = header[i].value; + break; + } + } + } + + location.len = proto.len + sizeof("://") - 1 + host.len + + + r->headers_out.location->value.len; + port = ngx_inet_get_port(fc->local_sockaddr); - location.len = sizeof("https://") - 1 + host.len - + r->headers_out.location->value.len; - if (clcf->port_in_redirect) { #if (NGX_HTTP_SSL) @@ -346,13 +380,7 @@ return NGX_ERROR; } - p = ngx_cpymem(location.data, "http", sizeof("http") - 1); - -#if (NGX_HTTP_SSL) - if (fc->ssl) { - *p++ = 's'; - } -#endif + p = ngx_cpymem(location.data, proto.data, proto.len); *p++ = ':'; *p++ = '/'; *p++ = '/'; p = ngx_cpymem(p, host.data, host.len); From mdounin at mdounin.ru Mon Mar 19 20:09:04 2018 From: mdounin at mdounin.ru (Maxim Dounin) Date: Mon, 19 Mar 2018 23:09:04 +0300 Subject: [PATCH] Support X-Forwarded-Proto in redirects In-Reply-To: <0f9ed8bb5c9505f7a772.1521489226@mojito> References: <0f9ed8bb5c9505f7a772.1521489226@mojito> Message-ID: <20180319200904.GV77253@mdounin.ru> Hello! On Mon, Mar 19, 2018 at 08:53:46PM +0100, Lukas Vacek wrote: > # HG changeset patch > # User Lukas Vacek > # Date 1521488415 -3600 > # Mon Mar 19 20:40:15 2018 +0100 > # Node ID 0f9ed8bb5c9505f7a77271eed54e7b01b9b65a81 > # Parent 413189f03c8d13d0d20bd5e44fa0e48e693badef > Support X-Forwarded-Proto in redirects > > This patch adds a new configuration option x_forwarded_proto_in_redirect > (default off) to fill schema from X-Forwarded-Proto HTTP header in > URLs generated by nginx. > > This is handy when running nginx behind SSL off-loading reverse proxy. Try "absolute_redirect off" instead, see http://nginx.org/r/absolute_redirect for details. [...] > + if (clcf->x_forwarded_proto_in_redirect) { > + 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_strcasecmp(header[i].key.data, > + (u_char *)"X-Forwarded-Proto") == 0) { > + proto = header[i].value; > + break; > + } > + } > + } Just a side note: full scan of all headers is certainly not something that should be used for such tasks. Instead, a field for a header should be added to r->headers_in, and an entry should be added to the ngx_http_headers_in array. With such approach it is possible to test / access interesting headers immediately in the code, with minimal overhead. -- Maxim Dounin http://mdounin.ru/ From gmm at csdoc.com Tue Mar 20 10:52:31 2018 From: gmm at csdoc.com (Gena Makhomed) Date: Tue, 20 Mar 2018 12:52:31 +0200 Subject: nginx undocumented directives In-Reply-To: References: <418fda51-227c-221f-804f-aa7e88322a53@csdoc.com> <20171228122533.GC15734@lo0.su> <20171228131735.GJ34136@mdounin.ru> Message-ID: <7897f302-90e2-1cec-3b2b-7ec0ec471bd0@csdoc.com> On 13.02.2018 13:02, Yaroslav Zhuravlev wrote: > Documentation added for smtp_client_buffer and smtp_greeting_delay Ok, thank you. But directive subrequest_output_buffer_size is still undocumented. It is added by http://hg.nginx.org/nginx/rev/20f139e9ffa8#l4.7 It is undocumented by mistake (and will be documented in near future) or it is undocumented by purpose and it will be never documented ? -- Best regards, Gena From maxim at nginx.com Tue Mar 20 10:53:59 2018 From: maxim at nginx.com (Maxim Konovalov) Date: Tue, 20 Mar 2018 13:53:59 +0300 Subject: nginx undocumented directives In-Reply-To: <7897f302-90e2-1cec-3b2b-7ec0ec471bd0@csdoc.com> References: <418fda51-227c-221f-804f-aa7e88322a53@csdoc.com> <20171228122533.GC15734@lo0.su> <20171228131735.GJ34136@mdounin.ru> <7897f302-90e2-1cec-3b2b-7ec0ec471bd0@csdoc.com> Message-ID: On 20/03/2018 13:52, Gena Makhomed wrote: > On 13.02.2018 13:02, Yaroslav Zhuravlev wrote: > >> Documentation added for smtp_client_buffer and smtp_greeting_delay > > Ok, thank you. > > But directive subrequest_output_buffer_size is still undocumented. > > It is added by http://hg.nginx.org/nginx/rev/20f139e9ffa8#l4.7 > > It is undocumented by mistake (and will be documented in near future) > or it is undocumented by purpose and it will be never documented ? > It is wip. -- Maxim Konovalov From pluknet at nginx.com Tue Mar 20 13:07:30 2018 From: pluknet at nginx.com (Sergey Kandaurov) Date: Tue, 20 Mar 2018 13:07:30 +0000 Subject: [nginx] gRPC: fixed missing state save in frame header parsing. Message-ID: details: http://hg.nginx.org/nginx/rev/25a4353633a0 branches: changeset: 7242:25a4353633a0 user: Sergey Kandaurov date: Tue Mar 20 15:58:11 2018 +0300 description: gRPC: fixed missing state save in frame header parsing. Previously, frame state wasn't saved if HEADERS frame payload that begins with header fragment was not received at once. diffstat: src/http/modules/ngx_http_grpc_module.c | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diffs (11 lines): diff -r 190591ab0d76 -r 25a4353633a0 src/http/modules/ngx_http_grpc_module.c --- a/src/http/modules/ngx_http_grpc_module.c Mon Mar 19 21:32:15 2018 +0300 +++ b/src/http/modules/ngx_http_grpc_module.c Tue Mar 20 15:58:11 2018 +0300 @@ -2410,6 +2410,7 @@ ngx_http_grpc_parse_header(ngx_http_requ } ctx->padding = 0; + ctx->frame_state = state; } if (state < sw_fragment) { From mdounin at mdounin.ru Tue Mar 20 16:02:09 2018 From: mdounin at mdounin.ru (Maxim Dounin) Date: Tue, 20 Mar 2018 16:02:09 +0000 Subject: [nginx] nginx-1.13.10-RELEASE Message-ID: details: http://hg.nginx.org/nginx/rev/31c929e16910 branches: changeset: 7243:31c929e16910 user: Maxim Dounin date: Tue Mar 20 18:58:30 2018 +0300 description: nginx-1.13.10-RELEASE diffstat: docs/xml/nginx/changes.xml | 84 ++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 84 insertions(+), 0 deletions(-) diffs (94 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,90 @@ + + + + +?????? ???????? set ? SSI-????????? include +????????? ????????? ? ?????????? ????? ??????; +???????????? ?????? ?????? ???????? ?????????? subrequest_output_buffer_size. + + +the "set" parameter of the "include" SSI directive now allows +writing arbitrary responses to a variable; +the "subrequest_output_buffer_size" directive defines maximum response size. + + + + + +?????? nginx ?????????? ????? clock_gettime(CLOCK_MONOTONIC), ???? ?? ????????, +??? ????????? ???????? ????????????? ???????????? ????????? +??? ?????????? ?????????? ???????. + + +now nginx uses clock_gettime(CLOCK_MONOTONIC) if available, +to avoid timeouts being incorrectly triggered +on system time changes. + + + + + +???????? "escape=none" ????????? log_format.
+??????? Johannes Baiter ? Calin Don. +
+ +the "escape=none" parameter of the "log_format" directive.
+Thanks to Johannes Baiter and Calin Don. +
+
+ + + +?????????? $ssl_preread_alpn_protocols +? ?????? ngx_stream_ssl_preread_module. + + +the $ssl_preread_alpn_protocols variable +in the ngx_stream_ssl_preread_module. + + + + + +?????? ngx_http_grpc_module. + + +the ngx_http_grpc_module. + + + + + +? ????????? ?????? ????????? ?????? ? ????????? geo. + + +in memory allocation error handling in the "geo" directive. + + + + + +??? ????????????? ?????????? ? ????????? auth_basic_user_file +? ??? ??? ?????????? ?????? '\0'.
+??????? ?????? ??????????. +
+ +when using variables in the "auth_basic_user_file" directive +a null character might appear in logs.
+Thanks to Vadim Filimonov. +
+
+ +
+ + From mdounin at mdounin.ru Tue Mar 20 16:02:10 2018 From: mdounin at mdounin.ru (Maxim Dounin) Date: Tue, 20 Mar 2018 16:02:10 +0000 Subject: [nginx] release-1.13.10 tag Message-ID: details: http://hg.nginx.org/nginx/rev/164124b71818 branches: changeset: 7244:164124b71818 user: Maxim Dounin date: Tue Mar 20 18:58:30 2018 +0300 description: release-1.13.10 tag diffstat: .hgtags | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diffs (8 lines): diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -422,3 +422,4 @@ f87da7d9ca02b8ced4caa6c5eb9013ccd47b0117 47cca243d0ed39bf5dcb9859184affc958b79b6f release-1.13.7 20ca4bcff108d3e66977f4d97508637093492287 release-1.13.8 fb1212c7eca4c5328fe17d6cd95b010c67336aac release-1.13.9 +31c929e16910c38492581ef474e72fa67c28f124 release-1.13.10 From vbart at nginx.com Tue Mar 20 16:02:22 2018 From: vbart at nginx.com (Valentin V. Bartenev) Date: Tue, 20 Mar 2018 19:02:22 +0300 Subject: [PATCH] HTTP/2: make http2 server support http1 In-Reply-To: <9195D529-BCBA-4D20-8CB0-28683145D1E3@lvht.net> References: <20180305191459.GR89840@mdounin.ru> <9195D529-BCBA-4D20-8CB0-28683145D1E3@lvht.net> Message-ID: <1583421.Ry823iCY5n@vbart-workstation> On Thursday 08 March 2018 08:42:27 Haitao Lv wrote: > Sorry for disturbing. But I have to fix a buffer overflow bug. > Here is the latest patch. > > Sorry. But please make your comments. Thank you. [..] There's no way for this patch to be accepted as it breaks PROXY protocol functionality. wbr, Valentin V. Bartenev From i at lvht.net Wed Mar 21 03:36:08 2018 From: i at lvht.net (Haitao Lv) Date: Wed, 21 Mar 2018 11:36:08 +0800 Subject: [PATCH] HTTP/2: make http2 server support http1 In-Reply-To: <1583421.Ry823iCY5n@vbart-workstation> References: <20180305191459.GR89840@mdounin.ru> <9195D529-BCBA-4D20-8CB0-28683145D1E3@lvht.net> <1583421.Ry823iCY5n@vbart-workstation> Message-ID: Thank you for reviewing. And here is the patch that fix the breaking PROXY protocol functionality. Sorry for disturbing. diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 2db7a627..9f1b8544 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -17,6 +17,10 @@ static ssize_t ngx_http_read_request_header(ngx_http_request_t *r); static ngx_int_t ngx_http_alloc_large_header_buffer(ngx_http_request_t *r, ngx_uint_t request_line); +#if (NGX_HTTP_V2) +static void ngx_http_wait_v2_preface_handler(ngx_event_t *rev); +#endif + 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_unique_header_line(ngx_http_request_t *r, @@ -325,7 +329,7 @@ ngx_http_init_connection(ngx_connection_t *c) #if (NGX_HTTP_V2) if (hc->addr_conf->http2) { - rev->handler = ngx_http_v2_init; + rev->handler = ngx_http_wait_v2_preface_handler; } #endif @@ -381,6 +385,131 @@ ngx_http_init_connection(ngx_connection_t *c) } +#if (NGX_HTTP_V2) +static void +ngx_http_wait_v2_preface_handler(ngx_event_t *rev) +{ + size_t size; + ssize_t n; + u_char *p; + ngx_buf_t *b; + ngx_connection_t *c; + ngx_http_connection_t *hc; + static const u_char preface[] = "PRI"; + + c = rev->data; + hc = c->data; + + size = sizeof(preface) - 1; + + if (hc->proxy_protocol) { + size += NGX_PROXY_PROTOCOL_MAX_HEADER; + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http wait h2 preface handler"); + + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); + ngx_http_close_connection(c); + return; + } + + if (c->close) { + ngx_http_close_connection(c); + return; + } + + b = c->buffer; + + if (b == NULL) { + b = ngx_create_temp_buf(c->pool, size); + if (b == NULL) { + ngx_http_close_connection(c); + return; + } + + c->buffer = b; + + } else if (b->start == NULL) { + + b->start = ngx_palloc(c->pool, size); + if (b->start == NULL) { + ngx_http_close_connection(c); + return; + } + + b->pos = b->start; + b->last = b->start; + b->end = b->last + size; + } + + n = c->recv(c, b->last, b->end - b->last); + + if (n == NGX_AGAIN) { + + if (!rev->timer_set) { + ngx_add_timer(rev, c->listening->post_accept_timeout); + ngx_reusable_connection(c, 1); + } + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + ngx_http_close_connection(c); + return; + } + + /* + * We are trying to not hold c->buffer's memory for an idle connection. + */ + + if (ngx_pfree(c->pool, b->start) == NGX_OK) { + b->start = NULL; + } + + return; + } + + if (n == NGX_ERROR) { + ngx_http_close_connection(c); + return; + } + + if (n == 0) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client closed connection"); + ngx_http_close_connection(c); + return; + } + + b->last += n; + + if (hc->proxy_protocol) { + hc->proxy_protocol = 0; + + p = ngx_proxy_protocol_read(c, b->pos, b->last); + + if (p == NULL) { + ngx_http_close_connection(c); + return; + } + + b->pos = p; + } + + if (b->last >= b->pos + sizeof(preface) - 1) { + /* b will be freed in ngx_http_v2_init/ngx_http_wait_request_handler */ + + if (ngx_strncmp(b->pos, preface, sizeof(preface) - 1) == 0) { + ngx_http_v2_init(rev); + } else { + rev->handler = ngx_http_wait_request_handler; + ngx_http_wait_request_handler(rev); + } + } +} +#endif + + static void ngx_http_wait_request_handler(ngx_event_t *rev) { @@ -393,6 +522,7 @@ ngx_http_wait_request_handler(ngx_event_t *rev) ngx_http_core_srv_conf_t *cscf; c = rev->data; + n = NGX_AGAIN; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http wait request handler"); @@ -434,9 +564,27 @@ ngx_http_wait_request_handler(ngx_event_t *rev) b->pos = b->start; b->last = b->start; b->end = b->last + size; + } else { + + p = ngx_palloc(c->pool, size); + if (p == NULL) { + ngx_http_close_connection(c); + return; + } + + n = b->last - b->pos; + ngx_memcpy(p, b->pos, n); + ngx_pfree(c->pool, b->start); + + b->start = p; + b->pos = b->start; + b->last = b->start + n; + b->end = b->last + size; } - n = c->recv(c, b->last, size); + if (n == NGX_AGAIN) { + n = c->recv(c, b->last, size); + } if (n == NGX_AGAIN) { diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c index 77ebb847..6724b662 100644 --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c @@ -229,6 +229,8 @@ static ngx_http_v2_parse_header_t ngx_http_v2_parse_headers[] = { void ngx_http_v2_init(ngx_event_t *rev) { + size_t size; + ngx_buf_t *b; ngx_connection_t *c; ngx_pool_cleanup_t *cln; ngx_http_connection_t *hc; @@ -260,6 +262,23 @@ ngx_http_v2_init(ngx_event_t *rev) return; } + b = c->buffer; + + if (b != NULL) { + size = b->last - b->pos; + + if (size > h2mcf->recv_buffer_size) { + size = h2mcf->recv_buffer_size; + } + + ngx_memcpy(h2mcf->recv_buffer, b->pos, size); + h2c->state.buffer_used = size; + + ngx_pfree(c->pool, b->start); + ngx_pfree(c->pool, b); + c->buffer = NULL; + } + h2c->connection = c; h2c->http_connection = hc; @@ -379,13 +398,15 @@ ngx_http_v2_read_handler(ngx_event_t *rev) h2mcf = ngx_http_get_module_main_conf(h2c->http_connection->conf_ctx, ngx_http_v2_module); - available = h2mcf->recv_buffer_size - 2 * NGX_HTTP_V2_STATE_BUFFER_SIZE; + available = h2mcf->recv_buffer_size - h2c->state.buffer_used - 2 * NGX_HTTP_V2_STATE_BUFFER_SIZE; do { p = h2mcf->recv_buffer; - ngx_memcpy(p, h2c->state.buffer, NGX_HTTP_V2_STATE_BUFFER_SIZE); end = p + h2c->state.buffer_used; + if (h2c->state.buffer_used == 0) { + ngx_memcpy(p, h2c->state.buffer, NGX_HTTP_V2_STATE_BUFFER_SIZE); + } n = c->recv(c, end, available); > On Mar 21, 2018, at 00:02, Valentin V. Bartenev wrote: > > On Thursday 08 March 2018 08:42:27 Haitao Lv wrote: >> Sorry for disturbing. But I have to fix a buffer overflow bug. >> Here is the latest patch. >> >> Sorry. But please make your comments. Thank you. > [..] > > There's no way for this patch to be accepted as it breaks PROXY protocol > functionality. > > wbr, Valentin V. Bartenev > > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel From yar at nginx.com Wed Mar 21 09:08:24 2018 From: yar at nginx.com (Yaroslav Zhuravlev) Date: Wed, 21 Mar 2018 12:08:24 +0300 Subject: nginx undocumented directives In-Reply-To: References: <418fda51-227c-221f-804f-aa7e88322a53@csdoc.com> <20171228122533.GC15734@lo0.su> <20171228131735.GJ34136@mdounin.ru> <7897f302-90e2-1cec-3b2b-7ec0ec471bd0@csdoc.com> Message-ID: Hello! [?] >> But directive subrequest_output_buffer_size is still undocumented. >> >> It is added by http://hg.nginx.org/nginx/rev/20f139e9ffa8#l4.7 >> >> It is undocumented by mistake (and will be documented in near future) >> or it is undocumented by purpose and it will be never documented ? >> > It is wip. Documentation added for subrequest_output_buffer_size: http://nginx.org/en/docs/http/ngx_http_core_module.html#subrequest_output_buffer_size http://nginx.org/ru/docs/http/ngx_http_core_module.html#subrequest_output_buffer_size From xeioex at nginx.com Wed Mar 21 14:33:27 2018 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Wed, 21 Mar 2018 14:33:27 +0000 Subject: [njs] http req.response() method. Message-ID: details: http://hg.nginx.org/njs/rev/fa22235730b1 branches: changeset: 464:fa22235730b1 user: Dmitry Volyntsev date: Wed Mar 21 17:33:12 2018 +0300 description: http req.response() method. diffstat: nginx/ngx_http_js_module.c | 31 +++++++++++++++++++++++++++++++ 1 files changed, 31 insertions(+), 0 deletions(-) diffs (55 lines): diff -r 6d599ae5b35b -r fa22235730b1 nginx/ngx_http_js_module.c --- a/nginx/ngx_http_js_module.c Wed Mar 21 17:33:12 2018 +0300 +++ b/nginx/ngx_http_js_module.c Wed Mar 21 17:33:12 2018 +0300 @@ -105,6 +105,8 @@ static njs_ret_t ngx_http_js_ext_next_ar void *obj, void *next); static njs_ret_t ngx_http_js_ext_get_variable(njs_vm_t *vm, njs_value_t *value, void *obj, uintptr_t data); +static njs_ret_t ngx_http_js_ext_get_response(njs_vm_t *vm, njs_value_t *value, + void *obj, uintptr_t data); static njs_host_event_t ngx_http_js_set_timer(njs_external_ptr_t external, uint64_t delay, njs_vm_event_t vm_event); @@ -367,6 +369,18 @@ static njs_external_t ngx_http_js_ext_r NULL, NULL, 0 }, + + { nxt_string("response"), + NJS_EXTERN_PROPERTY, + NULL, + 0, + ngx_http_js_ext_get_response, + NULL, + NULL, + NULL, + NULL, + NULL, + 0 }, }; @@ -1293,6 +1307,23 @@ ngx_http_js_ext_get_variable(njs_vm_t *v } +static njs_ret_t +ngx_http_js_ext_get_response(njs_vm_t *vm, njs_value_t *value, void *obj, + uintptr_t data) +{ + ngx_http_js_ctx_t *ctx; + ngx_http_request_t *r; + + r = (ngx_http_request_t *) obj; + + ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); + + njs_vm_retval_set(ctx->vm, &ctx->args[1]); + + return NJS_OK; +} + + static njs_host_event_t ngx_http_js_set_timer(njs_external_ptr_t external, uint64_t delay, njs_vm_event_t vm_event) From xeioex at nginx.com Wed Mar 21 14:33:27 2018 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Wed, 21 Mar 2018 14:33:27 +0000 Subject: [njs] http subrequest() method. Message-ID: details: http://hg.nginx.org/njs/rev/750f7c6f071c branches: changeset: 465:750f7c6f071c user: Dmitry Volyntsev date: Wed Mar 21 17:33:13 2018 +0300 description: http subrequest() method. Creates an nginx's subrequest with the specified arguments and registers a finalization callback. req.subrequest([, [, ]]): uri - string. options - string | object. string value - uri arguments. object value can contain: args, body, method all are string values. callback - function with the following argument: reply - the result object with the following properties: uri, method, status, contentType, contentLength, headers, args, body, parent. diffstat: nginx/ngx_http_js_module.c | 558 +++++++++++++++++++++++++++++++++++++++++++- njs/njs_error.c | 16 +- njs/njs_error.h | 1 + njs/njs_vm.c | 92 +++++++- njs/njscript.c | 78 ++++++- njs/njscript.h | 19 +- njs/test/njs_unit_test.c | 25 ++ 7 files changed, 754 insertions(+), 35 deletions(-) diffs (truncated from 1043 to 1000 lines): diff -r fa22235730b1 -r 750f7c6f071c nginx/ngx_http_js_module.c --- a/nginx/ngx_http_js_module.c Wed Mar 21 17:33:12 2018 +0300 +++ b/nginx/ngx_http_js_module.c Wed Mar 21 17:33:13 2018 +0300 @@ -24,6 +24,7 @@ typedef struct { ngx_str_t content; const njs_extern_t *req_proto; const njs_extern_t *res_proto; + const njs_extern_t *rep_proto; } ngx_http_js_loc_conf_t; @@ -31,6 +32,7 @@ typedef struct { njs_vm_t *vm; ngx_log_t *log; njs_opaque_value_t args[2]; + ngx_uint_t done; } ngx_http_js_ctx_t; @@ -107,6 +109,17 @@ static njs_ret_t ngx_http_js_ext_get_var void *obj, uintptr_t data); static njs_ret_t ngx_http_js_ext_get_response(njs_vm_t *vm, njs_value_t *value, void *obj, uintptr_t data); +static njs_ret_t ngx_http_js_ext_subrequest(njs_vm_t *vm, njs_value_t *args, + nxt_uint_t nargs, njs_index_t unused); +static ngx_int_t ngx_http_js_subrequest(ngx_http_request_t *r, + nxt_str_t *uri_arg, nxt_str_t *args_arg, njs_function_t *callback, + ngx_http_request_t **sr); +static ngx_int_t ngx_http_js_subrequest_done(ngx_http_request_t *r, + void *data, ngx_int_t rc); +static njs_ret_t ngx_http_js_ext_get_parent(njs_vm_t *vm, njs_value_t *value, + void *obj, uintptr_t data); +static njs_ret_t ngx_http_js_ext_get_body(njs_vm_t *vm, njs_value_t *value, + void *obj, uintptr_t data); static njs_host_event_t ngx_http_js_set_timer(njs_external_ptr_t external, uint64_t delay, njs_vm_event_t vm_event); @@ -114,7 +127,7 @@ static void ngx_http_js_clear_timer(njs_ njs_host_event_t event); static void ngx_http_js_timer_handler(ngx_event_t *ev); static void ngx_http_js_handle_event(ngx_http_request_t *r, - njs_vm_event_t vm_event); + njs_vm_event_t vm_event, njs_opaque_value_t *args, nxt_uint_t nargs); static char *ngx_http_js_include(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); @@ -381,6 +394,118 @@ static njs_external_t ngx_http_js_ext_r NULL, NULL, 0 }, + + { nxt_string("subrequest"), + NJS_EXTERN_METHOD, + NULL, + 0, + NULL, + NULL, + NULL, + NULL, + NULL, + ngx_http_js_ext_subrequest, + 0 }, +}; + + +static njs_external_t ngx_http_js_ext_reply[] = { + + { nxt_string("headers"), + NJS_EXTERN_OBJECT, + NULL, + 0, + ngx_http_js_ext_get_header_out, + NULL, + NULL, + ngx_http_js_ext_foreach_header_out, + ngx_http_js_ext_next_header, + NULL, + 0 }, + + { nxt_string("status"), + NJS_EXTERN_PROPERTY, + NULL, + 0, + ngx_http_js_ext_get_status, + NULL, + NULL, + NULL, + NULL, + NULL, + offsetof(ngx_http_request_t, headers_out.status) }, + + { nxt_string("uri"), + NJS_EXTERN_PROPERTY, + NULL, + 0, + ngx_http_js_ext_get_string, + NULL, + NULL, + NULL, + NULL, + NULL, + offsetof(ngx_http_request_t, uri) }, + + { nxt_string("method"), + NJS_EXTERN_PROPERTY, + NULL, + 0, + ngx_http_js_ext_get_string, + NULL, + NULL, + NULL, + NULL, + NULL, + offsetof(ngx_http_request_t, method_name) }, + + { nxt_string("contentType"), + NJS_EXTERN_PROPERTY, + NULL, + 0, + ngx_http_js_ext_get_string, + NULL, + NULL, + NULL, + NULL, + NULL, + offsetof(ngx_http_request_t, headers_out.content_type) }, + + { nxt_string("contentLength"), + NJS_EXTERN_PROPERTY, + NULL, + 0, + ngx_http_js_ext_get_content_length, + NULL, + NULL, + NULL, + NULL, + NULL, + 0 }, + + { nxt_string("parent"), + NJS_EXTERN_PROPERTY, + NULL, + 0, + ngx_http_js_ext_get_parent, + NULL, + NULL, + NULL, + NULL, + NULL, + 0 }, + + { nxt_string("body"), + NJS_EXTERN_PROPERTY, + NULL, + 0, + ngx_http_js_ext_get_body, + NULL, + NULL, + NULL, + NULL, + NULL, + 0 }, }; @@ -409,6 +534,18 @@ static njs_external_t ngx_http_js_exter NULL, NULL, 0 }, + + { nxt_string("reply"), + NJS_EXTERN_OBJECT, + ngx_http_js_ext_reply, + nxt_nitems(ngx_http_js_ext_reply), + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + 0 }, }; @@ -916,20 +1053,13 @@ static njs_ret_t ngx_http_js_ext_get_status(njs_vm_t *vm, njs_value_t *value, void *obj, uintptr_t data) { - size_t len; - u_char *p; ngx_http_request_t *r; r = (ngx_http_request_t *) obj; - p = ngx_pnalloc(r->pool, 3); - if (p == NULL) { - return NJS_ERROR; - } - - len = ngx_snprintf(p, 3, "%ui", r->headers_out.status) - p; - - return njs_string_create(vm, value, p, len, 0); + njs_value_number_set(njs_vm_retval(vm), r->headers_out.status); + + return NJS_OK; } @@ -957,20 +1087,13 @@ static njs_ret_t ngx_http_js_ext_get_content_length(njs_vm_t *vm, njs_value_t *value, void *obj, uintptr_t data) { - size_t len; - u_char *p; ngx_http_request_t *r; r = (ngx_http_request_t *) obj; - p = ngx_pnalloc(r->pool, NGX_OFF_T_LEN); - if (p == NULL) { - return NJS_ERROR; - } - - len = ngx_sprintf(p, "%O", r->headers_out.content_length_n) - p; - - return njs_string_create(vm, value, p, len, 0); + njs_value_number_set(njs_vm_retval(vm), r->headers_out.content_length_n); + + return NJS_OK; } @@ -1324,6 +1447,383 @@ ngx_http_js_ext_get_response(njs_vm_t *v } +static njs_ret_t +ngx_http_js_ext_subrequest(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + ngx_int_t rc; + nxt_str_t uri_arg, args_arg, method_name, body_arg; + ngx_uint_t cb_index, method, n, has_body; + const char *description; + njs_value_t *arg2, *options, *value; + njs_function_t *callback; + ngx_http_request_t *r, *sr; + ngx_http_request_body_t *rb; + + static const struct { + ngx_str_t name; + ngx_uint_t value; + } methods[] = { + { ngx_string("GET"), NGX_HTTP_GET }, + { ngx_string("POST"), NGX_HTTP_POST }, + { ngx_string("HEAD"), NGX_HTTP_HEAD }, + { ngx_string("OPTIONS"), NGX_HTTP_OPTIONS }, + { ngx_string("PROPFIND"), NGX_HTTP_PROPFIND }, + { ngx_string("PUT"), NGX_HTTP_PUT }, + { ngx_string("MKCOL"), NGX_HTTP_MKCOL }, + { ngx_string("DELETE"), NGX_HTTP_DELETE }, + { ngx_string("COPY"), NGX_HTTP_COPY }, + { ngx_string("MOVE"), NGX_HTTP_MOVE }, + { ngx_string("PROPPATCH"), NGX_HTTP_PROPPATCH }, + { ngx_string("LOCK"), NGX_HTTP_LOCK }, + { ngx_string("UNLOCK"), NGX_HTTP_UNLOCK }, + { ngx_string("PATCH"), NGX_HTTP_PATCH }, + { ngx_string("TRACE"), NGX_HTTP_TRACE }, + }; + + static const nxt_str_t args_key = nxt_string("args"); + static const nxt_str_t method_key = nxt_string("method"); + static const nxt_str_t body_key = nxt_string("body"); + + if (nargs < 2) { + description = "too few arguments"; + goto exception; + } + + r = njs_value_data(njs_argument(args, 0)); + + if (njs_vm_value_to_ext_string(vm, &uri_arg, njs_argument(args, 1), 0) + == NJS_ERROR) + { + description = "failed to convert uri arg"; + goto exception; + } + + options = NULL; + + method = 0; + args_arg.length = 0; + body_arg.length = 0; + has_body = 0; + + if (nargs > 2 && !njs_value_is_function(njs_argument(args, 2))) { + arg2 = njs_argument(args, 2); + + if (njs_value_is_object(arg2)) { + options = arg2; + + } else if (njs_value_is_string(arg2)) { + if (njs_vm_value_to_ext_string(vm, &args_arg, arg2, 0) + == NJS_ERROR) + { + description = "failed to convert args"; + goto exception; + } + + } else { + description = "failed to convert args"; + goto exception; + } + + cb_index = 3; + + } else { + cb_index = 2; + } + + if (options != NULL) { + value = njs_vm_object_prop(vm, options, &args_key); + if (value != NULL) { + if (njs_vm_value_to_ext_string(vm, &args_arg, value, 0) + == NJS_ERROR) + { + description = "failed to convert options.args"; + goto exception; + } + } + + value = njs_vm_object_prop(vm, options, &method_key); + if (value != NULL) { + if (njs_vm_value_to_ext_string(vm, &method_name, value, 0) + == NJS_ERROR) + { + description = "failed to convert options.method"; + goto exception; + } + + n = sizeof(methods) / sizeof(methods[0]); + + while (method < n) { + if (method_name.length == methods[method].name.len + && ngx_memcmp(method_name.start, methods[method].name.data, + method_name.length) + == 0) + { + break; + } + + method++; + } + + if (method == n) { + njs_value_error_set(vm, njs_vm_retval(vm), + "unknown method \"%.*s\"", + (int) method_name.length, + method_name.start); + + return NJS_ERROR; + } + } + + value = njs_vm_object_prop(vm, options, &body_key); + if (value != NULL) { + if (njs_vm_value_to_ext_string(vm, &body_arg, value, 0) + == NJS_ERROR) + { + description = "failed to convert options.body"; + goto exception; + } + + has_body = 1; + } + } + + callback = NULL; + + if (cb_index < nargs) { + if (!njs_value_is_function(njs_argument(args, cb_index))) { + description = "callback is not a function"; + goto exception; + + } else { + callback = njs_value_function(njs_argument(args, cb_index)); + } + } + + rc = ngx_http_js_subrequest(r, &uri_arg, &args_arg, callback, &sr); + if (rc != NGX_OK) { + return NJS_ERROR; + } + + sr->method = methods[method].value; + sr->method_name = methods[method].name; + sr->header_only = (sr->method == NGX_HTTP_HEAD) || (callback == NULL); + + if (has_body) { + rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)); + if (rb == NULL) { + return NJS_ERROR; + } + + rb->bufs = ngx_alloc_chain_link(r->pool); + if (rb->bufs == NULL) { + return NJS_ERROR; + } + + rb->bufs->next = NULL; + + rb->bufs->buf = ngx_calloc_buf(r->pool); + if (rb->bufs->buf == NULL) { + return NJS_ERROR; + } + + rb->bufs->buf->memory = 1; + rb->bufs->buf->last_buf = 1; + + rb->bufs->buf->pos = body_arg.start; + rb->bufs->buf->last = body_arg.start + body_arg.length; + + sr->request_body = rb; + sr->headers_in.content_length_n = body_arg.length; + sr->headers_in.chunked = 0; + } + + return NJS_OK; + +exception: + + njs_value_error_set(vm, njs_vm_retval(vm), description, NULL); + + return NJS_ERROR; +} + + +static ngx_int_t +ngx_http_js_subrequest(ngx_http_request_t *r, nxt_str_t *uri_arg, + nxt_str_t *args_arg, njs_function_t *callback, ngx_http_request_t **sr) +{ + ngx_int_t flags; + ngx_str_t uri, args; + const char *description; + njs_vm_event_t vm_event; + ngx_http_js_ctx_t *ctx; + ngx_http_post_subrequest_t *ps; + + ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); + + flags = NGX_HTTP_SUBREQUEST_BACKGROUND; + + if (callback != NULL) { + ps = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t)); + if (ps == NULL) { + description = "internal error"; + goto exception; + } + + vm_event = njs_vm_add_event(ctx->vm, callback, NULL, NULL); + if (vm_event == NULL) { + description = "internal error"; + goto exception; + } + + ps->handler = ngx_http_js_subrequest_done; + ps->data = vm_event; + + flags |= NGX_HTTP_SUBREQUEST_IN_MEMORY; + + } else { + ps = NULL; + vm_event = NULL; + } + + uri.len = uri_arg->length; + uri.data = uri_arg->start; + + args.len = args_arg->length; + args.data = args_arg->start; + + if (ngx_http_subrequest(r, &uri, args.len ? &args : NULL, sr, ps, flags) + != NGX_OK) + { + if (vm_event != NULL) { + njs_vm_del_event(ctx->vm, vm_event); + } + + description = "subrequest creation failed"; + goto exception; + } + + return NJS_OK; + +exception: + + njs_value_error_set(ctx->vm, njs_vm_retval(ctx->vm), description, NULL); + + return NJS_ERROR; +} + + +static ngx_int_t +ngx_http_js_subrequest_done(ngx_http_request_t *r, void *data, ngx_int_t rc) +{ + njs_vm_event_t vm_event = data; + + nxt_int_t ret; + ngx_http_js_ctx_t *ctx; + njs_opaque_value_t reply; + ngx_http_js_loc_conf_t *jlcf; + + if (rc != NGX_OK || r->connection->error || r->buffered) { + return rc; + } + + ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); + + if (ctx && ctx->done) { + return NGX_OK; + } + + if (ctx == NULL) { + ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_js_ctx_t)); + if (ctx == NULL) { + return NGX_ERROR; + } + + ngx_http_set_ctx(r, ctx, ngx_http_js_module); + } + + ctx->done = 1; + + jlcf = ngx_http_get_module_loc_conf(r->parent, ngx_http_js_module); + + ctx = ngx_http_get_module_ctx(r->parent, ngx_http_js_module); + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "js subrequest done s: %ui parent ctx: %p", + r->headers_out.status, ctx); + + if (ctx == NULL) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "js subrequest: failed to get the parent context"); + + return NGX_ERROR; + } + + ret = njs_vm_external_create(ctx->vm, &reply, jlcf->rep_proto, r); + if (ret != NXT_OK) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "js subrequest reply creation failed"); + + return NGX_ERROR; + } + + ngx_http_js_handle_event(r->parent, vm_event, &reply, 1); + + return NGX_OK; +} + + +static njs_ret_t +ngx_http_js_ext_get_parent(njs_vm_t *vm, njs_value_t *value, void *obj, + uintptr_t data) +{ + ngx_http_js_ctx_t *ctx; + ngx_http_request_t *r; + + r = (ngx_http_request_t *) obj; + + ctx = ngx_http_get_module_ctx(r->parent, ngx_http_js_module); + + if (ctx == NULL) { + njs_value_error_set(vm, njs_vm_retval(vm), + "failed to get the parent context", NULL); + return NJS_ERROR; + } + + njs_vm_retval_set(ctx->vm, &ctx->args[0]); + + return NJS_OK; +} + + +static njs_ret_t +ngx_http_js_ext_get_body(njs_vm_t *vm, njs_value_t *value, void *obj, + uintptr_t data) +{ + size_t len; + u_char *p; + ngx_buf_t *b; + ngx_http_request_t *r; + + r = (ngx_http_request_t *) obj; + + b = r->out ? r->out->buf : NULL; + + len = b ? b->last - b->pos : 0; + + p = njs_string_alloc(vm, value, len, 0); + if (p == NULL) { + return NJS_ERROR; + } + + if (len) { + ngx_memcpy(p, b->pos, len); + } + + return NJS_OK; +} + + static njs_host_event_t ngx_http_js_set_timer(njs_external_ptr_t external, uint64_t delay, njs_vm_event_t vm_event) @@ -1382,14 +1882,15 @@ ngx_http_js_timer_handler(ngx_event_t *e c = r->connection; - ngx_http_js_handle_event(r, js_event->vm_event); + ngx_http_js_handle_event(r, js_event->vm_event, NULL, 0); ngx_http_run_posted_requests(c); } static void -ngx_http_js_handle_event(ngx_http_request_t *r, njs_vm_event_t vm_event) +ngx_http_js_handle_event(ngx_http_request_t *r, njs_vm_event_t vm_event, + njs_opaque_value_t *args, nxt_uint_t nargs) { njs_ret_t rc; nxt_str_t exception; @@ -1397,7 +1898,7 @@ ngx_http_js_handle_event(ngx_http_reques ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); - njs_vm_post_event(ctx->vm, vm_event); + njs_vm_post_event(ctx->vm, vm_event, args, nargs); rc = njs_vm_run(ctx->vm); @@ -1525,6 +2026,13 @@ ngx_http_js_include(ngx_conf_t *cf, ngx_ return NGX_CONF_ERROR; } + jlcf->rep_proto = njs_vm_external_prototype(jlcf->vm, + &ngx_http_js_externals[2]); + if (jlcf->rep_proto == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "failed to add reply proto"); + return NGX_CONF_ERROR; + } + rc = njs_vm_compile(jlcf->vm, &start, end); if (rc != NJS_OK) { @@ -1621,6 +2129,7 @@ ngx_http_js_create_loc_conf(ngx_conf_t * * conf->vm = NULL; * conf->req_proto = NULL; * conf->res_proto = NULL; + * conf->rep_proto = NULL; */ return conf; @@ -1637,6 +2146,7 @@ ngx_http_js_merge_loc_conf(ngx_conf_t *c conf->vm = prev->vm; conf->req_proto = prev->req_proto; conf->res_proto = prev->res_proto; + conf->rep_proto = prev->rep_proto; } return NGX_CONF_OK; diff -r fa22235730b1 -r 750f7c6f071c njs/njs_error.c --- a/njs/njs_error.c Wed Mar 21 17:33:12 2018 +0300 +++ b/njs/njs_error.c Wed Mar 21 17:33:13 2018 +0300 @@ -486,8 +486,8 @@ const njs_object_init_t njs_uri_error_c }; -static void -njs_set_memory_error(njs_vm_t *vm) +void +njs_set_memory_error(njs_vm_t *vm, njs_value_t *value) { njs_object_t *object; njs_object_prototype_t *prototypes; @@ -507,17 +507,17 @@ njs_set_memory_error(njs_vm_t *vm) */ object->extensible = 0; - vm->retval.data.type = NJS_OBJECT_INTERNAL_ERROR; - vm->retval.data.truth = 1; - vm->retval.data.u.number = NAN; - vm->retval.data.u.object = object; + value->data.type = NJS_OBJECT_INTERNAL_ERROR; + value->data.truth = 1; + value->data.u.number = NAN; + value->data.u.object = object; } void njs_exception_memory_error(njs_vm_t *vm) { - njs_set_memory_error(vm); + njs_set_memory_error(vm, &vm->retval); } @@ -525,7 +525,7 @@ njs_ret_t njs_memory_error_constructor(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, njs_index_t unused) { - njs_set_memory_error(vm); + njs_set_memory_error(vm, &vm->retval); return NXT_OK; } diff -r fa22235730b1 -r 750f7c6f071c njs/njs_error.h --- a/njs/njs_error.h Wed Mar 21 17:33:12 2018 +0300 +++ b/njs/njs_error.h Wed Mar 21 17:33:13 2018 +0300 @@ -29,6 +29,7 @@ void njs_exception_error_create(njs_vm_t const char* fmt, ...); void njs_exception_memory_error(njs_vm_t *vm); +void njs_set_memory_error(njs_vm_t *vm, njs_value_t *value); njs_object_t *njs_error_alloc(njs_vm_t *vm, njs_value_type_t type, const njs_value_t *name, const njs_value_t *message); diff -r fa22235730b1 -r 750f7c6f071c njs/njs_vm.c --- a/njs/njs_vm.c Wed Mar 21 17:33:12 2018 +0300 +++ b/njs/njs_vm.c Wed Mar 21 17:33:13 2018 +0300 @@ -3655,6 +3655,54 @@ njs_value_number_set(njs_value_t *value, } +void +njs_value_error_set(njs_vm_t *vm, njs_value_t *value, const char *fmt, ...) +{ + size_t size; + va_list args; + nxt_int_t ret; + njs_value_t string; + njs_object_t *error; + char buf[256]; + + if (fmt != NULL) { + va_start(args, fmt); + size = vsnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + + } else { + size = 0; + } + + ret = njs_string_new(vm, &string, (u_char *) buf, size, size); + if (nxt_slow_path(ret != NXT_OK)) { + goto memory_error; + } + + error = njs_error_alloc(vm, NJS_OBJECT_ERROR, NULL, &string); + if (nxt_slow_path(error == NULL)) { + goto memory_error; + } + + value->data.u.object = error; + value->type = NJS_OBJECT_ERROR; + value->data.truth = 1; + + return; + +memory_error: + + njs_set_memory_error(vm, value); +} + + +nxt_noinline double +njs_value_number(njs_value_t *value) +{ + return value->data.u.number; +} + + nxt_noinline void * njs_value_data(njs_value_t *value) { @@ -3662,10 +3710,52 @@ njs_value_data(njs_value_t *value) } +nxt_noinline njs_function_t * +njs_value_function(njs_value_t *value) +{ + return value->data.u.function; +} + + nxt_noinline nxt_int_t njs_value_is_void(njs_value_t *value) { - return value->type == NJS_VOID; + return njs_is_void(value); +} + + +nxt_noinline nxt_int_t +njs_value_is_true(njs_value_t *value) +{ + return njs_is_true(value); +} + + +nxt_noinline nxt_int_t +njs_value_is_number(njs_value_t *value) +{ + return njs_is_number(value); +} + + +nxt_noinline nxt_int_t +njs_value_is_string(njs_value_t *value) +{ + return njs_is_string(value); +} + + +nxt_noinline nxt_int_t +njs_value_is_object(njs_value_t *value) +{ + return njs_is_object(value); +} + + +nxt_noinline nxt_int_t +njs_value_is_function(njs_value_t *value) +{ + return njs_is_function(value); } diff -r fa22235730b1 -r 750f7c6f071c njs/njscript.c --- a/njs/njscript.c Wed Mar 21 17:33:12 2018 +0300 +++ b/njs/njscript.c Wed Mar 21 17:33:13 2018 +0300 @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -503,6 +504,43 @@ njs_vm_call(njs_vm_t *vm, njs_function_t } +njs_vm_event_t +njs_vm_add_event(njs_vm_t *vm, njs_function_t *function, + njs_host_event_t host_ev, njs_event_destructor destructor) +{ + njs_event_t *event; + + event = nxt_mem_cache_alloc(vm->mem_cache_pool, sizeof(njs_event_t)); + if (nxt_slow_path(event == NULL)) { + return NULL; + } + + event->host_event = host_ev; + event->destructor = destructor; + event->function = function; + event->posted = 0; + event->nargs = 0; + event->args = NULL; + + if (njs_add_event(vm, event) != NJS_OK) { + return NULL; + } + + return event; +} + + +void +njs_vm_del_event(njs_vm_t *vm, njs_vm_event_t vm_event) +{ + njs_event_t *event; + + event = (njs_event_t *) vm_event; + + njs_del_event(vm, event, NJS_EVENT_RELEASE | NJS_EVENT_DELETE); +} + + nxt_int_t njs_vm_pending(njs_vm_t *vm) { @@ -511,12 +549,24 @@ njs_vm_pending(njs_vm_t *vm) nxt_int_t -njs_vm_post_event(njs_vm_t *vm, njs_vm_event_t vm_event) +njs_vm_post_event(njs_vm_t *vm, njs_vm_event_t vm_event, + njs_opaque_value_t *args, nxt_uint_t nargs) { njs_event_t *event; event = (njs_event_t *) vm_event; + if (nargs != 0 && !event->posted) { + event->nargs = nargs; + event->args = nxt_mem_cache_alloc(vm->mem_cache_pool, + sizeof(njs_opaque_value_t) * nargs); + if (nxt_slow_path(event->args == NULL)) { + return NJS_ERROR; + } + + memcpy(event->args, args, sizeof(njs_opaque_value_t) * nargs); + } + if (!event->posted) { event->posted = 1; nxt_queue_insert_tail(&vm->posted_events, &event->link); @@ -639,3 +689,29 @@ njs_ret_t njs_vm_retval_to_ext_string(nj return njs_vm_value_to_ext_string(vm, retval, &vm->retval, 1); } + + +njs_value_t * +njs_vm_object_prop(njs_vm_t *vm, njs_value_t *value, const nxt_str_t *key) +{ + nxt_int_t ret; + njs_object_prop_t *prop; + nxt_lvlhsh_query_t lhq; + + if (nxt_slow_path(!njs_is_object(value))) { + return NULL; + } + + lhq.key = *key; + lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length); + lhq.proto = &njs_object_hash_proto; + + ret = nxt_lvlhsh_find(&value->data.u.object->hash, &lhq); + if (nxt_slow_path(ret != NXT_OK)) { + return NULL; + } + + prop = lhq.value; + + return &prop->value; +} diff -r fa22235730b1 -r 750f7c6f071c njs/njscript.h --- a/njs/njscript.h Wed Mar 21 17:33:12 2018 +0300 +++ b/njs/njscript.h Wed Mar 21 17:33:13 2018 +0300 @@ -139,8 +139,13 @@ NXT_EXPORT njs_vm_t *njs_vm_clone(njs_vm NXT_EXPORT nxt_int_t njs_vm_call(njs_vm_t *vm, njs_function_t *function, njs_opaque_value_t *args, nxt_uint_t nargs); +NXT_EXPORT njs_vm_event_t njs_vm_add_event(njs_vm_t *vm, + njs_function_t *function, njs_host_event_t host_ev, + njs_event_destructor destructor); +NXT_EXPORT void njs_vm_del_event(njs_vm_t *vm, njs_vm_event_t vm_event); NXT_EXPORT nxt_int_t njs_vm_pending(njs_vm_t *vm); -NXT_EXPORT nxt_int_t njs_vm_post_event(njs_vm_t *vm, njs_vm_event_t vm_event); +NXT_EXPORT nxt_int_t njs_vm_post_event(njs_vm_t *vm, njs_vm_event_t vm_event, + njs_opaque_value_t *args, nxt_uint_t nargs); NXT_EXPORT nxt_int_t njs_vm_run(njs_vm_t *vm); @@ -174,10 +179,22 @@ NXT_EXPORT njs_ret_t njs_vm_retval_to_ex NXT_EXPORT void njs_value_void_set(njs_value_t *value); NXT_EXPORT void njs_value_boolean_set(njs_value_t *value, int yn); NXT_EXPORT void njs_value_number_set(njs_value_t *value, double num); +NXT_EXPORT void njs_value_error_set(njs_vm_t *vm, njs_value_t *value, + const char *fmt, ...); + +NXT_EXPORT double njs_value_number(njs_value_t *value); NXT_EXPORT void *njs_value_data(njs_value_t *value); +NXT_EXPORT njs_function_t *njs_value_function(njs_value_t *value); NXT_EXPORT nxt_int_t njs_value_is_void(njs_value_t *value); +NXT_EXPORT nxt_int_t njs_value_is_true(njs_value_t *value); +NXT_EXPORT nxt_int_t njs_value_is_number(njs_value_t *value); +NXT_EXPORT nxt_int_t njs_value_is_string(njs_value_t *value); +NXT_EXPORT nxt_int_t njs_value_is_object(njs_value_t *value); +NXT_EXPORT nxt_int_t njs_value_is_function(njs_value_t *value); +NXT_EXPORT njs_value_t *njs_vm_object_prop(njs_vm_t *vm, njs_value_t *value, + const nxt_str_t *key); extern const nxt_mem_proto_t njs_vm_mem_cache_pool_proto; diff -r fa22235730b1 -r 750f7c6f071c njs/test/njs_unit_test.c --- a/njs/test/njs_unit_test.c Wed Mar 21 17:33:12 2018 +0300 +++ b/njs/test/njs_unit_test.c Wed Mar 21 17:33:13 2018 +0300 @@ -3867,6 +3867,9 @@ static njs_unit_test_t njs_test[] = " valueOf: function() { return 1 } }; a"), nxt_string("1") }, From xeioex at nginx.com Wed Mar 21 14:33:27 2018 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Wed, 21 Mar 2018 14:33:27 +0000 Subject: [njs] setTimeout() and clearTimeout() methods. Message-ID: details: http://hg.nginx.org/njs/rev/6d599ae5b35b branches: changeset: 463:6d599ae5b35b user: Dmitry Volyntsev date: Wed Mar 21 17:33:12 2018 +0300 description: setTimeout() and clearTimeout() methods. Public methods are introduced to create and post async events for a VM instance. njs_vm_add_event() creates an async event for the VM to wait for. njs_vm_post_event() notifies the VM that the event occurred. If async events were added njs_vm_run() returns NJS_AGAIN until there are no remaining pending events. diffstat: Makefile | 27 ++++ nginx/ngx_http_js_module.c | 249 ++++++++++++++++++++++++++++++++++++++- nginx/ngx_stream_js_module.c | 38 +++++- njs/njs_builtin.c | 8 +- njs/njs_event.c | 121 +++++++++++++++++++ njs/njs_event.h | 39 ++++++ njs/njs_generator.c | 2 + njs/njs_lexer_keyword.c | 2 + njs/njs_parser.c | 2 + njs/njs_parser.h | 2 + njs/njs_time.c | 141 ++++++++++++++++++++++ njs/njs_time.h | 20 +++ njs/njs_vm.h | 14 +- njs/njscript.c | 89 ++++++++++++++- njs/njscript.h | 46 +++++++- njs/test/njs_interactive_test.c | 5 + njs/test/njs_unit_test.c | 19 +++ 17 files changed, 804 insertions(+), 20 deletions(-) diffs (truncated from 1264 to 1000 lines): diff -r e8c08e05d18c -r 6d599ae5b35b Makefile --- a/Makefile Thu Mar 15 15:15:25 2018 +0300 +++ b/Makefile Wed Mar 21 17:33:12 2018 +0300 @@ -20,7 +20,9 @@ NXT_BUILDDIR = build $(NXT_BUILDDIR)/njs_date.o \ $(NXT_BUILDDIR)/njs_error.o \ $(NXT_BUILDDIR)/njs_math.o \ + $(NXT_BUILDDIR)/njs_time.o \ $(NXT_BUILDDIR)/njs_module.o \ + $(NXT_BUILDDIR)/njs_event.o \ $(NXT_BUILDDIR)/njs_fs.o \ $(NXT_BUILDDIR)/njs_extern.o \ $(NXT_BUILDDIR)/njs_variable.o \ @@ -56,7 +58,9 @@ NXT_BUILDDIR = build $(NXT_BUILDDIR)/njs_date.o \ $(NXT_BUILDDIR)/njs_error.o \ $(NXT_BUILDDIR)/njs_math.o \ + $(NXT_BUILDDIR)/njs_time.o \ $(NXT_BUILDDIR)/njs_module.o \ + $(NXT_BUILDDIR)/njs_event.o \ $(NXT_BUILDDIR)/njs_fs.o \ $(NXT_BUILDDIR)/njs_extern.o \ $(NXT_BUILDDIR)/njs_variable.o \ @@ -301,6 +305,18 @@ dist: -I$(NXT_LIB) -Injs \ njs/njs_math.c +$(NXT_BUILDDIR)/njs_time.o: \ + $(NXT_BUILDDIR)/libnxt.a \ + njs/njscript.h \ + njs/njs_vm.h \ + njs/njs_object.h \ + njs/njs_time.h \ + njs/njs_time.c \ + + $(NXT_CC) -c -o $(NXT_BUILDDIR)/njs_time.o $(NXT_CFLAGS) \ + -I$(NXT_LIB) -Injs \ + njs/njs_time.c + $(NXT_BUILDDIR)/njs_module.o: \ $(NXT_BUILDDIR)/libnxt.a \ njs/njscript.h \ @@ -312,6 +328,17 @@ dist: -I$(NXT_LIB) -Injs \ njs/njs_module.c +$(NXT_BUILDDIR)/njs_event.o: \ + $(NXT_BUILDDIR)/libnxt.a \ + njs/njscript.h \ + njs/njs_vm.h \ + njs/njs_event.h \ + njs/njs_event.c \ + + $(NXT_CC) -c -o $(NXT_BUILDDIR)/njs_event.o $(NXT_CFLAGS) \ + -I$(NXT_LIB) -Injs \ + njs/njs_event.c + $(NXT_BUILDDIR)/njs_fs.o: \ $(NXT_BUILDDIR)/libnxt.a \ njs/njscript.h \ diff -r e8c08e05d18c -r 6d599ae5b35b nginx/ngx_http_js_module.c --- a/nginx/ngx_http_js_module.c Thu Mar 15 15:15:25 2018 +0300 +++ b/nginx/ngx_http_js_module.c Wed Mar 21 17:33:12 2018 +0300 @@ -29,6 +29,7 @@ typedef struct { typedef struct { njs_vm_t *vm; + ngx_log_t *log; njs_opaque_value_t args[2]; } ngx_http_js_ctx_t; @@ -39,10 +40,21 @@ typedef struct { } ngx_http_js_table_entry_t; -static ngx_int_t ngx_http_js_handler(ngx_http_request_t *r); +typedef struct { + ngx_http_request_t *request; + njs_vm_event_t vm_event; + void *unused; + ngx_int_t ident; +} ngx_http_js_event_t; + + +static ngx_int_t ngx_http_js_content_handler(ngx_http_request_t *r); +static void ngx_http_js_content_event_handler(ngx_http_request_t *r); +static void ngx_http_js_content_write_event_handler(ngx_http_request_t *r); static ngx_int_t ngx_http_js_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_js_init_vm(ngx_http_request_t *r); +static void ngx_http_js_cleanup_ctx(void *data); static void ngx_http_js_cleanup_vm(void *data); static njs_ret_t ngx_http_js_ext_get_string(njs_vm_t *vm, njs_value_t *value, @@ -94,6 +106,14 @@ static njs_ret_t ngx_http_js_ext_next_ar static njs_ret_t ngx_http_js_ext_get_variable(njs_vm_t *vm, njs_value_t *value, void *obj, uintptr_t data); +static njs_host_event_t ngx_http_js_set_timer(njs_external_ptr_t external, + uint64_t delay, njs_vm_event_t vm_event); +static void ngx_http_js_clear_timer(njs_external_ptr_t external, + njs_host_event_t event); +static void ngx_http_js_timer_handler(ngx_event_t *ev); +static void ngx_http_js_handle_event(ngx_http_request_t *r, + njs_vm_event_t vm_event); + static char *ngx_http_js_include(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_js_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); @@ -378,8 +398,33 @@ static njs_external_t ngx_http_js_exter }; +static njs_vm_ops_t ngx_http_js_ops = { + ngx_http_js_set_timer, + ngx_http_js_clear_timer +}; + + static ngx_int_t -ngx_http_js_handler(ngx_http_request_t *r) +ngx_http_js_content_handler(ngx_http_request_t *r) +{ + ngx_int_t rc; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http js content handler"); + + rc = ngx_http_read_client_request_body(r, + ngx_http_js_content_event_handler); + + if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { + return rc; + } + + return NGX_DONE; +} + + +static void +ngx_http_js_content_event_handler(ngx_http_request_t *r) { ngx_int_t rc; nxt_str_t name, exception; @@ -387,10 +432,14 @@ ngx_http_js_handler(ngx_http_request_t * ngx_http_js_ctx_t *ctx; ngx_http_js_loc_conf_t *jlcf; + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http js content event handler"); + rc = ngx_http_js_init_vm(r); if (rc == NGX_ERROR || rc == NGX_DECLINED) { - return rc; + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; } jlcf = ngx_http_get_module_loc_conf(r, ngx_http_js_module); @@ -407,7 +456,8 @@ ngx_http_js_handler(ngx_http_request_t * if (func == NULL) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "js function \"%V\" not found", &jlcf->content); - return NGX_DECLINED; + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; } if (njs_vm_call(ctx->vm, func, ctx->args, 2) != NJS_OK) { @@ -416,10 +466,66 @@ ngx_http_js_handler(ngx_http_request_t * ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "js exception: %*s", exception.length, exception.start); - return NGX_ERROR; + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + if (njs_vm_pending(ctx->vm)) { + r->write_event_handler = ngx_http_js_content_write_event_handler; + return; } - return NGX_OK; + ngx_http_finalize_request(r, NGX_OK); +} + + +static void +ngx_http_js_content_write_event_handler(ngx_http_request_t *r) +{ + ngx_event_t *wev; + ngx_connection_t *c; + ngx_http_js_ctx_t *ctx; + ngx_http_core_loc_conf_t *clcf; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http js content write event handler"); + + ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); + + if (!njs_vm_pending(ctx->vm)) { + ngx_http_finalize_request(r, NGX_OK); + return; + } + + c = r->connection; + wev = c->write; + + if (wev->timedout) { + ngx_connection_error(c, NGX_ETIMEDOUT, "client timed out"); + ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT); + return; + } + + if (ngx_http_output_filter(r, NULL) == NGX_ERROR) { + ngx_http_finalize_request(r, NGX_ERROR); + return; + } + + clcf = ngx_http_get_module_loc_conf(r->main, ngx_http_core_module); + + if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) { + ngx_http_finalize_request(r, NGX_ERROR); + return; + } + + if (!wev->delayed) { + if (wev->active && !wev->ready) { + ngx_add_timer(wev, clcf->send_timeout); + + } else if (wev->timer_set) { + ngx_del_timer(wev); + } + } } @@ -430,6 +536,7 @@ ngx_http_js_variable(ngx_http_request_t ngx_str_t *fname = (ngx_str_t *) data; ngx_int_t rc; + nxt_int_t pending; nxt_str_t name, value, exception; njs_function_t *func; ngx_http_js_ctx_t *ctx; @@ -461,6 +568,8 @@ ngx_http_js_variable(ngx_http_request_t return NGX_OK; } + pending = njs_vm_pending(ctx->vm); + if (njs_vm_call(ctx->vm, func, ctx->args, 2) != NJS_OK) { njs_vm_retval_to_ext_string(ctx->vm, &exception); @@ -475,6 +584,12 @@ ngx_http_js_variable(ngx_http_request_t return NGX_ERROR; } + if (!pending && njs_vm_pending(ctx->vm)) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "async operation inside \"%V\" variable handler", fname); + return NGX_ERROR; + } + v->len = value.length; v->valid = 1; v->no_cacheable = 0; @@ -489,6 +604,7 @@ static ngx_int_t ngx_http_js_init_vm(ngx_http_request_t *r) { nxt_int_t rc; + nxt_str_t exception; ngx_http_js_ctx_t *ctx; ngx_pool_cleanup_t *cln; ngx_http_js_loc_conf_t *jlcf; @@ -523,10 +639,17 @@ ngx_http_js_init_vm(ngx_http_request_t * return NGX_ERROR; } - cln->handler = ngx_http_js_cleanup_vm; - cln->data = ctx->vm; + ctx->log = r->connection->log; + + cln->handler = ngx_http_js_cleanup_ctx; + cln->data = ctx; - if (njs_vm_run(ctx->vm) != NJS_OK) { + if (njs_vm_run(ctx->vm) == NJS_ERROR) { + njs_vm_retval_to_ext_string(ctx->vm, &exception); + + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "js exception: %*s", exception.length, exception.start); + return NGX_ERROR; } @@ -545,6 +668,19 @@ ngx_http_js_init_vm(ngx_http_request_t * static void +ngx_http_js_cleanup_ctx(void *data) +{ + ngx_http_js_ctx_t *ctx = data; + + if (njs_vm_pending(ctx->vm)) { + ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "pending events"); + } + + njs_vm_destroy(ctx->vm); +} + + +static void ngx_http_js_cleanup_vm(void *data) { njs_vm_t *vm = data; @@ -1157,6 +1293,98 @@ ngx_http_js_ext_get_variable(njs_vm_t *v } +static njs_host_event_t +ngx_http_js_set_timer(njs_external_ptr_t external, uint64_t delay, + njs_vm_event_t vm_event) +{ + ngx_event_t *ev; + ngx_http_request_t *r; + ngx_http_js_event_t *js_event; + + r = (ngx_http_request_t *) external; + + ev = ngx_pcalloc(r->pool, sizeof(ngx_event_t)); + if (ev == NULL) { + return NULL; + } + + js_event = ngx_palloc(r->pool, sizeof(ngx_http_js_event_t)); + if (js_event == NULL) { + return NULL; + } + + js_event->request = r; + js_event->vm_event = vm_event; + js_event->ident = r->connection->fd; + + ev->data = js_event; + ev->log = r->connection->log; + ev->handler = ngx_http_js_timer_handler; + + ngx_add_timer(ev, delay); + + return ev; +} + + +static void +ngx_http_js_clear_timer(njs_external_ptr_t external, njs_host_event_t event) +{ + ngx_event_t *ev = event; + + if (ev->timer_set) { + ngx_del_timer(ev); + } +} + + +static void +ngx_http_js_timer_handler(ngx_event_t *ev) +{ + ngx_connection_t *c; + ngx_http_request_t *r; + ngx_http_js_event_t *js_event; + + js_event = (ngx_http_js_event_t *) ev->data; + + r = js_event->request; + + c = r->connection; + + ngx_http_js_handle_event(r, js_event->vm_event); + + ngx_http_run_posted_requests(c); +} + + +static void +ngx_http_js_handle_event(ngx_http_request_t *r, njs_vm_event_t vm_event) +{ + njs_ret_t rc; + nxt_str_t exception; + ngx_http_js_ctx_t *ctx; + + ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); + + njs_vm_post_event(ctx->vm, vm_event); + + rc = njs_vm_run(ctx->vm); + + if (rc == NJS_ERROR) { + njs_vm_retval_to_ext_string(ctx->vm, &exception); + + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "js exception: %*s", exception.length, exception.start); + + ngx_http_finalize_request(r, NGX_ERROR); + } + + if (rc == NJS_OK) { + ngx_http_post_request(r, NULL); + } +} + + static char * ngx_http_js_include(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { @@ -1235,6 +1463,7 @@ ngx_http_js_include(ngx_conf_t *cf, ngx_ ngx_memzero(&options, sizeof(njs_vm_opt_t)); options.backtrace = 1; + options.ops = &ngx_http_js_ops; jlcf->vm = njs_vm_create(&options); if (jlcf->vm == NULL) { @@ -1339,7 +1568,7 @@ ngx_http_js_content(ngx_conf_t *cf, ngx_ jlcf->content = value[1]; clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); - clcf->handler = ngx_http_js_handler; + clcf->handler = ngx_http_js_content_handler; return NGX_CONF_OK; } diff -r e8c08e05d18c -r 6d599ae5b35b nginx/ngx_stream_js_module.c --- a/nginx/ngx_stream_js_module.c Thu Mar 15 15:15:25 2018 +0300 +++ b/nginx/ngx_stream_js_module.c Wed Mar 21 17:33:12 2018 +0300 @@ -30,6 +30,7 @@ typedef struct { typedef struct { njs_vm_t *vm; + ngx_log_t *log; njs_opaque_value_t arg; ngx_buf_t *buf; ngx_chain_t *free; @@ -49,6 +50,7 @@ static ngx_int_t ngx_stream_js_body_filt static ngx_int_t ngx_stream_js_variable(ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_stream_js_init_vm(ngx_stream_session_t *s); +static void ngx_stream_js_cleanup_ctx(void *data); static void ngx_stream_js_cleanup_vm(void *data); static njs_ret_t ngx_stream_js_ext_get_remote_address(njs_vm_t *vm, @@ -526,6 +528,7 @@ ngx_stream_js_variable(ngx_stream_sessio ngx_str_t *fname = (ngx_str_t *) data; ngx_int_t rc; + nxt_int_t pending; nxt_str_t name, value, exception; njs_function_t *func; ngx_stream_js_ctx_t *ctx; @@ -557,6 +560,8 @@ ngx_stream_js_variable(ngx_stream_sessio return NGX_OK; } + pending = njs_vm_pending(ctx->vm); + if (njs_vm_call(ctx->vm, func, &ctx->arg, 1) != NJS_OK) { njs_vm_retval_to_ext_string(ctx->vm, &exception); @@ -571,6 +576,12 @@ ngx_stream_js_variable(ngx_stream_sessio return NGX_ERROR; } + if (!pending && njs_vm_pending(ctx->vm)) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "async operation inside \"%V\" variable handler", fname); + return NGX_ERROR; + } + v->len = value.length; v->valid = 1; v->no_cacheable = 0; @@ -585,6 +596,7 @@ static ngx_int_t ngx_stream_js_init_vm(ngx_stream_session_t *s) { nxt_int_t rc; + nxt_str_t exception; ngx_pool_cleanup_t *cln; ngx_stream_js_ctx_t *ctx; ngx_stream_js_srv_conf_t *jscf; @@ -619,10 +631,17 @@ ngx_stream_js_init_vm(ngx_stream_session return NGX_ERROR; } - cln->handler = ngx_stream_js_cleanup_vm; - cln->data = ctx->vm; + ctx->log = s->connection->log; + + cln->handler = ngx_stream_js_cleanup_ctx; + cln->data = ctx; - if (njs_vm_run(ctx->vm) != NJS_OK) { + if (njs_vm_run(ctx->vm) == NJS_ERROR) { + njs_vm_retval_to_ext_string(ctx->vm, &exception); + + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "js exception: %*s", exception.length, exception.start); + return NGX_ERROR; } @@ -636,6 +655,19 @@ ngx_stream_js_init_vm(ngx_stream_session static void +ngx_stream_js_cleanup_ctx(void *data) +{ + ngx_stream_js_ctx_t *ctx = data; + + if (njs_vm_pending(ctx->vm)) { + ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "pending events"); + } + + njs_vm_destroy(ctx->vm); +} + + +static void ngx_stream_js_cleanup_vm(void *data) { njs_vm_t *vm = data; diff -r e8c08e05d18c -r 6d599ae5b35b njs/njs_builtin.c --- a/njs/njs_builtin.c Thu Mar 15 15:15:25 2018 +0300 +++ b/njs/njs_builtin.c Wed Mar 21 17:33:12 2018 +0300 @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -113,7 +114,9 @@ const njs_object_init_t *njs_function &njs_encode_uri_component_function_init, &njs_decode_uri_function_init, &njs_decode_uri_component_function_init, - &njs_require_function_init + &njs_require_function_init, + &njs_set_timeout_function_init, + &njs_clear_timeout_function_init }; @@ -131,6 +134,9 @@ const njs_function_init_t njs_native_fu { njs_string_decode_uri, { NJS_SKIP_ARG, NJS_STRING_ARG } }, { njs_string_decode_uri_component, { NJS_SKIP_ARG, NJS_STRING_ARG } }, { njs_module_require, { NJS_SKIP_ARG, NJS_STRING_ARG } }, + { njs_set_timeout, + { NJS_SKIP_ARG, NJS_FUNCTION_ARG, NJS_NUMBER_ARG } }, + { njs_clear_timeout, { NJS_SKIP_ARG, NJS_NUMBER_ARG } }, }; diff -r e8c08e05d18c -r 6d599ae5b35b njs/njs_event.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/njs/njs_event.c Wed Mar 21 17:33:12 2018 +0300 @@ -0,0 +1,121 @@ + +/* + * Copyright (C) Dmitry Volyntsev + * Copyright (C) NGINX, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static nxt_int_t njs_event_hash_test(nxt_lvlhsh_query_t *lhq, void *data); + + +const nxt_lvlhsh_proto_t njs_event_hash_proto + nxt_aligned(64) = +{ + NXT_LVLHSH_DEFAULT, + 0, + njs_event_hash_test, + njs_lvlhsh_alloc, + njs_lvlhsh_free, +}; + + +static nxt_int_t +njs_event_hash_test(nxt_lvlhsh_query_t *lhq, void *data) +{ + nxt_str_t id; + njs_event_t *event; + + event = data; + + njs_string_get(&event->id, &id); + + if (nxt_strstr_eq(&lhq->key, &id)) { + return NXT_OK; + } + + return NXT_DECLINED; +} + + +nxt_int_t +njs_add_event(njs_vm_t *vm, njs_event_t *event) +{ + size_t size; + nxt_int_t ret; + nxt_lvlhsh_query_t lhq; + + size = snprintf((char *) njs_string_short_start(&event->id), + NJS_STRING_SHORT, "%u", vm->event_id++); + njs_string_short_set(&event->id, size, size); + + njs_string_get(&event->id, &lhq.key); + lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length); + lhq.value = event; + lhq.proto = &njs_event_hash_proto; + lhq.pool = vm->mem_cache_pool; + + ret = nxt_lvlhsh_insert(&vm->events_hash, &lhq); + if (nxt_slow_path(ret != NXT_OK)) { + njs_exception_internal_error(vm, "Failed to add event with id: %s", + njs_string_short_start(&event->id)); + + njs_del_event(vm, event, NJS_EVENT_RELEASE | NJS_EVENT_DELETE); + return NJS_ERROR; + } + + njs_value_number_set(&vm->retval, vm->event_id - 1); + + return NJS_OK; +} + + +void +njs_del_event(njs_vm_t *vm, njs_event_t *ev, nxt_uint_t action) +{ + nxt_lvlhsh_query_t lhq; + + if (action & NJS_EVENT_RELEASE) { + if (ev->destructor != NULL && ev->host_event != NULL) { + ev->destructor(vm->external, ev->host_event); + } + + ev->host_event = NULL; + } + + if (action & NJS_EVENT_DELETE) { + njs_string_get(&ev->id, &lhq.key); + lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length); + lhq.proto = &njs_event_hash_proto; + lhq.pool = vm->mem_cache_pool; + + if (ev->posted) { + ev->posted = 0; + nxt_queue_remove(&ev->link); + } + + (void) nxt_lvlhsh_delete(&vm->events_hash, &lhq); + } +} diff -r e8c08e05d18c -r 6d599ae5b35b njs/njs_event.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/njs/njs_event.h Wed Mar 21 17:33:12 2018 +0300 @@ -0,0 +1,39 @@ + +/* + * Copyright (C) Dmitry Volyntsev + * Copyright (C) NGINX, Inc. + */ + +#ifndef _NJS_EVENT_H_INCLUDED_ +#define _NJS_EVENT_H_INCLUDED_ + + +#define NJS_EVENT_RELEASE 1 +#define NJS_EVENT_DELETE 2 + + +#define njs_is_pending_events(vm) (!nxt_lvlhsh_is_empty(&(vm)->events_hash)) + + +typedef struct { + njs_function_t *function; + njs_opaque_value_t *args; + nxt_uint_t nargs; + njs_host_event_t host_event; + njs_event_destructor destructor; + + njs_value_t id; + nxt_queue_link_t link; + + unsigned posted:1; +} njs_event_t; + + +nxt_int_t njs_add_event(njs_vm_t *vm, njs_event_t *event); +void njs_del_event(njs_vm_t *vm, njs_event_t *event, nxt_uint_t action); + + +extern const nxt_lvlhsh_proto_t njs_event_hash_proto; + + +#endif /* _NJS_EVENT_H_INCLUDED_ */ diff -r e8c08e05d18c -r 6d599ae5b35b njs/njs_generator.c --- a/njs/njs_generator.c Thu Mar 15 15:15:25 2018 +0300 +++ b/njs/njs_generator.c Wed Mar 21 17:33:12 2018 +0300 @@ -320,6 +320,8 @@ njs_generator(njs_vm_t *vm, njs_parser_t case NJS_TOKEN_DECODE_URI: case NJS_TOKEN_DECODE_URI_COMPONENT: case NJS_TOKEN_REQUIRE: + case NJS_TOKEN_SET_TIMEOUT: + case NJS_TOKEN_CLEAR_TIMEOUT: return njs_generate_builtin_object(vm, parser, node); case NJS_TOKEN_FUNCTION: diff -r e8c08e05d18c -r 6d599ae5b35b njs/njs_lexer_keyword.c --- a/njs/njs_lexer_keyword.c Thu Mar 15 15:15:25 2018 +0300 +++ b/njs/njs_lexer_keyword.c Wed Mar 21 17:33:12 2018 +0300 @@ -102,6 +102,8 @@ static const njs_keyword_t njs_keywords { nxt_string("decodeURI"), NJS_TOKEN_DECODE_URI, 0 }, { nxt_string("decodeURIComponent"), NJS_TOKEN_DECODE_URI_COMPONENT, 0 }, { nxt_string("require"), NJS_TOKEN_REQUIRE, 0 }, + { nxt_string("setTimeout"), NJS_TOKEN_SET_TIMEOUT, 0 }, + { nxt_string("clearTimeout"), NJS_TOKEN_CLEAR_TIMEOUT, 0 }, /* Reserved words. */ diff -r e8c08e05d18c -r 6d599ae5b35b njs/njs_parser.c --- a/njs/njs_parser.c Thu Mar 15 15:15:25 2018 +0300 +++ b/njs/njs_parser.c Wed Mar 21 17:33:12 2018 +0300 @@ -2072,6 +2072,8 @@ njs_parser_terminal(njs_vm_t *vm, njs_pa case NJS_TOKEN_DECODE_URI: case NJS_TOKEN_DECODE_URI_COMPONENT: case NJS_TOKEN_REQUIRE: + case NJS_TOKEN_SET_TIMEOUT: + case NJS_TOKEN_CLEAR_TIMEOUT: return njs_parser_builtin_function(vm, parser, node); default: diff -r e8c08e05d18c -r 6d599ae5b35b njs/njs_parser.h --- a/njs/njs_parser.h Thu Mar 15 15:15:25 2018 +0300 +++ b/njs/njs_parser.h Wed Mar 21 17:33:12 2018 +0300 @@ -199,6 +199,8 @@ typedef enum { NJS_TOKEN_DECODE_URI, NJS_TOKEN_DECODE_URI_COMPONENT, NJS_TOKEN_REQUIRE, + NJS_TOKEN_SET_TIMEOUT, + NJS_TOKEN_CLEAR_TIMEOUT, NJS_TOKEN_RESERVED, } njs_token_t; diff -r e8c08e05d18c -r 6d599ae5b35b njs/njs_time.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/njs/njs_time.c Wed Mar 21 17:33:12 2018 +0300 @@ -0,0 +1,141 @@ + +/* + * Copyright (C) Dmitry Volyntsev + * Copyright (C) NGINX, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +njs_ret_t +njs_set_timeout(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + uint64_t delay; + njs_event_t *event; + njs_vm_ops_t *ops; + + if (nxt_slow_path(nargs < 2)) { + njs_exception_type_error(vm, "too few arguments", NULL); + return NJS_ERROR; + } + + if (nxt_slow_path(!njs_is_function(&args[1]))) { + njs_exception_type_error(vm, "first arg must be a function", NULL); + return NJS_ERROR; + } + + ops = vm->ops; + if (nxt_slow_path(ops == NULL)) { + njs_exception_internal_error(vm, "not supported by host environment", + NULL); + return NJS_ERROR; + } + + delay = 0; + + if (nargs >= 3 && njs_is_number(&args[2])) { + delay = args[2].data.u.number; + } + + event = nxt_mem_cache_alloc(vm->mem_cache_pool, sizeof(njs_event_t)); + if (nxt_slow_path(event == NULL)) { + goto memory_error; + } + + event->destructor = ops->clear_timer; + event->function = args[1].data.u.function; + event->nargs = (nargs >= 3) ? nargs - 3 : 0; + event->posted = 0; + + if (event->nargs != 0) { + event->args = nxt_mem_cache_alloc(vm->mem_cache_pool, + sizeof(njs_value_t) * event->nargs); + if (nxt_slow_path(event->args == NULL)) { + goto memory_error; + } + + memcpy(event->args, &args[3], sizeof(njs_value_t) * event->nargs); + } + + event->host_event = ops->set_timer(vm->external, delay, event); + if (event->host_event == NULL) { + njs_exception_internal_error(vm, "set_timer() failed", NULL); + return NJS_ERROR; + } + + return njs_add_event(vm, event); + +memory_error: + + njs_exception_memory_error(vm); + return NJS_ERROR; +} + + +njs_ret_t +njs_clear_timeout(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + u_char buf[16]; + njs_ret_t ret; + njs_event_t *event; + nxt_lvlhsh_query_t lhq; + + if (nxt_fast_path(nargs < 2) || !njs_is_number(&args[1])) { + vm->retval = njs_string_void; + return NJS_OK; + } + + lhq.key.start = buf; + lhq.key.length = snprintf((char *) buf, sizeof(buf) - 1, "%u", + (unsigned) args[1].data.u.number); + lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length); + lhq.proto = &njs_event_hash_proto; + lhq.pool = vm->mem_cache_pool; + + ret = nxt_lvlhsh_find(&vm->events_hash, &lhq); + if (ret == NXT_OK) { + event = lhq.value; + njs_del_event(vm, event, NJS_EVENT_RELEASE | NJS_EVENT_DELETE); + } + + vm->retval = njs_string_void; + + return NJS_OK; +} + + +const njs_object_init_t njs_set_timeout_function_init = { + nxt_string("setTimeout"), + NULL, + 0, +}; + + +const njs_object_init_t njs_clear_timeout_function_init = { + nxt_string("clearTimeout"), + NULL, + 0, +}; diff -r e8c08e05d18c -r 6d599ae5b35b njs/njs_time.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/njs/njs_time.h Wed Mar 21 17:33:12 2018 +0300 @@ -0,0 +1,20 @@ + +/* + * Copyright (C) Dmitry Volyntsev + * Copyright (C) NGINX, Inc. + */ + +#ifndef _NJS_TIMEOUT_H_INCLUDED_ +#define _NJS_TIMEOUT_H_INCLUDED_ + + +njs_ret_t njs_set_timeout(njs_vm_t *vm, njs_value_t *args, + nxt_uint_t nargs, njs_index_t unused); +njs_ret_t njs_clear_timeout(njs_vm_t *vm, njs_value_t *args, + nxt_uint_t nargs, njs_index_t unused); + + +extern const njs_object_init_t njs_set_timeout_function_init; +extern const njs_object_init_t njs_clear_timeout_function_init; + +#endif /* _NJS_TIMEOUT_H_INCLUDED_ */ diff -r e8c08e05d18c -r 6d599ae5b35b njs/njs_vm.h --- a/njs/njs_vm.h Thu Mar 15 15:15:25 2018 +0300 +++ b/njs/njs_vm.h Wed Mar 21 17:33:12 2018 +0300 @@ -881,7 +881,9 @@ enum njs_function_e { NJS_FUNCTION_STRING_DECODE_URI, NJS_FUNCTION_STRING_DECODE_URI_COMPONENT, NJS_FUNCTION_REQUIRE, -#define NJS_FUNCTION_MAX (NJS_FUNCTION_REQUIRE + 1) + NJS_FUNCTION_SET_TIMEOUT, + NJS_FUNCTION_CLEAR_TIMEOUT, +#define NJS_FUNCTION_MAX (NJS_FUNCTION_CLEAR_TIMEOUT + 1) }; @@ -960,12 +962,12 @@ struct njs_vm_s { njs_value_t *scopes[NJS_SCOPES]; - void *external; + njs_external_ptr_t external; njs_native_frame_t *top_frame; njs_frame_t *active_frame; - nxt_array_t *external_objects; /* of void * */ + nxt_array_t *external_objects; /* of njs_external_ptr_t */ nxt_lvlhsh_t externals_hash; nxt_lvlhsh_t external_prototypes_hash; @@ -974,6 +976,12 @@ struct njs_vm_s { nxt_lvlhsh_t values_hash; nxt_lvlhsh_t modules_hash; + uint32_t event_id; + nxt_lvlhsh_t events_hash; + nxt_queue_t posted_events; + + njs_vm_ops_t *ops; + /* * The prototypes and constructors arrays must be together because * they are copied from njs_vm_shared_t by single memcpy() diff -r e8c08e05d18c -r 6d599ae5b35b njs/njscript.c --- a/njs/njscript.c Thu Mar 15 15:15:25 2018 +0300 +++ b/njs/njscript.c Wed Mar 21 17:33:12 2018 +0300 @@ -20,13 +20,17 @@ #include #include #include +#include #include From xeioex at nginx.com Thu Mar 22 12:05:26 2018 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Thu, 22 Mar 2018 12:05:26 +0000 Subject: [njs] Fixed function frame alignment on 32bits platforms. Message-ID: details: http://hg.nginx.org/njs/rev/529c376ec5e2 branches: changeset: 466:529c376ec5e2 user: Dmitry Volyntsev date: Thu Mar 22 15:05:06 2018 +0300 description: Fixed function frame alignment on 32bits platforms. diffstat: njs/njs_function.c | 8 ++++++++ njs/test/njs_unit_test.c | 3 +++ 2 files changed, 11 insertions(+), 0 deletions(-) diffs (31 lines): diff -r 750f7c6f071c -r 529c376ec5e2 njs/njs_function.c --- a/njs/njs_function.c Wed Mar 21 17:33:13 2018 +0300 +++ b/njs/njs_function.c Thu Mar 22 15:05:06 2018 +0300 @@ -237,6 +237,14 @@ njs_function_frame_alloc(njs_vm_t *vm, s size_t spare_size, chunk_size; njs_native_frame_t *frame; + /* + * The size value must be aligned to njs_value_t because vm->top_frame + * may point to frame->free and vm->top_frame is used as a base pointer + * in njs_vm_continuation() which is expected to return pointers aligned + * to njs_value_t. + */ + size = nxt_align_size(size, sizeof(njs_value_t)); + spare_size = vm->top_frame->free_size; if (nxt_fast_path(size <= spare_size)) { diff -r 750f7c6f071c -r 529c376ec5e2 njs/test/njs_unit_test.c --- a/njs/test/njs_unit_test.c Wed Mar 21 17:33:13 2018 +0300 +++ b/njs/test/njs_unit_test.c Thu Mar 22 15:05:06 2018 +0300 @@ -2975,6 +2975,9 @@ static njs_unit_test_t njs_test[] = "a.forEach(function(v, i, a) { a[i+3] = a.length }); a"), nxt_string("1,2,3,3,4,5") }, + { nxt_string("function f() { var c; [1].forEach(function(v) { c })}; f()"), + nxt_string("undefined") }, + { nxt_string("var a = [1,2,3]; var s = { sum: 0 };" "[].forEach.call(a, function(v, i, a) { this.sum += v }, s);" "s.sum"), From xeioex at nginx.com Thu Mar 22 12:05:26 2018 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Thu, 22 Mar 2018 12:05:26 +0000 Subject: [njs] Fixed njs_vm_t struct alignment on 32bits platforms. Message-ID: details: http://hg.nginx.org/njs/rev/f58d5003ba33 branches: changeset: 467:f58d5003ba33 user: Dmitry Volyntsev date: Thu Mar 22 15:05:08 2018 +0300 description: Fixed njs_vm_t struct alignment on 32bits platforms. diffstat: njs/njscript.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diffs (12 lines): diff -r 529c376ec5e2 -r f58d5003ba33 njs/njscript.c --- a/njs/njscript.c Thu Mar 22 15:05:06 2018 +0300 +++ b/njs/njscript.c Thu Mar 22 15:05:08 2018 +0300 @@ -337,7 +337,7 @@ njs_vm_clone(njs_vm_t *vm, njs_external_ return NULL; } - nvm = nxt_mem_cache_zalloc(nmcp, sizeof(njs_vm_t)); + nvm = nxt_mem_cache_zalign(nmcp, sizeof(njs_value_t), sizeof(njs_vm_t)); if (nxt_fast_path(nvm != NULL)) { nvm->mem_cache_pool = nmcp; From mdounin at mdounin.ru Thu Mar 22 13:38:32 2018 From: mdounin at mdounin.ru (Maxim Dounin) Date: Thu, 22 Mar 2018 13:38:32 +0000 Subject: [nginx] Version bump. Message-ID: details: http://hg.nginx.org/nginx/rev/3f1c5ff0d7fb branches: changeset: 7245:3f1c5ff0d7fb user: Maxim Dounin date: Thu Mar 22 15:55:52 2018 +0300 description: Version bump. diffstat: src/core/nginx.h | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diffs (14 lines): diff --git a/src/core/nginx.h b/src/core/nginx.h --- a/src/core/nginx.h +++ b/src/core/nginx.h @@ -9,8 +9,8 @@ #define _NGINX_H_INCLUDED_ -#define nginx_version 1013010 -#define NGINX_VERSION "1.13.10" +#define nginx_version 1013011 +#define NGINX_VERSION "1.13.11" #define NGINX_VER "nginx/" NGINX_VERSION #ifdef NGX_BUILD From mdounin at mdounin.ru Thu Mar 22 13:38:34 2018 From: mdounin at mdounin.ru (Maxim Dounin) Date: Thu, 22 Mar 2018 13:38:34 +0000 Subject: [nginx] Configure: fixed static compilation with OpenSSL 1.1.1. Message-ID: details: http://hg.nginx.org/nginx/rev/04ebf29eaf5b branches: changeset: 7246:04ebf29eaf5b user: Maxim Dounin date: Thu Mar 22 15:55:57 2018 +0300 description: Configure: fixed static compilation with OpenSSL 1.1.1. OpenSSL now uses pthread_atfork(), and this requires -lpthread on Linux to compile. Introduced NGX_LIBPTHREAD to add it as appropriate, similar to existing NGX_LIBDL. diffstat: auto/lib/openssl/conf | 21 ++++++++++++++------- auto/threads | 1 + auto/unix | 1 + 3 files changed, 16 insertions(+), 7 deletions(-) diffs (88 lines): diff --git a/auto/lib/openssl/conf b/auto/lib/openssl/conf --- a/auto/lib/openssl/conf +++ b/auto/lib/openssl/conf @@ -41,6 +41,7 @@ if [ $OPENSSL != NONE ]; then CORE_LIBS="$CORE_LIBS $OPENSSL/.openssl/lib/libssl.a" CORE_LIBS="$CORE_LIBS $OPENSSL/.openssl/lib/libcrypto.a" CORE_LIBS="$CORE_LIBS $NGX_LIBDL" + CORE_LIBS="$CORE_LIBS $NGX_LIBPTHREAD" if [ "$NGX_PLATFORM" = win32 ]; then CORE_LIBS="$CORE_LIBS -lgdi32 -lcrypt32 -lws2_32" @@ -59,7 +60,7 @@ else ngx_feature_run=no ngx_feature_incs="#include " ngx_feature_path= - ngx_feature_libs="-lssl -lcrypto $NGX_LIBDL" + ngx_feature_libs="-lssl -lcrypto $NGX_LIBDL $NGX_LIBPTHREAD" ngx_feature_test="SSL_CTX_set_options(NULL, 0)" . auto/feature @@ -71,11 +72,13 @@ else ngx_feature_path="/usr/local/include" if [ $NGX_RPATH = YES ]; then - ngx_feature_libs="-R/usr/local/lib -L/usr/local/lib -lssl -lcrypto $NGX_LIBDL" + ngx_feature_libs="-R/usr/local/lib -L/usr/local/lib -lssl -lcrypto" else - ngx_feature_libs="-L/usr/local/lib -lssl -lcrypto $NGX_LIBDL" + ngx_feature_libs="-L/usr/local/lib -lssl -lcrypto" fi + ngx_feature_libs="$ngx_feature_libs $NGX_LIBDL $NGX_LIBPTHREAD" + . auto/feature fi @@ -87,11 +90,13 @@ else ngx_feature_path="/usr/pkg/include" if [ $NGX_RPATH = YES ]; then - ngx_feature_libs="-R/usr/pkg/lib -L/usr/pkg/lib -lssl -lcrypto $NGX_LIBDL" + ngx_feature_libs="-R/usr/pkg/lib -L/usr/pkg/lib -lssl -lcrypto" else - ngx_feature_libs="-L/usr/pkg/lib -lssl -lcrypto $NGX_LIBDL" + ngx_feature_libs="-L/usr/pkg/lib -lssl -lcrypto" fi + ngx_feature_libs="$ngx_feature_libs $NGX_LIBDL $NGX_LIBPTHREAD" + . auto/feature fi @@ -103,11 +108,13 @@ else ngx_feature_path="/opt/local/include" if [ $NGX_RPATH = YES ]; then - ngx_feature_libs="-R/opt/local/lib -L/opt/local/lib -lssl -lcrypto $NGX_LIBDL" + ngx_feature_libs="-R/opt/local/lib -L/opt/local/lib -lssl -lcrypto" else - ngx_feature_libs="-L/opt/local/lib -lssl -lcrypto $NGX_LIBDL" + ngx_feature_libs="-L/opt/local/lib -lssl -lcrypto" fi + ngx_feature_libs="$ngx_feature_libs $NGX_LIBDL $NGX_LIBPTHREAD" + . auto/feature fi diff --git a/auto/threads b/auto/threads --- a/auto/threads +++ b/auto/threads @@ -17,4 +17,5 @@ END CORE_DEPS="$CORE_DEPS $THREAD_POOL_DEPS" CORE_SRCS="$CORE_SRCS $THREAD_POOL_SRCS" CORE_LIBS="$CORE_LIBS -lpthread" + NGX_LIBPTHREAD="-lpthread" fi diff --git a/auto/unix b/auto/unix --- a/auto/unix +++ b/auto/unix @@ -901,6 +901,7 @@ if [ $ngx_found = no ]; then if [ $ngx_found = yes ]; then CORE_LIBS="$CORE_LIBS -lpthread" + NGX_LIBPTHREAD="-lpthread" fi fi From mdounin at mdounin.ru Thu Mar 22 13:38:35 2018 From: mdounin at mdounin.ru (Maxim Dounin) Date: Thu, 22 Mar 2018 13:38:35 +0000 Subject: [nginx] Configure: restored "no-threads" in OpenSSL builds. Message-ID: details: http://hg.nginx.org/nginx/rev/b329c0ab1a48 branches: changeset: 7247:b329c0ab1a48 user: Maxim Dounin date: Thu Mar 22 15:56:07 2018 +0300 description: Configure: restored "no-threads" in OpenSSL builds. This was previously used, but was incorrectly removed in 83d54192e97b while removing old threads remnants. Instead of using it conditionally when threads are not used, we now set in unconditionally, as even with thread pools enabled we never call OpenSSL functions in threads. This fixes resulting binary when using --with-openssl with OpenSSL 1.1.0+ and without -lpthread linked (notably on FreeBSD without PCRE). diffstat: auto/lib/openssl/make | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diffs (12 lines): diff --git a/auto/lib/openssl/make b/auto/lib/openssl/make --- a/auto/lib/openssl/make +++ b/auto/lib/openssl/make @@ -51,7 +51,7 @@ END $OPENSSL/.openssl/include/openssl/ssl.h: $NGX_MAKEFILE cd $OPENSSL \\ && if [ -f Makefile ]; then \$(MAKE) clean; fi \\ - && ./config --prefix=$ngx_prefix no-shared $OPENSSL_OPT \\ + && ./config --prefix=$ngx_prefix no-shared no-threads $OPENSSL_OPT \\ && \$(MAKE) \\ && \$(MAKE) install_sw LIBDIR=lib From ru at nginx.com Thu Mar 22 15:14:52 2018 From: ru at nginx.com (Ruslan Ermilov) Date: Thu, 22 Mar 2018 15:14:52 +0000 Subject: [nginx] Improved code readability (closes #1512). Message-ID: details: http://hg.nginx.org/nginx/rev/fdd6a070d518 branches: changeset: 7248:fdd6a070d518 user: Ruslan Ermilov date: Thu Mar 22 18:13:33 2018 +0300 description: Improved code readability (closes #1512). diffstat: src/core/nginx.c | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diffs (14 lines): diff -r b329c0ab1a48 -r fdd6a070d518 src/core/nginx.c --- a/src/core/nginx.c Thu Mar 22 15:56:07 2018 +0300 +++ b/src/core/nginx.c Thu Mar 22 18:13:33 2018 +0300 @@ -985,8 +985,8 @@ ngx_process_options(ngx_cycle_t *cycle) p--) { if (ngx_path_separator(*p)) { - cycle->conf_prefix.len = p - ngx_cycle->conf_file.data + 1; - cycle->conf_prefix.data = ngx_cycle->conf_file.data; + cycle->conf_prefix.len = p - cycle->conf_file.data + 1; + cycle->conf_prefix.data = cycle->conf_file.data; break; } } From mdounin at mdounin.ru Thu Mar 22 18:07:38 2018 From: mdounin at mdounin.ru (Maxim Dounin) Date: Thu, 22 Mar 2018 18:07:38 +0000 Subject: [nginx] gRPC: fixed possible sign extension of error and setting_value. Message-ID: details: http://hg.nginx.org/nginx/rev/070c972336c4 branches: changeset: 7249:070c972336c4 user: Maxim Dounin date: Thu Mar 22 19:26:25 2018 +0300 description: gRPC: fixed possible sign extension of error and setting_value. All cases are harmless and should not happen on valid values, though can result in bad values being shown incorrectly in logs. Found by Coverity (CID 1430311, 1430312, 1430313). diffstat: src/http/modules/ngx_http_grpc_module.c | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-) diffs (30 lines): diff --git a/src/http/modules/ngx_http_grpc_module.c b/src/http/modules/ngx_http_grpc_module.c --- a/src/http/modules/ngx_http_grpc_module.c +++ b/src/http/modules/ngx_http_grpc_module.c @@ -3212,7 +3212,7 @@ ngx_http_grpc_parse_rst_stream(ngx_http_ switch (state) { case sw_start: - ctx->error = ch << 24; + ctx->error = (ngx_uint_t) ch << 24; state = sw_error_2; break; @@ -3325,7 +3325,7 @@ ngx_http_grpc_parse_goaway(ngx_http_requ break; case sw_error: - ctx->error = ch << 24; + ctx->error = (ngx_uint_t) ch << 24; state = sw_error_2; break; @@ -3555,7 +3555,7 @@ ngx_http_grpc_parse_settings(ngx_http_re break; case sw_value: - ctx->setting_value = ch << 24; + ctx->setting_value = (ngx_uint_t) ch << 24; state = sw_value_2; break; From xeioex at nginx.com Fri Mar 23 10:53:51 2018 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Fri, 23 Mar 2018 10:53:51 +0000 Subject: [njs] Fixed copying of a garbage value. Message-ID: details: http://hg.nginx.org/njs/rev/77e675beae48 branches: changeset: 468:77e675beae48 user: Dmitry Volyntsev date: Fri Mar 23 13:53:27 2018 +0300 description: Fixed copying of a garbage value. Found by Clang Static Analyzer. Additionally, unnecessary body_arg.length zeroing is removed. diffstat: nginx/ngx_http_js_module.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diffs (12 lines): diff -r f58d5003ba33 -r 77e675beae48 nginx/ngx_http_js_module.c --- a/nginx/ngx_http_js_module.c Thu Mar 22 15:05:08 2018 +0300 +++ b/nginx/ngx_http_js_module.c Fri Mar 23 13:53:27 2018 +0300 @@ -1503,7 +1503,7 @@ ngx_http_js_ext_subrequest(njs_vm_t *vm, method = 0; args_arg.length = 0; - body_arg.length = 0; + args_arg.start = NULL; has_body = 0; if (nargs > 2 && !njs_value_is_function(njs_argument(args, 2))) { From arut at nginx.com Fri Mar 23 11:16:49 2018 From: arut at nginx.com (Roman Arutyunyan) Date: Fri, 23 Mar 2018 11:16:49 +0000 Subject: [nginx] Stream: set action before each recv/send while proxying. Message-ID: details: http://hg.nginx.org/nginx/rev/ec4d95eed062 branches: changeset: 7250:ec4d95eed062 user: Roman Arutyunyan date: Thu Mar 22 18:43:49 2018 +0300 description: Stream: set action before each recv/send while proxying. Now it's clear from log error message if the error occurred on client or upstream side. diffstat: src/stream/ngx_stream_proxy_module.c | 13 +++++++++++-- 1 files changed, 11 insertions(+), 2 deletions(-) diffs (65 lines): diff -r 070c972336c4 -r ec4d95eed062 src/stream/ngx_stream_proxy_module.c --- a/src/stream/ngx_stream_proxy_module.c Thu Mar 22 19:26:25 2018 +0300 +++ b/src/stream/ngx_stream_proxy_module.c Thu Mar 22 18:43:49 2018 +0300 @@ -801,8 +801,6 @@ ngx_stream_proxy_init_upstream(ngx_strea NGX_STREAM_UPSTREAM_NOTIFY_CONNECT); } - c->log->action = "proxying connection"; - if (u->upstream_buf.start == NULL) { p = ngx_pnalloc(c->pool, pscf->buffer_size); if (p == NULL) { @@ -1449,6 +1447,7 @@ static void ngx_stream_proxy_process(ngx_stream_session_t *s, ngx_uint_t from_upstream, ngx_uint_t do_write) { + char *recv_action, *send_action; off_t *received, limit; size_t size, limit_rate; ssize_t n; @@ -1492,6 +1491,8 @@ ngx_stream_proxy_process(ngx_stream_sess received = &u->received; out = &u->downstream_out; busy = &u->downstream_busy; + recv_action = "proxying and reading from upstream"; + send_action = "proxying and sending to client"; } else { src = c; @@ -1501,6 +1502,8 @@ ngx_stream_proxy_process(ngx_stream_sess received = &s->received; out = &u->upstream_out; busy = &u->upstream_busy; + recv_action = "proxying and reading from client"; + send_action = "proxying and sending to upstream"; } for ( ;; ) { @@ -1508,6 +1511,8 @@ ngx_stream_proxy_process(ngx_stream_sess if (do_write && dst) { if (*out || *busy || dst->buffered) { + c->log->action = send_action; + rc = ngx_stream_top_filter(s, *out, from_upstream); if (rc == NGX_ERROR) { @@ -1551,6 +1556,8 @@ ngx_stream_proxy_process(ngx_stream_sess } } + c->log->action = recv_action; + n = src->recv(src, b->last, size); if (n == NGX_AGAIN) { @@ -1620,6 +1627,8 @@ ngx_stream_proxy_process(ngx_stream_sess break; } + c->log->action = "proxying connection"; + if (src->read->eof && dst && (dst->read->eof || !dst->buffered)) { handler = c->log->handler; c->log->handler = NULL; From xeioex at nginx.com Fri Mar 23 11:22:23 2018 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Fri, 23 Mar 2018 11:22:23 +0000 Subject: [njs] Fixed external_objects cloning. Message-ID: details: http://hg.nginx.org/njs/rev/567cdbcee5ce branches: changeset: 469:567cdbcee5ce user: Dmitry Volyntsev date: Fri Mar 23 14:02:22 2018 +0300 description: Fixed external_objects cloning. diffstat: njs/njscript.c | 15 +++++++++------ 1 files changed, 9 insertions(+), 6 deletions(-) diffs (39 lines): diff -r 77e675beae48 -r 567cdbcee5ce njs/njscript.c --- a/njs/njscript.c Fri Mar 23 13:53:27 2018 +0300 +++ b/njs/njscript.c Fri Mar 23 14:02:22 2018 +0300 @@ -323,6 +323,7 @@ njs_vm_clone(njs_vm_t *vm, njs_external_ njs_vm_t *nvm; uint32_t items; nxt_int_t ret; + nxt_array_t *externals; nxt_mem_cache_pool_t *nmcp; nxt_thread_log_debug("CLONE:"); @@ -352,19 +353,21 @@ njs_vm_clone(njs_vm_t *vm, njs_external_ nvm->external_prototypes_hash = vm->external_prototypes_hash; items = vm->external_objects->items; - nvm->external_objects = nxt_array_create(items + 4, sizeof(void *), - &njs_array_mem_proto, - vm->mem_cache_pool); - if (nxt_slow_path(vm->external_objects == NULL)) { + externals = nxt_array_create(items + 4, sizeof(void *), + &njs_array_mem_proto, nvm->mem_cache_pool); + + if (nxt_slow_path(externals == NULL)) { return NULL; } if (items > 0) { - memcpy(nvm->external_objects->start, vm->external_objects->start, + memcpy(externals->start, vm->external_objects->start, items * sizeof(void *)); - vm->external_objects->items = items; + externals->items = items; } + nvm->external_objects = externals; + nvm->ops = vm->ops; nvm->current = vm->current; From xeioex at nginx.com Fri Mar 23 11:22:23 2018 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Fri, 23 Mar 2018 11:22:23 +0000 Subject: [njs] Fixed njs_object_hash_create() for 0 elements. Message-ID: details: http://hg.nginx.org/njs/rev/9dfa2e1892d0 branches: changeset: 470:9dfa2e1892d0 user: Dmitry Volyntsev date: Fri Mar 23 14:03:09 2018 +0300 description: Fixed njs_object_hash_create() for 0 elements. diffstat: njs/njs_object.c | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diffs (21 lines): diff -r 567cdbcee5ce -r 9dfa2e1892d0 njs/njs_object.c --- a/njs/njs_object.c Fri Mar 23 14:02:22 2018 +0300 +++ b/njs/njs_object.c Fri Mar 23 14:03:09 2018 +0300 @@ -111,7 +111,7 @@ njs_object_hash_create(njs_vm_t *vm, nxt lhq.proto = &njs_object_hash_proto; lhq.pool = vm->mem_cache_pool; - do { + while (n != 0) { njs_string_get(&prop->name, &lhq.key); lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length); lhq.value = (void *) prop; @@ -123,7 +123,7 @@ njs_object_hash_create(njs_vm_t *vm, nxt prop++; n--; - } while (n != 0); + } return NXT_OK; } From jeppojeps at gmail.com Fri Mar 23 17:01:58 2018 From: jeppojeps at gmail.com (Antonio Nappa) Date: Fri, 23 Mar 2018 18:01:58 +0100 Subject: Conditional Logging Message-ID: Hello, I am doing experiments on a module with the ngx_http_log_loc_conf_t structure by setting the off flag to 1. I have defined a custom log format and use it with the access_log directive. I noticed that if there is a 302 even if lcf->off is set to 1 I get some output in the log file. Do you have any clues why this is happening? Thanks, Antonio -------------- next part -------------- An HTML attachment was scrubbed... URL: From vl at nginx.com Mon Mar 26 19:58:47 2018 From: vl at nginx.com (Vladimir Homutov) Date: Mon, 26 Mar 2018 19:58:47 +0000 Subject: [nginx] Core: added processing of version 2 of the PROXY protocol. Message-ID: details: http://hg.nginx.org/nginx/rev/416953ef0428 branches: changeset: 7251:416953ef0428 user: Vladimir Homutov date: Thu Mar 22 15:55:28 2018 +0300 description: Core: added processing of version 2 of the PROXY protocol. The protocol used on inbound connection is auto-detected and corresponding parser is used to extract passed addresses. TLV parameters are ignored. The maximum supported size of PROXY protocol header is 107 bytes (similar to version 1). diffstat: src/core/ngx_proxy_protocol.c | 192 ++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 192 insertions(+), 0 deletions(-) diffs (213 lines): diff -r ec4d95eed062 -r 416953ef0428 src/core/ngx_proxy_protocol.c --- a/src/core/ngx_proxy_protocol.c Thu Mar 22 18:43:49 2018 +0300 +++ b/src/core/ngx_proxy_protocol.c Thu Mar 22 15:55:28 2018 +0300 @@ -9,6 +9,57 @@ #include +#define NGX_PP_V2_SIGLEN 12 +#define NGX_PP_V2_CMD_PROXY 1 +#define NGX_PP_V2_STREAM 1 + +#define NGX_PP_V2_AF_UNSPEC 0 +#define NGX_PP_V2_AF_INET 1 +#define NGX_PP_V2_AF_INET6 2 + + +#define ngx_pp_v2_get_u16(p) \ + ( ((uint16_t) ((u_char *) (p))[0] << 8) \ + + ( ((u_char *) (p))[1]) ) + + +typedef struct { + u_char signature[NGX_PP_V2_SIGLEN]; + u_char ver_cmd; + u_char fam_transp; + u_char len[2]; +} ngx_pp_v2_header_t; + + +typedef struct { + u_char src[4]; + u_char dst[4]; + u_char sport[2]; + u_char dport[2]; +} ngx_pp_v2_inet_addrs_t; + + +typedef struct { + u_char src[16]; + u_char dst[16]; + u_char sport[2]; + u_char dport[2]; +} ngx_pp_v2_inet6_addrs_t; + + +typedef union { + ngx_pp_v2_inet_addrs_t inet; + ngx_pp_v2_inet6_addrs_t inet6; +} ngx_pp_v2_addrs_t; + + +static u_char *ngx_proxy_protocol_v2_read(ngx_connection_t *c, u_char *buf, + u_char *last); + +static const u_char ngx_pp_v2_signature[NGX_PP_V2_SIGLEN] = + { 0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A }; + + u_char * ngx_proxy_protocol_read(ngx_connection_t *c, u_char *buf, u_char *last) { @@ -19,6 +70,12 @@ ngx_proxy_protocol_read(ngx_connection_t p = buf; len = last - buf; + if (len >= sizeof(ngx_pp_v2_header_t) + && memcmp(p, ngx_pp_v2_signature, NGX_PP_V2_SIGLEN) == 0) + { + return ngx_proxy_protocol_v2_read(c, buf, last); + } + if (len < 8 || ngx_strncmp(p, "PROXY ", 6) != 0) { goto invalid; } @@ -166,3 +223,138 @@ ngx_proxy_protocol_write(ngx_connection_ return ngx_slprintf(buf, last, " %ui %ui" CRLF, port, lport); } + + +static u_char * +ngx_proxy_protocol_v2_read(ngx_connection_t *c, u_char *buf, u_char *last) +{ + u_char *end; + size_t len; + socklen_t socklen; + ngx_str_t *name; + ngx_uint_t ver, cmd, family, transport; + ngx_sockaddr_t sockaddr; + ngx_pp_v2_addrs_t *addrs; + ngx_pp_v2_header_t *hdr; + + hdr = (ngx_pp_v2_header_t *) buf; + + buf += sizeof(ngx_pp_v2_header_t); + + ver = hdr->ver_cmd >> 4; + + if (ver != 2) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "unsupported PROXY protocol version: %ui", ver); + return NULL; + } + + len = ngx_pp_v2_get_u16(hdr->len); + + if ((size_t) (last - buf) < len) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, "header is too large"); + return NULL; + } + + end = buf + len; + + cmd = hdr->ver_cmd & 0x0F; + + if (cmd != NGX_PP_V2_CMD_PROXY) { + ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0, + "PROXY protocol v2 unsupported cmd 0x%xi", cmd); + return end; + } + + transport = hdr->fam_transp & 0x0F; + + if (transport != NGX_PP_V2_STREAM) { + ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0, + "PROXY protocol v2 unsupported transport 0x%xi", + transport); + return end; + } + + family = hdr->fam_transp >> 4; + + addrs = (ngx_pp_v2_addrs_t *) buf; + + switch (family) { + + case NGX_PP_V2_AF_UNSPEC: + ngx_log_debug0(NGX_LOG_DEBUG_CORE, c->log, 0, + "PROXY protocol v2 AF_UNSPEC ignored"); + return end; + + case NGX_PP_V2_AF_INET: + + if ((size_t) (end - buf) < sizeof(ngx_pp_v2_inet_addrs_t)) { + return NULL; + } + + sockaddr.sockaddr_in.sin_family = AF_INET; + sockaddr.sockaddr_in.sin_port = 0; + memcpy(&sockaddr.sockaddr_in.sin_addr, addrs->inet.src, 4); + + c->proxy_protocol_port = ngx_pp_v2_get_u16(addrs->inet.sport); + + socklen = sizeof(struct sockaddr_in); + + buf += sizeof(ngx_pp_v2_inet_addrs_t); + + break; + +#if (NGX_HAVE_INET6) + + case NGX_PP_V2_AF_INET6: + + if ((size_t) (end - buf) < sizeof(ngx_pp_v2_inet6_addrs_t)) { + return NULL; + } + + sockaddr.sockaddr_in6.sin6_family = AF_INET6; + sockaddr.sockaddr_in6.sin6_port = 0; + memcpy(&sockaddr.sockaddr_in6.sin6_addr, addrs->inet6.src, 16); + + c->proxy_protocol_port = ngx_pp_v2_get_u16(addrs->inet6.sport); + + socklen = sizeof(struct sockaddr_in6); + + buf += sizeof(ngx_pp_v2_inet6_addrs_t); + + break; + +#endif + + default: + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0, + "PROXY_protocol v2 unsupported address family " + "0x%xi", family); + return end; + } + + name = &c->proxy_protocol_addr; + + name->data = ngx_pnalloc(c->pool, NGX_SOCKADDR_STRLEN); + if (name->data == NULL) { + return NULL; + } + + name->len = ngx_sock_ntop(&sockaddr.sockaddr, socklen, name->data, + NGX_SOCKADDR_STRLEN, 0); + if (name->len == 0) { + return NULL; + } + + ngx_log_debug2(NGX_LOG_DEBUG_CORE, c->log, 0, + "PROXY protocol v2 address: %V %i", name, + (ngx_int_t) c->proxy_protocol_port); + + if (buf < end) { + ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0, + "PROXY protocol v2 %z bytes tlv ignored", end - buf); + } + + return end; +} From xeioex at nginx.com Tue Mar 27 16:12:47 2018 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Tue, 27 Mar 2018 16:12:47 +0000 Subject: [njs] Short exception macros' names. Message-ID: details: http://hg.nginx.org/njs/rev/ecf693afd698 branches: changeset: 471:ecf693afd698 user: Dmitry Volyntsev date: Tue Mar 27 19:11:04 2018 +0300 description: Short exception macros' names. diffstat: njs/njs_array.c | 8 ++-- njs/njs_boolean.c | 8 ++-- njs/njs_builtin.c | 6 +- njs/njs_date.c | 4 +- njs/njs_error.c | 28 +++++++------- njs/njs_error.h | 24 ++++++------ njs/njs_event.c | 4 +- njs/njs_fs.c | 88 ++++++++++++++++++++++---------------------- njs/njs_function.c | 14 +++---- njs/njs_generator.c | 4 +- njs/njs_json.c | 45 +++++++++++----------- njs/njs_module.c | 6 +- njs/njs_number.c | 10 ++-- njs/njs_object.c | 39 ++++++++----------- njs/njs_parser.c | 13 +++--- njs/njs_parser_expression.c | 14 ++++--- njs/njs_regexp.c | 15 +++---- njs/njs_string.c | 23 +++++------ njs/njs_time.c | 11 ++--- njs/njs_variable.c | 3 +- njs/njs_vm.c | 41 +++++++++----------- 21 files changed, 196 insertions(+), 212 deletions(-) diffs (truncated from 1446 to 1000 lines): diff -r 9dfa2e1892d0 -r ecf693afd698 njs/njs_array.c --- a/njs/njs_array.c Fri Mar 23 14:03:09 2018 +0300 +++ b/njs/njs_array.c Tue Mar 27 19:11:04 2018 +0300 @@ -249,7 +249,7 @@ njs_array_constructor(njs_vm_t *vm, njs_ size = (uint32_t) num; if ((double) size != num) { - njs_exception_range_error(vm, NULL, NULL); + njs_range_error(vm, NULL, NULL); return NXT_ERROR; } @@ -1715,7 +1715,7 @@ njs_array_prototype_reduce(njs_vm_t *vm, n = njs_array_iterator_index(array, iter); if (n == NJS_ARRAY_INVALID_INDEX) { - njs_exception_type_error(vm, "invalid index", NULL); + njs_type_error(vm, "invalid index", NULL); return NXT_ERROR; } @@ -1776,7 +1776,7 @@ njs_array_iterator_args(njs_vm_t *vm, nj return NXT_OK; } - njs_exception_type_error(vm, "unexpected iterator arguments", NULL); + njs_type_error(vm, "unexpected iterator arguments", NULL); return NXT_ERROR; } @@ -1850,7 +1850,7 @@ njs_array_prototype_reduce_right(njs_vm_ n = njs_array_reduce_right_index(array, iter); if (n == NJS_ARRAY_INVALID_INDEX) { - njs_exception_type_error(vm, "invalid index", NULL); + njs_type_error(vm, "invalid index", NULL); return NXT_ERROR; } diff -r 9dfa2e1892d0 -r ecf693afd698 njs/njs_boolean.c --- a/njs/njs_boolean.c Fri Mar 23 14:03:09 2018 +0300 +++ b/njs/njs_boolean.c Tue Mar 27 19:11:04 2018 +0300 @@ -99,8 +99,8 @@ njs_boolean_prototype_value_of(njs_vm_t value = &value->data.u.object_value->value; } else { - njs_exception_type_error(vm, "unexpected value type:%s", - njs_type_string(value->type)); + njs_type_error(vm, "unexpected value type:%s", + njs_type_string(value->type)); return NXT_ERROR; } } @@ -125,8 +125,8 @@ njs_boolean_prototype_to_string(njs_vm_t value = &value->data.u.object_value->value; } else { - njs_exception_type_error(vm, "unexpected value type:%s", - njs_type_string(value->type)); + njs_type_error(vm, "unexpected value type:%s", + njs_type_string(value->type)); return NXT_ERROR; } } diff -r 9dfa2e1892d0 -r ecf693afd698 njs/njs_builtin.c --- a/njs/njs_builtin.c Fri Mar 23 14:03:09 2018 +0300 +++ b/njs/njs_builtin.c Tue Mar 27 19:11:04 2018 +0300 @@ -75,7 +75,7 @@ const njs_object_init_t *njs_prototype_ &njs_eval_error_prototype_init, &njs_internal_error_prototype_init, &njs_range_error_prototype_init, - &njs_ref_error_prototype_init, + &njs_reference_error_prototype_init, &njs_syntax_error_prototype_init, &njs_type_error_prototype_init, &njs_uri_error_prototype_init, @@ -95,7 +95,7 @@ const njs_object_init_t *njs_construc &njs_eval_error_constructor_init, &njs_internal_error_constructor_init, &njs_range_error_constructor_init, - &njs_ref_error_constructor_init, + &njs_reference_error_constructor_init, &njs_syntax_error_constructor_init, &njs_type_error_constructor_init, &njs_uri_error_constructor_init, @@ -220,7 +220,7 @@ njs_builtin_objects_create(njs_vm_t *vm) { NJS_SKIP_ARG, NJS_STRING_ARG } }, { njs_range_error_constructor, { NJS_SKIP_ARG, NJS_STRING_ARG } }, - { njs_ref_error_constructor, { NJS_SKIP_ARG, NJS_STRING_ARG } }, + { njs_reference_error_constructor, { NJS_SKIP_ARG, NJS_STRING_ARG } }, { njs_syntax_error_constructor, { NJS_SKIP_ARG, NJS_STRING_ARG } }, { njs_type_error_constructor, { NJS_SKIP_ARG, NJS_STRING_ARG } }, diff -r 9dfa2e1892d0 -r ecf693afd698 njs/njs_date.c --- a/njs/njs_date.c Fri Mar 23 14:03:09 2018 +0300 +++ b/njs/njs_date.c Tue Mar 27 19:11:04 2018 +0300 @@ -1063,7 +1063,7 @@ njs_date_prototype_to_iso_string(njs_vm_ return njs_string_new(vm, &vm->retval, buf, size, size); } - njs_exception_range_error(vm, NULL, NULL); + njs_range_error(vm, NULL, NULL); return NXT_ERROR; } @@ -1911,7 +1911,7 @@ njs_date_prototype_to_json(njs_vm_t *vm, } } - njs_exception_type_error(vm, "'this' argument is not an object", NULL); + njs_type_error(vm, "'this' argument is not an object", NULL); return NXT_ERROR; } diff -r 9dfa2e1892d0 -r ecf693afd698 njs/njs_error.c --- a/njs/njs_error.c Fri Mar 23 14:03:09 2018 +0300 +++ b/njs/njs_error.c Tue Mar 27 19:11:04 2018 +0300 @@ -68,7 +68,7 @@ njs_exception_error_create(njs_vm_t *vm, memory_error: - njs_exception_memory_error(vm); + njs_memory_error(vm); } @@ -154,7 +154,7 @@ njs_error_create(njs_vm_t *vm, njs_value error = njs_error_alloc(vm, type, NULL, value); if (nxt_slow_path(error == NULL)) { - njs_exception_memory_error(vm); + njs_memory_error(vm); return NXT_ERROR; } @@ -327,14 +327,14 @@ const njs_object_init_t njs_range_error njs_ret_t -njs_ref_error_constructor(njs_vm_t *vm, njs_value_t *args, +njs_reference_error_constructor(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, njs_index_t unused) { return njs_error_create(vm, args, nargs, NJS_OBJECT_REF_ERROR); } -static const njs_object_prop_t njs_ref_error_constructor_properties[] = +static const njs_object_prop_t njs_reference_error_constructor_properties[] = { /* ReferenceError.name == "ReferenceError". */ { @@ -359,10 +359,10 @@ static const njs_object_prop_t njs_ref_ }; -const njs_object_init_t njs_ref_error_constructor_init = { +const njs_object_init_t njs_reference_error_constructor_init = { nxt_string("ReferenceError"), - njs_ref_error_constructor_properties, - nxt_nitems(njs_ref_error_constructor_properties), + njs_reference_error_constructor_properties, + nxt_nitems(njs_reference_error_constructor_properties), }; @@ -515,7 +515,7 @@ njs_set_memory_error(njs_vm_t *vm, njs_v void -njs_exception_memory_error(njs_vm_t *vm) +njs_memory_error(njs_vm_t *vm) { njs_set_memory_error(vm, &vm->retval); } @@ -612,7 +612,7 @@ njs_error_prototype_to_string(njs_vm_t * static const njs_value_t default_name = njs_string("Error"); if (nargs < 1 || !njs_is_object(&args[0])) { - njs_exception_type_error(vm, "'this' argument is not an object", NULL); + njs_type_error(vm, "'this' argument is not an object", NULL); return NXT_ERROR; } @@ -668,7 +668,7 @@ njs_error_prototype_to_string(njs_vm_t * return NJS_OK; } - njs_exception_memory_error(vm); + njs_memory_error(vm); return NJS_ERROR; } @@ -786,7 +786,7 @@ const njs_object_init_t njs_range_error }; -static const njs_object_prop_t njs_ref_error_prototype_properties[] = +static const njs_object_prop_t njs_reference_error_prototype_properties[] = { { .type = NJS_PROPERTY, @@ -796,10 +796,10 @@ static const njs_object_prop_t njs_ref_ }; -const njs_object_init_t njs_ref_error_prototype_init = { +const njs_object_init_t njs_reference_error_prototype_init = { nxt_string("ReferenceError"), - njs_ref_error_prototype_properties, - nxt_nitems(njs_ref_error_prototype_properties), + njs_reference_error_prototype_properties, + nxt_nitems(njs_reference_error_prototype_properties), }; diff -r 9dfa2e1892d0 -r ecf693afd698 njs/njs_error.h --- a/njs/njs_error.h Fri Mar 23 14:03:09 2018 +0300 +++ b/njs/njs_error.h Tue Mar 27 19:11:04 2018 +0300 @@ -8,27 +8,27 @@ #define _NJS_ERROR_H_INCLUDED_ -#define njs_exception_error(vm, fmt, ...) \ +#define njs_error(vm, fmt, ...) \ njs_exception_error_create(vm, NJS_OBJECT_ERROR, fmt, __VA_ARGS__) -#define njs_exception_eval_error(vm, fmt, ...) \ +#define njs_eval_error(vm, fmt, ...) \ njs_exception_error_create(vm, NJS_OBJECT_EVAL_ERROR, fmt, __VA_ARGS__) -#define njs_exception_internal_error(vm, fmt, ...) \ +#define njs_internal_error(vm, fmt, ...) \ njs_exception_error_create(vm, NJS_OBJECT_INTERNAL_ERROR, fmt, __VA_ARGS__) -#define njs_exception_range_error(vm, fmt, ...) \ +#define njs_range_error(vm, fmt, ...) \ njs_exception_error_create(vm, NJS_OBJECT_RANGE_ERROR, fmt, __VA_ARGS__) -#define njs_exception_ref_error(vm, fmt, ...) \ +#define njs_reference_error(vm, fmt, ...) \ njs_exception_error_create(vm, NJS_OBJECT_REF_ERROR, fmt, __VA_ARGS__) -#define njs_exception_syntax_error(vm, fmt, ...) \ +#define njs_syntax_error(vm, fmt, ...) \ njs_exception_error_create(vm, NJS_OBJECT_SYNTAX_ERROR, fmt, __VA_ARGS__) -#define njs_exception_type_error(vm, fmt, ...) \ +#define njs_type_error(vm, fmt, ...) \ njs_exception_error_create(vm, NJS_OBJECT_TYPE_ERROR, fmt, __VA_ARGS__) -#define njs_exception_uri_error(vm, fmt, ...) \ +#define njs_uri_error(vm, fmt, ...) \ njs_exception_error_create(vm, NJS_OBJECT_URI_ERROR, fmt, __VA_ARGS__) void njs_exception_error_create(njs_vm_t *vm, njs_value_type_t type, const char* fmt, ...); -void njs_exception_memory_error(njs_vm_t *vm); +void njs_memory_error(njs_vm_t *vm); void njs_set_memory_error(njs_vm_t *vm, njs_value_t *value); njs_object_t *njs_error_alloc(njs_vm_t *vm, njs_value_type_t type, @@ -41,7 +41,7 @@ njs_ret_t njs_internal_error_constructor nxt_uint_t nargs, njs_index_t unused); njs_ret_t njs_range_error_constructor(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, njs_index_t unused); -njs_ret_t njs_ref_error_constructor(njs_vm_t *vm, njs_value_t *args, +njs_ret_t njs_reference_error_constructor(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, njs_index_t unused); njs_ret_t njs_syntax_error_constructor(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, njs_index_t unused); @@ -57,7 +57,7 @@ extern const njs_object_init_t njs_erro extern const njs_object_init_t njs_eval_error_constructor_init; extern const njs_object_init_t njs_internal_error_constructor_init; extern const njs_object_init_t njs_range_error_constructor_init; -extern const njs_object_init_t njs_ref_error_constructor_init; +extern const njs_object_init_t njs_reference_error_constructor_init; extern const njs_object_init_t njs_syntax_error_constructor_init; extern const njs_object_init_t njs_type_error_constructor_init; extern const njs_object_init_t njs_uri_error_constructor_init; @@ -68,7 +68,7 @@ extern const njs_object_init_t njs_erro extern const njs_object_init_t njs_eval_error_prototype_init; extern const njs_object_init_t njs_internal_error_prototype_init; extern const njs_object_init_t njs_range_error_prototype_init; -extern const njs_object_init_t njs_ref_error_prototype_init; +extern const njs_object_init_t njs_reference_error_prototype_init; extern const njs_object_init_t njs_syntax_error_prototype_init; extern const njs_object_init_t njs_type_error_prototype_init; extern const njs_object_init_t njs_uri_error_prototype_init; diff -r 9dfa2e1892d0 -r ecf693afd698 njs/njs_event.c --- a/njs/njs_event.c Fri Mar 23 14:03:09 2018 +0300 +++ b/njs/njs_event.c Tue Mar 27 19:11:04 2018 +0300 @@ -79,8 +79,8 @@ njs_add_event(njs_vm_t *vm, njs_event_t ret = nxt_lvlhsh_insert(&vm->events_hash, &lhq); if (nxt_slow_path(ret != NXT_OK)) { - njs_exception_internal_error(vm, "Failed to add event with id: %s", - njs_string_short_start(&event->id)); + njs_internal_error(vm, "Failed to add event with id: %s", + njs_string_short_start(&event->id)); njs_del_event(vm, event, NJS_EVENT_RELEASE | NJS_EVENT_DELETE); return NJS_ERROR; diff -r 9dfa2e1892d0 -r ecf693afd698 njs/njs_fs.c --- a/njs/njs_fs.c Fri Mar 23 14:03:09 2018 +0300 +++ b/njs/njs_fs.c Tue Mar 27 19:11:04 2018 +0300 @@ -116,12 +116,12 @@ njs_fs_read_file(njs_vm_t *vm, njs_value nxt_lvlhsh_query_t lhq; if (nxt_slow_path(nargs < 3)) { - njs_exception_type_error(vm, "too few arguments", NULL); + njs_type_error(vm, "too few arguments", NULL); return NJS_ERROR; } if (nxt_slow_path(!njs_is_string(&args[1]))) { - njs_exception_type_error(vm, "path must be a string", NULL); + njs_type_error(vm, "path must be a string", NULL); return NJS_ERROR; } @@ -155,13 +155,13 @@ njs_fs_read_file(njs_vm_t *vm, njs_value } } else { - njs_exception_type_error(vm, "Unknown options type " - "(a string or object required)", NULL); + njs_type_error(vm, "Unknown options type " + "(a string or object required)", NULL); return NJS_ERROR; } if (nxt_slow_path(nargs < 4 || !njs_is_function(&args[3]))) { - njs_exception_type_error(vm, "callback must be a function", NULL); + njs_type_error(vm, "callback must be a function", NULL); return NJS_ERROR; } @@ -169,7 +169,7 @@ njs_fs_read_file(njs_vm_t *vm, njs_value } else { if (nxt_slow_path(!njs_is_function(&args[2]))) { - njs_exception_type_error(vm, "callback must be a function", NULL); + njs_type_error(vm, "callback must be a function", NULL); return NJS_ERROR; } @@ -182,8 +182,8 @@ njs_fs_read_file(njs_vm_t *vm, njs_value flags = njs_fs_flags(&flag); if (nxt_slow_path(flags == -1)) { - njs_exception_type_error(vm, "Unknown file open flags: '%.*s'", - (int) flag.length, flag.start); + njs_type_error(vm, "Unknown file open flags: '%.*s'", + (int) flag.length, flag.start); return NJS_ERROR; } @@ -195,8 +195,8 @@ njs_fs_read_file(njs_vm_t *vm, njs_value if (encoding.length != 0 && (encoding.length != 4 || memcmp(encoding.start, "utf8", 4) != 0)) { - njs_exception_type_error(vm, "Unknown encoding: '%.*s'", - (int) encoding.length, encoding.start); + njs_type_error(vm, "Unknown encoding: '%.*s'", + (int) encoding.length, encoding.start); return NJS_ERROR; } @@ -309,7 +309,7 @@ memory_error: (void) close(fd); } - njs_exception_memory_error(vm); + njs_memory_error(vm); return NJS_ERROR; } @@ -330,12 +330,12 @@ njs_fs_read_file_sync(njs_vm_t *vm, njs_ nxt_lvlhsh_query_t lhq; if (nxt_slow_path(nargs < 2)) { - njs_exception_type_error(vm, "too few arguments", NULL); + njs_type_error(vm, "too few arguments", NULL); return NJS_ERROR; } if (nxt_slow_path(!njs_is_string(&args[1]))) { - njs_exception_type_error(vm, "path must be a string", NULL); + njs_type_error(vm, "path must be a string", NULL); return NJS_ERROR; } @@ -369,8 +369,8 @@ njs_fs_read_file_sync(njs_vm_t *vm, njs_ } } else { - njs_exception_type_error(vm, "Unknown options type " - "(a string or object required)", NULL); + njs_type_error(vm, "Unknown options type " + "(a string or object required)", NULL); return NJS_ERROR; } } @@ -381,8 +381,8 @@ njs_fs_read_file_sync(njs_vm_t *vm, njs_ flags = njs_fs_flags(&flag); if (nxt_slow_path(flags == -1)) { - njs_exception_type_error(vm, "Unknown file open flags: '%.*s'", - (int) flag.length, flag.start); + njs_type_error(vm, "Unknown file open flags: '%.*s'", + (int) flag.length, flag.start); return NJS_ERROR; } @@ -394,8 +394,8 @@ njs_fs_read_file_sync(njs_vm_t *vm, njs_ if (encoding.length != 0 && (encoding.length != 4 || memcmp(encoding.start, "utf8", 4) != 0)) { - njs_exception_type_error(vm, "Unknown encoding: '%.*s'", - (int) encoding.length, encoding.start); + njs_type_error(vm, "Unknown encoding: '%.*s'", + (int) encoding.length, encoding.start); return NJS_ERROR; } @@ -495,7 +495,7 @@ memory_error: (void) close(fd); } - njs_exception_memory_error(vm); + njs_memory_error(vm); return NJS_ERROR; } @@ -551,17 +551,17 @@ static njs_ret_t njs_fs_write_file_inter nxt_lvlhsh_query_t lhq; if (nxt_slow_path(nargs < 4)) { - njs_exception_type_error(vm, "too few arguments", NULL); + njs_type_error(vm, "too few arguments", NULL); return NJS_ERROR; } if (nxt_slow_path(!njs_is_string(&args[1]))) { - njs_exception_type_error(vm, "path must be a string", NULL); + njs_type_error(vm, "path must be a string", NULL); return NJS_ERROR; } if (nxt_slow_path(!njs_is_string(&args[2]))) { - njs_exception_type_error(vm, "data must be a string", NULL); + njs_type_error(vm, "data must be a string", NULL); return NJS_ERROR; } @@ -608,13 +608,13 @@ static njs_ret_t njs_fs_write_file_inter } } else { - njs_exception_type_error(vm, "Unknown options type " - "(a string or object required)", NULL); + njs_type_error(vm, "Unknown options type " + "(a string or object required)", NULL); return NJS_ERROR; } if (nxt_slow_path(nargs < 5 || !njs_is_function(&args[4]))) { - njs_exception_type_error(vm, "callback must be a function", NULL); + njs_type_error(vm, "callback must be a function", NULL); return NJS_ERROR; } @@ -622,7 +622,7 @@ static njs_ret_t njs_fs_write_file_inter } else { if (nxt_slow_path(!njs_is_function(&args[3]))) { - njs_exception_type_error(vm, "callback must be a function", NULL); + njs_type_error(vm, "callback must be a function", NULL); return NJS_ERROR; } @@ -632,8 +632,8 @@ static njs_ret_t njs_fs_write_file_inter if (flag.start != NULL) { flags = njs_fs_flags(&flag); if (nxt_slow_path(flags == -1)) { - njs_exception_type_error(vm, "Unknown file open flags: '%.*s'", - (int) flag.length, flag.start); + njs_type_error(vm, "Unknown file open flags: '%.*s'", + (int) flag.length, flag.start); return NJS_ERROR; } @@ -656,8 +656,8 @@ static njs_ret_t njs_fs_write_file_inter if (encoding.length != 0 && (encoding.length != 4 || memcmp(encoding.start, "utf8", 4) != 0)) { - njs_exception_type_error(vm, "Unknown encoding: '%.*s'", - (int) encoding.length, encoding.start); + njs_type_error(vm, "Unknown encoding: '%.*s'", + (int) encoding.length, encoding.start); return NJS_ERROR; } @@ -740,17 +740,17 @@ njs_fs_write_file_sync_internal(njs_vm_t nxt_lvlhsh_query_t lhq; if (nxt_slow_path(nargs < 3)) { - njs_exception_type_error(vm, "too few arguments", NULL); + njs_type_error(vm, "too few arguments", NULL); return NJS_ERROR; } if (nxt_slow_path(!njs_is_string(&args[1]))) { - njs_exception_type_error(vm, "path must be a string", NULL); + njs_type_error(vm, "path must be a string", NULL); return NJS_ERROR; } if (nxt_slow_path(!njs_is_string(&args[2]))) { - njs_exception_type_error(vm, "data must be a string", NULL); + njs_type_error(vm, "data must be a string", NULL); return NJS_ERROR; } @@ -797,8 +797,8 @@ njs_fs_write_file_sync_internal(njs_vm_t } } else { - njs_exception_type_error(vm, "Unknown options type " - "(a string or object required)", NULL); + njs_type_error(vm, "Unknown options type " + "(a string or object required)", NULL); return NJS_ERROR; } } @@ -806,8 +806,8 @@ njs_fs_write_file_sync_internal(njs_vm_t if (flag.start != NULL) { flags = njs_fs_flags(&flag); if (nxt_slow_path(flags == -1)) { - njs_exception_type_error(vm, "Unknown file open flags: '%.*s'", - (int) flag.length, flag.start); + njs_type_error(vm, "Unknown file open flags: '%.*s'", + (int) flag.length, flag.start); return NJS_ERROR; } @@ -830,8 +830,8 @@ njs_fs_write_file_sync_internal(njs_vm_t if (encoding.length != 0 && (encoding.length != 4 || memcmp(encoding.start, "utf8", 4) != 0)) { - njs_exception_type_error(vm, "Unknown encoding: '%.*s'", - (int) encoding.length, encoding.start); + njs_type_error(vm, "Unknown encoding: '%.*s'", + (int) encoding.length, encoding.start); return NJS_ERROR; } @@ -944,7 +944,7 @@ static njs_ret_t njs_fs_error(njs_vm_t * ret = nxt_lvlhsh_insert(&error->hash, &lhq); if (nxt_slow_path(ret != NXT_OK)) { - njs_exception_internal_error(vm, NULL, NULL); + njs_internal_error(vm, NULL, NULL); return NJS_ERROR; } } @@ -963,7 +963,7 @@ static njs_ret_t njs_fs_error(njs_vm_t * ret = nxt_lvlhsh_insert(&error->hash, &lhq); if (nxt_slow_path(ret != NXT_OK)) { - njs_exception_internal_error(vm, NULL, NULL); + njs_internal_error(vm, NULL, NULL); return NJS_ERROR; } } @@ -988,7 +988,7 @@ static njs_ret_t njs_fs_error(njs_vm_t * ret = nxt_lvlhsh_insert(&error->hash, &lhq); if (nxt_slow_path(ret != NXT_OK)) { - njs_exception_internal_error(vm, NULL, NULL); + njs_internal_error(vm, NULL, NULL); return NJS_ERROR; } } @@ -1001,7 +1001,7 @@ static njs_ret_t njs_fs_error(njs_vm_t * memory_error: - njs_exception_memory_error(vm); + njs_memory_error(vm); return NJS_ERROR; } diff -r 9dfa2e1892d0 -r ecf693afd698 njs/njs_function.c --- a/njs/njs_function.c Fri Mar 23 14:03:09 2018 +0300 +++ b/njs/njs_function.c Tue Mar 27 19:11:04 2018 +0300 @@ -256,8 +256,7 @@ njs_function_frame_alloc(njs_vm_t *vm, s spare_size = nxt_align_size(spare_size, NJS_FRAME_SPARE_SIZE); if (vm->stack_size + spare_size > NJS_MAX_STACK_SIZE) { - njs_exception_range_error(vm, "Maximum call stack size exceeded", - NULL); + njs_range_error(vm, "Maximum call stack size exceeded", NULL); return NULL; } @@ -518,7 +517,7 @@ njs_function_prototype_call(njs_vm_t *vm njs_function_t *function; if (!njs_is_function(&args[0])) { - njs_exception_type_error(vm, "'this' argument is not a function", NULL); + njs_type_error(vm, "'this' argument is not a function", NULL); return NXT_ERROR; } @@ -546,7 +545,7 @@ njs_function_prototype_apply(njs_vm_t *v njs_function_t *function; if (!njs_is_function(&args[0])) { - njs_exception_type_error(vm, "'this' argument is not a function", NULL); + njs_type_error(vm, "'this' argument is not a function", NULL); return NXT_ERROR; } @@ -555,8 +554,7 @@ njs_function_prototype_apply(njs_vm_t *v if (nargs > 2) { if (!njs_is_array(&args[2])) { - njs_exception_type_error(vm, "second argument is not an array", - NULL); + njs_type_error(vm, "second argument is not an array", NULL); return NXT_ERROR; } @@ -628,7 +626,7 @@ njs_function_prototype_bind(njs_vm_t *vm njs_function_t *function; if (!njs_is_function(&args[0])) { - njs_exception_type_error(vm, "'this' argument is not a function", NULL); + njs_type_error(vm, "'this' argument is not a function", NULL); return NXT_ERROR; } @@ -707,7 +705,7 @@ njs_ret_t njs_eval_function(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, njs_index_t unused) { - njs_exception_internal_error(vm, "Not implemented", NULL); + njs_internal_error(vm, "Not implemented", NULL); return NXT_ERROR; } diff -r 9dfa2e1892d0 -r ecf693afd698 njs/njs_generator.c --- a/njs/njs_generator.c Fri Mar 23 14:03:09 2018 +0300 +++ b/njs/njs_generator.c Tue Mar 27 19:11:04 2018 +0300 @@ -344,7 +344,7 @@ njs_generator(njs_vm_t *vm, njs_parser_t default: nxt_thread_log_debug("unknown token: %d", node->token); - njs_exception_syntax_error(vm, "unknown token", NULL); + njs_syntax_error(vm, "unknown token", NULL); return NXT_ERROR; } @@ -2082,7 +2082,7 @@ njs_generate_scope(njs_vm_t *vm, njs_par parser->code_size, code_size); if (nxt_slow_path(parser->code_size < code_size)) { - njs_exception_internal_error(vm, NULL, NULL); + njs_internal_error(vm, NULL, NULL); return NXT_ERROR; } diff -r 9dfa2e1892d0 -r ecf693afd698 njs/njs_json.c --- a/njs/njs_json.c Fri Mar 23 14:03:09 2018 +0300 +++ b/njs/njs_json.c Tue Mar 27 19:11:04 2018 +0300 @@ -187,7 +187,7 @@ njs_json_parse(njs_vm_t *vm, njs_value_t value = nxt_mem_cache_alloc(vm->mem_cache_pool, sizeof(njs_value_t)); if (nxt_slow_path(value == NULL)) { - njs_exception_memory_error(vm); + njs_memory_error(vm); return NXT_ERROR; } @@ -255,7 +255,7 @@ njs_json_parse(njs_vm_t *vm, njs_value_t memory_error: - njs_exception_memory_error(vm); + njs_memory_error(vm); return NXT_ERROR; } @@ -342,7 +342,7 @@ njs_json_stringify(njs_vm_t *vm, njs_val memory_error: - njs_exception_memory_error(vm); + njs_memory_error(vm); return NXT_ERROR; } @@ -487,7 +487,7 @@ njs_json_parse_object(njs_json_parse_ctx ret = nxt_lvlhsh_insert(&object->hash, &lhq); if (nxt_slow_path(ret != NXT_OK)) { - njs_exception_internal_error(ctx->vm, NULL, NULL); + njs_internal_error(ctx->vm, NULL, NULL); return NULL; } @@ -527,7 +527,7 @@ error_end: memory_error: - njs_exception_memory_error(ctx->vm); + njs_memory_error(ctx->vm); return NULL; } @@ -547,7 +547,7 @@ njs_json_parse_array(njs_json_parse_ctx_ array = njs_array_alloc(ctx->vm, 0, 0); if (nxt_slow_path(array == NULL)) { - njs_exception_memory_error(ctx->vm); + njs_memory_error(ctx->vm); return NULL; } @@ -570,7 +570,7 @@ njs_json_parse_array(njs_json_parse_ctx_ element = nxt_mem_cache_alloc(ctx->pool, sizeof(njs_value_t)); if (nxt_slow_path(element == NULL)) { - njs_exception_memory_error(ctx->vm); + njs_memory_error(ctx->vm); return NULL; } @@ -581,7 +581,7 @@ njs_json_parse_array(njs_json_parse_ctx_ ret = njs_array_add(ctx->vm, array, element); if (nxt_slow_path(ret != NXT_OK)) { - njs_exception_internal_error(ctx->vm, NULL, NULL); + njs_internal_error(ctx->vm, NULL, NULL); return NULL; } @@ -736,7 +736,7 @@ njs_json_parse_string(njs_json_parse_ctx start = nxt_mem_cache_alloc(ctx->pool, size); if (nxt_slow_path(start == NULL)) { - njs_exception_memory_error(ctx->vm);; + njs_memory_error(ctx->vm);; return NULL; } @@ -821,7 +821,7 @@ njs_json_parse_string(njs_json_parse_ctx ret = njs_string_create(ctx->vm, value, start, size, length); if (nxt_slow_path(ret != NXT_OK)) { - njs_exception_memory_error(ctx->vm); + njs_memory_error(ctx->vm); return NULL; } @@ -992,7 +992,7 @@ njs_json_parse_continuation(njs_vm_t *vm } if (nxt_slow_path(ret != NXT_OK)) { - njs_exception_internal_error(vm, NULL, NULL); + njs_internal_error(vm, NULL, NULL); return NXT_ERROR; } @@ -1030,14 +1030,14 @@ njs_json_parse_continuation(njs_vm_t *vm break; default: - njs_exception_internal_error(vm, NULL, NULL); + njs_internal_error(vm, NULL, NULL); return NXT_ERROR; } } memory_error: - njs_exception_memory_error(vm); + njs_memory_error(vm); return NXT_ERROR; } @@ -1072,7 +1072,7 @@ njs_json_parse_continuation_apply(njs_vm break; default: - njs_exception_internal_error(vm, NULL, NULL); + njs_internal_error(vm, NULL, NULL); return NXT_ERROR; } @@ -1139,7 +1139,7 @@ njs_json_parse_exception(njs_json_parse_ length = 0; } - njs_exception_syntax_error(ctx->vm, "%s at position %zu", msg, length); + njs_syntax_error(ctx->vm, "%s at position %zu", msg, length); } @@ -1431,7 +1431,7 @@ done: memory_error: - njs_exception_memory_error(vm); + njs_memory_error(vm); return NXT_ERROR; } @@ -1495,7 +1495,7 @@ njs_json_stringify_to_json(njs_vm_t *vm, break; default: - njs_exception_internal_error(vm, NULL, NULL); + njs_internal_error(vm, NULL, NULL); return NXT_ERROR; } @@ -1539,7 +1539,7 @@ njs_json_stringify_replacer(njs_vm_t *vm break; default: - njs_exception_internal_error(vm, NULL, NULL); + njs_internal_error(vm, NULL, NULL); return NXT_ERROR; } @@ -1633,15 +1633,15 @@ njs_json_push_stringify_state(njs_vm_t * njs_json_state_t *state; if (stringify->stack.items >= 32) { - njs_exception_type_error(stringify->vm, - "Nested too deep or a cyclic structure", NULL); + njs_type_error(stringify->vm, + "Nested too deep or a cyclic structure", NULL); return NULL; } state = nxt_array_add(&stringify->stack, &njs_array_mem_proto, vm->mem_cache_pool); if (nxt_slow_path(state == NULL)) { - njs_exception_memory_error(vm); + njs_memory_error(vm); return NULL; } @@ -1723,8 +1723,7 @@ njs_json_append_value(njs_json_stringify return njs_json_buf_append(stringify, "null", 4); default: - njs_exception_type_error(stringify->vm, "Non-serializable object", - NULL); + njs_type_error(stringify->vm, "Non-serializable object", NULL); return NXT_DECLINED; } } diff -r 9dfa2e1892d0 -r ecf693afd698 njs/njs_module.c --- a/njs/njs_module.c Fri Mar 23 14:03:09 2018 +0300 +++ b/njs/njs_module.c Tue Mar 27 19:11:04 2018 +0300 @@ -59,7 +59,7 @@ njs_ret_t njs_module_require(njs_vm_t *v nxt_lvlhsh_query_t lhq; if (nargs < 2) { - njs_exception_type_error(vm, "missing path", NULL); + njs_type_error(vm, "missing path", NULL); return NJS_ERROR; } @@ -78,8 +78,8 @@ njs_ret_t njs_module_require(njs_vm_t *v return NXT_OK; } - njs_exception_error(vm, "Cannot find module '%.*s'", (int) lhq.key.length, - lhq.key.start); + njs_error(vm, "Cannot find module '%.*s'", + (int) lhq.key.length, lhq.key.start); return NJS_ERROR; } diff -r 9dfa2e1892d0 -r ecf693afd698 njs/njs_number.c --- a/njs/njs_number.c Fri Mar 23 14:03:09 2018 +0300 +++ b/njs/njs_number.c Tue Mar 27 19:11:04 2018 +0300 @@ -586,8 +586,8 @@ njs_number_prototype_value_of(njs_vm_t * value = &value->data.u.object_value->value; } else { - njs_exception_type_error(vm, "unexpected value type:%s", - njs_type_string(value->type)); + njs_type_error(vm, "unexpected value type:%s", + njs_type_string(value->type)); return NXT_ERROR; } } @@ -613,8 +613,8 @@ njs_number_prototype_to_string(njs_vm_t value = &value->data.u.object_value->value; } else { - njs_exception_type_error(vm, "unexpected value type:%s", - njs_type_string(value->type)); + njs_type_error(vm, "unexpected value type:%s", + njs_type_string(value->type)); return NXT_ERROR; } } @@ -623,7 +623,7 @@ njs_number_prototype_to_string(njs_vm_t radix = args[1].data.u.number; if (radix < 2 || radix > 36 || radix != (int) radix) { - njs_exception_range_error(vm, NULL, NULL); + njs_range_error(vm, NULL, NULL); return NXT_ERROR; } diff -r 9dfa2e1892d0 -r ecf693afd698 njs/njs_object.c --- a/njs/njs_object.c Fri Mar 23 14:03:09 2018 +0300 +++ b/njs/njs_object.c Tue Mar 27 19:11:04 2018 +0300 @@ -262,8 +262,8 @@ njs_object_constructor(njs_vm_t *vm, njs type = njs_object_value_type(value->type); } else { - njs_exception_type_error(vm, "unexpected constructor argument:%s", - njs_type_string(value->type)); + njs_type_error(vm, "unexpected constructor argument:%s", + njs_type_string(value->type)); return NXT_ERROR; } @@ -310,7 +310,7 @@ njs_object_create(njs_vm_t *vm, njs_valu } } - njs_exception_type_error(vm, "too few arguments", NULL); + njs_type_error(vm, "too few arguments", NULL); return NXT_ERROR; } @@ -323,16 +323,15 @@ njs_object_keys(njs_vm_t *vm, njs_value_ njs_array_t *keys; if (nargs < 2 || !njs_is_object(&args[1])) { - njs_exception_type_error(vm, "cannot convert %s argument to object", - (nargs >= 2) ? njs_type_string(args[1].type) - : "null"); + njs_type_error(vm, "cannot convert %s argument to object", + (nargs >= 2) ? njs_type_string(args[1].type) : "null"); return NXT_ERROR; } keys = njs_object_keys_array(vm, &args[1]); if (keys == NULL) { - njs_exception_memory_error(vm); + njs_memory_error(vm); return NXT_ERROR; } @@ -433,18 +432,17 @@ njs_object_define_property(njs_vm_t *vm, if (nargs < 4 || !njs_is_object(&args[1]) || !njs_is_object(&args[3])) { if (nargs < 2 || !njs_is_object(&args[1])) { type = (nargs > 1) ? njs_type_string(args[1].type) : "null"; - njs_exception_type_error(vm, "cannot convert %s argument to object", - type); + njs_type_error(vm, "cannot convert %s argument to object", type); } else { - njs_exception_type_error(vm, "descriptor is not an object", NULL); + njs_type_error(vm, "descriptor is not an object", NULL); } return NXT_ERROR; } if (!args[1].data.u.object->extensible) { - njs_exception_type_error(vm, "object is not extensible", NULL); + njs_type_error(vm, "object is not extensible", NULL); return NXT_ERROR; } @@ -475,18 +473,17 @@ njs_object_define_properties(njs_vm_t *v if (nargs < 3 || !njs_is_object(&args[1]) || !njs_is_object(&args[2])) { if (nargs < 2 || !njs_is_object(&args[1])) { type = (nargs > 1) ? njs_type_string(args[1].type) : "null"; - njs_exception_type_error(vm, "cannot convert %s argument to object", - type); + njs_type_error(vm, "cannot convert %s argument to object", type); } else { - njs_exception_type_error(vm, "descriptor is not an object", NULL); + njs_type_error(vm, "descriptor is not an object", NULL); } return NXT_ERROR; } From vl at nginx.com Tue Mar 27 17:40:02 2018 From: vl at nginx.com (Vladimir Homutov) Date: Tue, 27 Mar 2018 17:40:02 +0000 Subject: [nginx] Core: style. Message-ID: details: http://hg.nginx.org/nginx/rev/7bdab16c55f1 branches: changeset: 7252:7bdab16c55f1 user: Vladimir Homutov date: Tue Mar 27 18:39:38 2018 +0300 description: Core: style. diffstat: src/core/ngx_proxy_protocol.c | 29 ++++++++++++++--------------- 1 files changed, 14 insertions(+), 15 deletions(-) diffs (99 lines): diff -r 416953ef0428 -r 7bdab16c55f1 src/core/ngx_proxy_protocol.c --- a/src/core/ngx_proxy_protocol.c Thu Mar 22 15:55:28 2018 +0300 +++ b/src/core/ngx_proxy_protocol.c Tue Mar 27 18:39:38 2018 +0300 @@ -18,15 +18,13 @@ #define NGX_PP_V2_AF_INET6 2 -#define ngx_pp_v2_get_u16(p) \ - ( ((uint16_t) ((u_char *) (p))[0] << 8) \ - + ( ((u_char *) (p))[1]) ) +#define ngx_pp_v2_get_u16(p) ((p)[0] << 8 | (p)[1]) typedef struct { u_char signature[NGX_PP_V2_SIGLEN]; u_char ver_cmd; - u_char fam_transp; + u_char family_transport; u_char len[2]; } ngx_pp_v2_header_t; @@ -57,7 +55,7 @@ static u_char *ngx_proxy_protocol_v2_rea u_char *last); static const u_char ngx_pp_v2_signature[NGX_PP_V2_SIGLEN] = - { 0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A }; + { 0x0d, 0x0a, 0x0d, 0x0a, 0x00, 0x0d, 0x0a, 0x51, 0x55, 0x49, 0x54, 0x0a }; u_char * @@ -162,7 +160,8 @@ ngx_proxy_protocol_read(ngx_connection_t c->proxy_protocol_port = (in_port_t) n; ngx_log_debug2(NGX_LOG_DEBUG_CORE, c->log, 0, - "PROXY protocol address: %V %i", &c->proxy_protocol_addr, n); + "PROXY protocol address: %V %d", &c->proxy_protocol_addr, + c->proxy_protocol_port); skip: @@ -258,15 +257,15 @@ ngx_proxy_protocol_v2_read(ngx_connectio end = buf + len; - cmd = hdr->ver_cmd & 0x0F; + cmd = hdr->ver_cmd & 0x0f; if (cmd != NGX_PP_V2_CMD_PROXY) { ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0, - "PROXY protocol v2 unsupported cmd 0x%xi", cmd); + "PROXY protocol v2 unsupported command 0x%xi", cmd); return end; } - transport = hdr->fam_transp & 0x0F; + transport = hdr->family_transport & 0x0f; if (transport != NGX_PP_V2_STREAM) { ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0, @@ -275,7 +274,7 @@ ngx_proxy_protocol_v2_read(ngx_connectio return end; } - family = hdr->fam_transp >> 4; + family = hdr->family_transport >> 4; addrs = (ngx_pp_v2_addrs_t *) buf; @@ -308,7 +307,7 @@ ngx_proxy_protocol_v2_read(ngx_connectio case NGX_PP_V2_AF_INET6: - if ((size_t) (end - buf) < sizeof(ngx_pp_v2_inet6_addrs_t)) { + if ((size_t) (end - buf) < sizeof(ngx_pp_v2_inet6_addrs_t)) { return NULL; } @@ -329,8 +328,8 @@ ngx_proxy_protocol_v2_read(ngx_connectio default: ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0, - "PROXY_protocol v2 unsupported address family " - "0x%xi", family); + "PROXY protocol v2 unsupported address family 0x%xi", + family); return end; } @@ -348,8 +347,8 @@ ngx_proxy_protocol_v2_read(ngx_connectio } ngx_log_debug2(NGX_LOG_DEBUG_CORE, c->log, 0, - "PROXY protocol v2 address: %V %i", name, - (ngx_int_t) c->proxy_protocol_port); + "PROXY protocol v2 address: %V %d", name, + c->proxy_protocol_port); if (buf < end) { ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0, From rnickb731 at gmail.com Wed Mar 28 19:47:04 2018 From: rnickb731 at gmail.com (Ryan Burn) Date: Wed, 28 Mar 2018 15:47:04 -0400 Subject: Restrictions to modifying request->headers_in.headers in NGX_HTTP_PREACCESS_PHASE? Message-ID: Hi, I have a module that registers a handler to run in NGX_HTTP_PREACCESS_PHASE. The handler adds headers to request->headers_in.headers. This works as I would expect most of the time. But I am seeing cases of some crashes when calling ngx_list_push(&request->headers_in.headers)); Are there any restrictions where you are not allowed to push to headers_in.headers like that in the preaccess phase? This is the line that where the segfault is reported as happening: https://github.com/opentracing-contrib/nginx-opentracing/blob/master/opentracing/src/inject_span_context.cpp#L23 And the backtrace (taken from https://github.com/kubernetes/ingress-nginx/issues/2222#issue-306992059): #0 0x0000561139f929ec in ngx_list_push () #1 0x00007f7a1adbb6ab in ngx_opentracing::inject_span_context(opentracing::v1::Tracer const&, ngx_http_request_s*, opentracing::v1::SpanContext const&) () from /etc/nginx/modules/ngx_http_opentracing_module.so #2 0x00007f7a1adbcf81 in ngx_opentracing::OpenTracingRequestInstrumentor::OpenTracingRequestInstrumentor(ngx_http_request_s*, ngx_http_core_loc_conf_s*, ngx_opentracing::opentracing_loc_conf_t*) () from /etc/nginx/modules/ngx_http_opentracing_module.so #3 0x00007f7a1adbdce6 in ngx_opentracing::on_enter_block(ngx_http_request_s*) () from /etc/nginx/modules/ngx_http_opentracing_module.so #4 0x000056113a097a33 in ngx_http_core_generic_phase () #5 0x000056113a093645 in ngx_http_core_run_phases () #6 0x000056113a08f03e in ngx_http_process_request () #7 0x000056113a09068c in ngx_http_process_request_line () #8 0x000056113a0a6be1 in ngx_epoll_process_events.lto_priv () #9 0x000056113a0c45df in ngx_process_events_and_timers () #10 0x000056113a0a8405 in ngx_worker_process_cycle () #11 0x000056113a0be07f in ngx_spawn_process () #12 0x000056113a0a9715 in ngx_master_process_cycle () #13 0x0000561139f8a654 in main () From mdounin at mdounin.ru Wed Mar 28 19:56:45 2018 From: mdounin at mdounin.ru (Maxim Dounin) Date: Wed, 28 Mar 2018 22:56:45 +0300 Subject: Restrictions to modifying request->headers_in.headers in NGX_HTTP_PREACCESS_PHASE? In-Reply-To: References: Message-ID: <20180328195645.GO77253@mdounin.ru> Hello! On Wed, Mar 28, 2018 at 03:47:04PM -0400, Ryan Burn wrote: > I have a module that registers a handler to run in > NGX_HTTP_PREACCESS_PHASE. The handler adds headers to > request->headers_in.headers. This works as I would expect most of the > time. But I am seeing cases of some crashes when calling > > ngx_list_push(&request->headers_in.headers)); > > Are there any restrictions where you are not allowed to push to > headers_in.headers like that in the preaccess phase? You are not allowed to modify r->headers_in, at any time. -- Maxim Dounin http://mdounin.ru/ From rnickb731 at gmail.com Wed Mar 28 20:15:44 2018 From: rnickb731 at gmail.com (Ryan Burn) Date: Wed, 28 Mar 2018 16:15:44 -0400 Subject: Restrictions to modifying request->headers_in.headers in NGX_HTTP_PREACCESS_PHASE? In-Reply-To: <20180328195645.GO77253@mdounin.ru> References: <20180328195645.GO77253@mdounin.ru> Message-ID: > > You are not allowed to modify r->headers_in, at any time. > If I want to ensure that certain headers are added if the request is proxied upstream, is there any way to do that? From zchao1995 at gmail.com Thu Mar 29 02:06:57 2018 From: zchao1995 at gmail.com (tokers) Date: Wed, 28 Mar 2018 19:06:57 -0700 Subject: details about HTTP/2 Huffman Message-ID: Hello! I am wondering some details about HTTP/2 Huffman. There is a statement ( http://hg.nginx.org/nginx/file/tip/src/http/v2/ngx_http_v2_huff_encode.c#l238 ) buf |= (ngx_uint_t) ?1 >> pending; I am confused about this statement, why should we set the low bits of but to 1 (sizeof(buf) - pending). On the other hand, i am interested in the the generation about the huffman decode table ( http://hg.nginx.org/nginx/file/tip/src/http/v2/ngx_http_v2_huff_decode.c#l25 ), is this really a Huffman tree, which takes 4bits each time? -------------- next part -------------- An HTML attachment was scrubbed... URL: From jeppojeps at gmail.com Thu Mar 29 08:51:08 2018 From: jeppojeps at gmail.com (Antonio Nappa) Date: Thu, 29 Mar 2018 08:51:08 +0000 Subject: ngx_write_fd Message-ID: Hi, I was wondering what happens with ngx_write_fd function and multiple workers, I have made some tests and it doesn't look like they are interfering with each other and writing in the middle of a line. However I am not sure, that's the reason of my question. The file was opened with the ngx_conf_open_file. Thanks, Antonio -------------- next part -------------- An HTML attachment was scrubbed... URL: From dedok.mad at gmail.com Thu Mar 29 09:11:00 2018 From: dedok.mad at gmail.com (Vasily Soshnikov) Date: Thu, 29 Mar 2018 12:11:00 +0300 Subject: ngx_write_fd In-Reply-To: References: Message-ID: Hi, The UNIX-like system does not guarantee atomic write to a file (i.e. regular file). Actual, it works sometimes and for some system with tons of limitation. I prefer to avoid this practice. What can you do? You can lock (i.e. flock() and so on) file for writing, It's synchronization of concurrent access. Also, I suggest reading about fsync(), fdatasync() and kernel cache, if you wish to avoid the power-off problem. On 29 March 2018 at 11:51, Antonio Nappa wrote: > Hi, > > I was wondering what happens with ngx_write_fd function and multiple > workers, I have made some tests and it doesn't look like they are > interfering with each other and writing in the middle of a line. However I > am not sure, that's the reason of my question. The file was opened with the > ngx_conf_open_file. > > Thanks, > Antonio > > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel > -- *Regard,Soshnikov Vasily mailto:dedok.mad at gmail.com * -------------- next part -------------- An HTML attachment was scrubbed... URL: From xeioex at nginx.com Thu Mar 29 13:38:14 2018 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Thu, 29 Mar 2018 13:38:14 +0000 Subject: [njs] Added additional log routines for different nginx log levels. Message-ID: details: http://hg.nginx.org/njs/rev/36ef67f13052 branches: changeset: 472:36ef67f13052 user: Dmitry Volyntsev date: Thu Mar 29 16:38:02 2018 +0300 description: Added additional log routines for different nginx log levels. warn(), error(). diffstat: nginx/ngx_http_js_module.c | 82 +++++++++++++++++++++++++++++++++++++------ nginx/ngx_stream_js_module.c | 67 +++++++++++++++++++++++++++++++++-- 2 files changed, 131 insertions(+), 18 deletions(-) diffs (235 lines): diff -r ecf693afd698 -r 36ef67f13052 nginx/ngx_http_js_module.c --- a/nginx/ngx_http_js_module.c Tue Mar 27 19:11:04 2018 +0300 +++ b/nginx/ngx_http_js_module.c Thu Mar 29 16:38:02 2018 +0300 @@ -89,8 +89,16 @@ static njs_ret_t ngx_http_js_ext_send(nj nxt_uint_t nargs, njs_index_t unused); static njs_ret_t ngx_http_js_ext_finish(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, njs_index_t unused); + static njs_ret_t ngx_http_js_ext_log(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, njs_index_t unused); +static njs_ret_t ngx_http_js_ext_warn(njs_vm_t *vm, njs_value_t *args, + nxt_uint_t nargs, njs_index_t unused); +static njs_ret_t ngx_http_js_ext_error(njs_vm_t *vm, njs_value_t *args, + nxt_uint_t nargs, njs_index_t unused); +static njs_ret_t ngx_http_js_ext_log_core(njs_vm_t *vm, njs_value_t *args, + nxt_uint_t nargs, ngx_uint_t level); + static njs_ret_t ngx_http_js_ext_get_http_version(njs_vm_t *vm, njs_value_t *value, void *obj, uintptr_t data); static njs_ret_t ngx_http_js_ext_get_remote_address(njs_vm_t *vm, @@ -287,18 +295,6 @@ static njs_external_t ngx_http_js_ext_r static njs_external_t ngx_http_js_ext_request[] = { - { nxt_string("log"), - NJS_EXTERN_METHOD, - NULL, - 0, - NULL, - NULL, - NULL, - NULL, - NULL, - ngx_http_js_ext_log, - 0 }, - { nxt_string("uri"), NJS_EXTERN_PROPERTY, NULL, @@ -406,6 +402,42 @@ static njs_external_t ngx_http_js_ext_r NULL, ngx_http_js_ext_subrequest, 0 }, + + { nxt_string("log"), + NJS_EXTERN_METHOD, + NULL, + 0, + NULL, + NULL, + NULL, + NULL, + NULL, + ngx_http_js_ext_log, + 0 }, + + { nxt_string("warn"), + NJS_EXTERN_METHOD, + NULL, + 0, + NULL, + NULL, + NULL, + NULL, + NULL, + ngx_http_js_ext_warn, + 0 }, + + { nxt_string("error"), + NJS_EXTERN_METHOD, + NULL, + 0, + NULL, + NULL, + NULL, + NULL, + NULL, + ngx_http_js_ext_error, + 0 }, }; @@ -1226,6 +1258,30 @@ static njs_ret_t ngx_http_js_ext_log(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, njs_index_t unused) { + return ngx_http_js_ext_log_core(vm, args, nargs, NGX_LOG_INFO); +} + + +static njs_ret_t +ngx_http_js_ext_warn(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + return ngx_http_js_ext_log_core(vm, args, nargs, NGX_LOG_WARN); +} + + +static njs_ret_t +ngx_http_js_ext_error(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + return ngx_http_js_ext_log_core(vm, args, nargs, NGX_LOG_ERR); +} + + +static njs_ret_t +ngx_http_js_ext_log_core(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + ngx_uint_t level) +{ nxt_str_t msg; ngx_connection_t *c; ngx_log_handler_pt handler; @@ -1243,7 +1299,7 @@ ngx_http_js_ext_log(njs_vm_t *vm, njs_va handler = c->log->handler; c->log->handler = NULL; - ngx_log_error(NGX_LOG_INFO, c->log, 0, "js: %*s", msg.length, msg.start); + ngx_log_error(level, c->log, 0, "js: %*s", msg.length, msg.start); c->log->handler = handler; diff -r ecf693afd698 -r 36ef67f13052 nginx/ngx_stream_js_module.c --- a/nginx/ngx_stream_js_module.c Tue Mar 27 19:11:04 2018 +0300 +++ b/nginx/ngx_stream_js_module.c Thu Mar 29 16:38:02 2018 +0300 @@ -63,8 +63,16 @@ static njs_ret_t ngx_stream_js_ext_get_b void *obj, uintptr_t data); static njs_ret_t ngx_stream_js_ext_set_buffer(njs_vm_t *vm, void *obj, uintptr_t data, nxt_str_t *value); - static njs_ret_t ngx_stream_js_ext_log(njs_vm_t *vm, njs_value_t *args, + +static njs_ret_t ngx_stream_js_ext_log(njs_vm_t *vm, njs_value_t *args, + nxt_uint_t nargs, njs_index_t unused); +static njs_ret_t ngx_stream_js_ext_warn(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, njs_index_t unused); +static njs_ret_t ngx_stream_js_ext_error(njs_vm_t *vm, njs_value_t *args, + nxt_uint_t nargs, njs_index_t unused); +static njs_ret_t ngx_stream_js_ext_log_core(njs_vm_t *vm, njs_value_t *args, + nxt_uint_t nargs, ngx_uint_t level); + static njs_ret_t ngx_stream_js_ext_get_variable(njs_vm_t *vm, njs_value_t *value, void *obj, uintptr_t data); static njs_ret_t ngx_stream_js_ext_get_code(njs_vm_t *vm, @@ -199,6 +207,19 @@ static njs_external_t ngx_stream_js_ext NULL, 0 }, + + { nxt_string("variables"), + NJS_EXTERN_OBJECT, + NULL, + 0, + ngx_stream_js_ext_get_variable, + NULL, + NULL, + NULL, + NULL, + NULL, + 0 }, + { nxt_string("log"), NJS_EXTERN_METHOD, NULL, @@ -211,16 +232,28 @@ static njs_external_t ngx_stream_js_ext ngx_stream_js_ext_log, 0 }, - { nxt_string("variables"), - NJS_EXTERN_OBJECT, + { nxt_string("warn"), + NJS_EXTERN_METHOD, NULL, 0, - ngx_stream_js_ext_get_variable, NULL, NULL, NULL, NULL, NULL, + ngx_stream_js_ext_warn, + 0 }, + + { nxt_string("error"), + NJS_EXTERN_METHOD, + NULL, + 0, + NULL, + NULL, + NULL, + NULL, + NULL, + ngx_stream_js_ext_error, 0 }, { nxt_string("OK"), @@ -825,6 +858,30 @@ static njs_ret_t ngx_stream_js_ext_log(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, njs_index_t unused) { + return ngx_stream_js_ext_log_core(vm, args, nargs, NGX_LOG_INFO); +} + + +static njs_ret_t +ngx_stream_js_ext_warn(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + return ngx_stream_js_ext_log_core(vm, args, nargs, NGX_LOG_WARN); +} + + +static njs_ret_t +ngx_stream_js_ext_error(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + return ngx_stream_js_ext_log_core(vm, args, nargs, NGX_LOG_ERR); +} + + +static njs_ret_t +ngx_stream_js_ext_log_core(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + ngx_uint_t level) +{ nxt_str_t msg; ngx_connection_t *c; ngx_log_handler_pt handler; @@ -842,7 +899,7 @@ ngx_stream_js_ext_log(njs_vm_t *vm, njs_ handler = c->log->handler; c->log->handler = NULL; - ngx_log_error(NGX_LOG_INFO, c->log, 0, "js: %*s", msg.length, msg.start); + ngx_log_error(level, c->log, 0, "js: %*s", msg.length, msg.start); c->log->handler = handler; From mdounin at mdounin.ru Thu Mar 29 13:40:35 2018 From: mdounin at mdounin.ru (Maxim Dounin) Date: Thu, 29 Mar 2018 16:40:35 +0300 Subject: Restrictions to modifying request->headers_in.headers in NGX_HTTP_PREACCESS_PHASE? In-Reply-To: References: <20180328195645.GO77253@mdounin.ru> Message-ID: <20180329134035.GP77253@mdounin.ru> Hello! On Wed, Mar 28, 2018 at 04:15:44PM -0400, Ryan Burn wrote: > > You are not allowed to modify r->headers_in, at any time. > > If I want to ensure that certain headers are added if the request is > proxied upstream, is there any way to do that? Recommended approach is to export a variable with the appropriate value, and use proxy_set_header X-Your-Header $your_variable; in the configuration. For example, nginx itself uses this approach with X-Forwarded-For, see the $proxy_add_x_forwarded_for variable description here: http://nginx.org/en/docs/http/ngx_http_proxy_module.html#var_proxy_add_x_forwarded_for -- Maxim Dounin http://mdounin.ru/ From ru at nginx.com Thu Mar 29 13:57:40 2018 From: ru at nginx.com (Ruslan Ermilov) Date: Thu, 29 Mar 2018 16:57:40 +0300 Subject: ngx_write_fd In-Reply-To: References: Message-ID: <20180329135740.GA71448@lo0.su> On Thu, Mar 29, 2018 at 08:51:08AM +0000, Antonio Nappa wrote: > Hi, > > I was wondering what happens with ngx_write_fd function and multiple > workers, I have made some tests and it doesn't look like they are > interfering with each other and writing in the middle of a line. However I > am not sure, that's the reason of my question. The file was opened with the > ngx_conf_open_file. This is guaranteed by opening the files with O_APPEND; see ngx_init_cycle() and NGX_FILE_APPEND. From pauloasilva at gmail.com Thu Mar 29 15:17:02 2018 From: pauloasilva at gmail.com (Paulo Silva) Date: Thu, 29 Mar 2018 16:17:02 +0100 Subject: Access Nginx variables from HTTP modules Message-ID: Hi everyone, can you point me the right way to access Nginx variables from my HTTP module? I am looking for a way to read the $request_id value from within one of my modules' functions. Thanks, Paulo A. Silva https://a-caminho.de https://tech.pauloasilva.com From vl at nginx.com Thu Mar 29 15:39:23 2018 From: vl at nginx.com (Vladimir Homutov) Date: Thu, 29 Mar 2018 18:39:23 +0300 Subject: Access Nginx variables from HTTP modules In-Reply-To: References: Message-ID: <20180329153922.GA5328@vlpc> On Thu, Mar 29, 2018 at 04:17:02PM +0100, Paulo Silva wrote: > Hi everyone, > can you point me the right way to access Nginx variables from my HTTP module? > > I am looking for a way to read the $request_id value from within one > of my modules' functions. > http://nginx.org/en/docs/dev/development_guide.html#http_variables From pauloasilva at gmail.com Thu Mar 29 16:12:01 2018 From: pauloasilva at gmail.com (Paulo Silva) Date: Thu, 29 Mar 2018 17:12:01 +0100 Subject: Access Nginx variables from HTTP modules In-Reply-To: <20180329153922.GA5328@vlpc> References: <20180329153922.GA5328@vlpc> Message-ID: Would it be Ok doing something like? ngx_str_t variable_name = ngx_string("request_id"); ngx_uint_t variable_name_hash; ngx_http_variable_value_t *variable = NULL; variable_name_hash = ngx_hash_key(variable_name.data, variable_name.len); variable = ngx_http_get_variable(r, &variable_name, variable_name_hash); On Thu, Mar 29, 2018 at 4:39 PM, Vladimir Homutov wrote: > On Thu, Mar 29, 2018 at 04:17:02PM +0100, Paulo Silva wrote: >> Hi everyone, >> can you point me the right way to access Nginx variables from my HTTP module? >> >> I am looking for a way to read the $request_id value from within one >> of my modules' functions. >> > > http://nginx.org/en/docs/dev/development_guide.html#http_variables > > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel -- Paulo A. Silva https://a-caminho.de https://tech.pauloasilva.com From rnickb731 at gmail.com Thu Mar 29 18:06:00 2018 From: rnickb731 at gmail.com (Ryan Burn) Date: Thu, 29 Mar 2018 14:06:00 -0400 Subject: Restrictions to modifying request->headers_in.headers in NGX_HTTP_PREACCESS_PHASE? In-Reply-To: <20180329134035.GP77253@mdounin.ru> References: <20180328195645.GO77253@mdounin.ru> <20180329134035.GP77253@mdounin.ru> Message-ID: On Thu, Mar 29, 2018 at 9:40 AM, Maxim Dounin wrote: > Hello! > > On Wed, Mar 28, 2018 at 04:15:44PM -0400, Ryan Burn wrote: > >> > You are not allowed to modify r->headers_in, at any time. >> >> If I want to ensure that certain headers are added if the request is >> proxied upstream, is there any way to do that? > > Recommended approach is to export a variable with the appropriate > value, and use > > proxy_set_header X-Your-Header $your_variable; This module injects multiple headers and the number, keys, etc aren't meant to be exposed to the user. (This module is mean to support tracing and the injected headers btw correspond to a SpanContext: http://opentracing.io/documentation/pages/api/cross-process-tracing.html which varies by the trace used). Is there way to accomplish this without having the configuration author do anything? From rpaprocki at fearnothingproductions.net Thu Mar 29 18:34:49 2018 From: rpaprocki at fearnothingproductions.net (Robert Paprocki) Date: Thu, 29 Mar 2018 11:34:49 -0700 Subject: Restrictions to modifying request->headers_in.headers in NGX_HTTP_PREACCESS_PHASE? In-Reply-To: References: <20180328195645.GO77253@mdounin.ru> <20180329134035.GP77253@mdounin.ru> Message-ID: Hello, On Thu, Mar 29, 2018 at 11:06 AM, Ryan Burn wrote: > > > This module injects multiple headers and the number, keys, etc aren't > meant to be exposed to the user. > > (This module is mean to support tracing and the injected headers btw > correspond to a SpanContext: > http://opentracing.io/documentation/pages/api/cross-process-tracing.html > which varies by the trace used). > > Is there way to accomplish this without having the configuration > author do anything? > What's the source of these header names/values? If it's passed from the downstream client, it will automatically be proxied to the upstream server (with some exceptions, see the proxy module documentation). If it's generated on the Nginx node itself, you should be able to use the proxy_set_header Maxim defined above, in combination with variables which could be generated via your custom module or some other approach. -------------- next part -------------- An HTML attachment was scrubbed... URL: From xeioex at nginx.com Fri Mar 30 12:47:10 2018 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Fri, 30 Mar 2018 12:47:10 +0000 Subject: [njs] Added hex encoding for byte strings. Message-ID: details: http://hg.nginx.org/njs/rev/c685137c4e57 branches: changeset: 473:c685137c4e57 user: Dmitry Volyntsev date: Fri Mar 30 15:46:38 2018 +0300 description: Added hex encoding for byte strings. diffstat: njs/njs_string.c | 31 +++++++++++++++++++++++++++++++ njs/njs_string.h | 2 ++ 2 files changed, 33 insertions(+), 0 deletions(-) diffs (53 lines): diff -r 36ef67f13052 -r c685137c4e57 njs/njs_string.c --- a/njs/njs_string.c Thu Mar 29 16:38:02 2018 +0300 +++ b/njs/njs_string.c Fri Mar 30 15:46:38 2018 +0300 @@ -244,6 +244,37 @@ njs_string_alloc(njs_vm_t *vm, njs_value } +nxt_noinline njs_ret_t +njs_string_hex(njs_vm_t *vm, njs_value_t *value, const nxt_str_t *src) +{ + u_char *p, c; + size_t len; + nxt_uint_t i; + const u_char *start; + + static const u_char hex[16] = "0123456789abcdef"; + + len = src->length; + start = src->start; + + p = njs_string_alloc(vm, value, len * 2, len * 2); + + if (nxt_fast_path(p != NULL)) { + for (i = 0; i < len; i++) { + c = start[i]; + *p++ = hex[c >> 4]; + *p++ = hex[c & 0x0f]; + } + + return NXT_OK; + } + + njs_memory_error(vm); + + return NXT_ERROR; +} + + void njs_string_copy(njs_value_t *dst, njs_value_t *src) { diff -r 36ef67f13052 -r c685137c4e57 njs/njs_string.h --- a/njs/njs_string.h Thu Mar 29 16:38:02 2018 +0300 +++ b/njs/njs_string.h Fri Mar 30 15:46:38 2018 +0300 @@ -122,6 +122,8 @@ njs_string_length(njs_utf8_t utf8, u_cha njs_ret_t njs_string_new(njs_vm_t *vm, njs_value_t *value, const u_char *start, uint32_t size, uint32_t length); +njs_ret_t njs_string_hex(njs_vm_t *vm, njs_value_t *value, + const nxt_str_t *src); void njs_string_copy(njs_value_t *dst, njs_value_t *src); njs_ret_t njs_string_validate(njs_vm_t *vm, njs_string_prop_t *string, njs_value_t *value); From xeioex at nginx.com Fri Mar 30 12:47:10 2018 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Fri, 30 Mar 2018 12:47:10 +0000 Subject: [njs] Added base64 encoding for byte strings. Message-ID: details: http://hg.nginx.org/njs/rev/d35406201c78 branches: changeset: 474:d35406201c78 user: Dmitry Volyntsev date: Fri Mar 30 15:46:38 2018 +0300 description: Added base64 encoding for byte strings. diffstat: njs/njs_string.c | 134 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ njs/njs_string.h | 4 + 2 files changed, 138 insertions(+), 0 deletions(-) diffs (172 lines): diff -r c685137c4e57 -r d35406201c78 njs/njs_string.c --- a/njs/njs_string.c Fri Mar 30 15:46:38 2018 +0300 +++ b/njs/njs_string.c Fri Mar 30 15:46:38 2018 +0300 @@ -79,6 +79,8 @@ typedef struct { } njs_string_replace_t; +static void njs_encode_base64_core(nxt_str_t *dst, const nxt_str_t *src, + const u_char *basis, nxt_uint_t padding); static nxt_noinline void njs_string_slice_prop(njs_string_prop_t *string, njs_slice_prop_t *slice, njs_value_t *args, nxt_uint_t nargs); static nxt_noinline void njs_string_slice_args(njs_slice_prop_t *slice, @@ -119,6 +121,9 @@ static njs_ret_t njs_string_decode(njs_v const uint32_t *reserve); +#define njs_base64_encoded_length(len) (((len + 2) / 3) * 4) + + njs_ret_t njs_string_create(njs_vm_t *vm, njs_value_t *value, u_char *start, uint32_t size, uint32_t length) @@ -275,6 +280,135 @@ njs_string_hex(njs_vm_t *vm, njs_value_t } +static void +njs_encode_base64(nxt_str_t *dst, const nxt_str_t *src) +{ + static u_char basis64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + njs_encode_base64_core(dst, src, basis64, 1); +} + + +static void +njs_encode_base64url(nxt_str_t *dst, const nxt_str_t *src) +{ + static u_char basis64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; + + njs_encode_base64_core(dst, src, basis64, 0); +} + + +static void +njs_encode_base64_core(nxt_str_t *dst, const nxt_str_t *src, + const u_char *basis, nxt_bool_t padding) +{ + u_char *d, *s, c0, c1, c2; + size_t len; + + len = src->length; + s = src->start; + d = dst->start; + + while (len > 2) { + c0 = s[0]; + c1 = s[1]; + c2 = s[2]; + + *d++ = basis[c0 >> 2]; + *d++ = basis[((c0 & 0x03) << 4) | (c1 >> 4)]; + *d++ = basis[((c1 & 0x0f) << 2) | (c2 >> 6)]; + *d++ = basis[c2 & 0x3f]; + + s += 3; + len -= 3; + } + + if (len > 0) { + c0 = s[0]; + *d++ = basis[c0 >> 2]; + + if (len == 1) { + *d++ = basis[(c0 & 0x03) << 4]; + if (padding) { + *d++ = '='; + *d++ = '='; + } + + } else { + c1 = s[1]; + + *d++ = basis[((c0 & 0x03) << 4) | (c1 >> 4)]; + *d++ = basis[(c1 & 0x0f) << 2]; + + if (padding) { + *d++ = '='; + } + } + + } + + dst->length = d - dst->start; +} + + +nxt_noinline njs_ret_t +njs_string_base64(njs_vm_t *vm, njs_value_t *value, const nxt_str_t *src) +{ + nxt_str_t dst; + + if (nxt_slow_path(src->length == 0)) { + vm->retval = njs_string_empty; + return NXT_OK; + } + + dst.length = njs_base64_encoded_length(src->length); + + dst.start = njs_string_alloc(vm, &vm->retval, dst.length, dst.length); + if (nxt_slow_path(dst.start == NULL)) { + njs_memory_error(vm); + return NXT_ERROR; + } + + njs_encode_base64(&dst, src); + + return NXT_OK; +} + + +nxt_noinline njs_ret_t +njs_string_base64url(njs_vm_t *vm, njs_value_t *value, const nxt_str_t *src) +{ + size_t padding; + nxt_str_t dst; + + if (nxt_slow_path(src->length == 0)) { + vm->retval = njs_string_empty; + return NXT_OK; + } + + padding = src->length % 3; + + /* + * Calculating the padding length: 0 -> 0, 1 -> 2, 2 -> 1. + */ + padding = (4 >> padding) & 0x03; + + dst.length = njs_base64_encoded_length(src->length) - padding; + + dst.start = njs_string_alloc(vm, &vm->retval, dst.length, dst.length); + if (nxt_slow_path(dst.start == NULL)) { + njs_memory_error(vm); + return NXT_ERROR; + } + + njs_encode_base64url(&dst, src); + + return NXT_OK; +} + + void njs_string_copy(njs_value_t *dst, njs_value_t *src) { diff -r c685137c4e57 -r d35406201c78 njs/njs_string.h --- a/njs/njs_string.h Fri Mar 30 15:46:38 2018 +0300 +++ b/njs/njs_string.h Fri Mar 30 15:46:38 2018 +0300 @@ -124,6 +124,10 @@ njs_ret_t njs_string_new(njs_vm_t *vm, n uint32_t size, uint32_t length); njs_ret_t njs_string_hex(njs_vm_t *vm, njs_value_t *value, const nxt_str_t *src); +njs_ret_t njs_string_base64(njs_vm_t *vm, njs_value_t *value, + const nxt_str_t *src); +njs_ret_t njs_string_base64url(njs_vm_t *vm, njs_value_t *value, + const nxt_str_t *src); void njs_string_copy(njs_value_t *dst, njs_value_t *src); njs_ret_t njs_string_validate(njs_vm_t *vm, njs_string_prop_t *string, njs_value_t *value); From xeioex at nginx.com Fri Mar 30 12:47:10 2018 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Fri, 30 Mar 2018 12:47:10 +0000 Subject: [njs] Byte string to hex, base64, base64url encodings. Message-ID: details: http://hg.nginx.org/njs/rev/6343166cb0ea branches: changeset: 475:6343166cb0ea user: Dmitry Volyntsev date: Fri Mar 30 15:46:39 2018 +0300 description: Byte string to hex, base64, base64url encodings. diffstat: njs/njs_string.c | 63 +++++++++++++++++++++++++++++++++++++++++++- njs/test/njs_expect_test.exp | 7 ++++ njs/test/njs_unit_test.c | 53 +++++++++++++++++++++++++++++++++++++ 3 files changed, 122 insertions(+), 1 deletions(-) diffs (160 lines): diff -r d35406201c78 -r 6343166cb0ea njs/njs_string.c --- a/njs/njs_string.c Fri Mar 30 15:46:38 2018 +0300 +++ b/njs/njs_string.c Fri Mar 30 15:46:39 2018 +0300 @@ -730,6 +730,67 @@ njs_string_prototype_value_of(njs_vm_t * return NXT_OK; } + +/* + * String.toString([encoding]). + * Returns the string as is if no additional argument is provided, + * otherwise converts a byte string into an encoded string: hex, base64, + * base64url. + */ + +static njs_ret_t +njs_string_prototype_to_string(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + nxt_int_t ret; + nxt_str_t enc, str; + njs_value_t value; + njs_string_prop_t string; + + ret = njs_string_prototype_value_of(vm, args, nargs, unused); + if (nxt_slow_path(ret != NXT_OK)) { + return ret; + } + + if (nargs < 2) { + return NJS_OK; + } + + if (nxt_slow_path(!njs_is_string(&args[1]))) { + njs_type_error(vm, "encoding must be a string", NULL); + return NJS_ERROR; + } + + value = vm->retval; + + (void) njs_string_prop(&string, &value); + + if (nxt_slow_path(string.length != 0)) { + njs_type_error(vm, "argument must be a byte string", NULL); + return NJS_ERROR; + } + + njs_string_get(&args[1], &enc); + + str.length = string.size; + str.start = string.start; + + if (enc.length == 3 && memcmp(enc.start, "hex", 3) == 0) { + return njs_string_hex(vm, &vm->retval, &str); + + } else if (enc.length == 6 && memcmp(enc.start, "base64", 6) == 0) { + return njs_string_base64(vm, &vm->retval, &str); + + } else if (enc.length == 9 && memcmp(enc.start, "base64url", 9) == 0) { + return njs_string_base64url(vm, &vm->retval, &str); + } + + njs_type_error(vm, "Unknown encoding: '%.*s'", (int) enc.length, enc.start); + + return NJS_ERROR; +} + + /* * String.concat(string2[, ..., stringN]). * JavaScript 1.2, ECMAScript 3. @@ -3305,7 +3366,7 @@ static const njs_object_prop_t njs_stri { .type = NJS_METHOD, .name = njs_string("toString"), - .value = njs_native_function(njs_string_prototype_value_of, 0, 0), + .value = njs_native_function(njs_string_prototype_to_string, 0, 0), }, { diff -r d35406201c78 -r 6343166cb0ea njs/test/njs_expect_test.exp --- a/njs/test/njs_expect_test.exp Fri Mar 30 15:46:38 2018 +0300 +++ b/njs/test/njs_expect_test.exp Fri Mar 30 15:46:39 2018 +0300 @@ -258,6 +258,13 @@ njs_test { njs_test { {"var fs = require('fs')\r\n" "undefined\r\n>> "} + {"fs.readFileSync('njs_test_file').toString('base64')\r\n" + "zrHOslrOsw==\r\n>> "} +} + +njs_test { + {"var fs = require('fs')\r\n" + "undefined\r\n>> "} {"fs.readFileSync('njs_test_file', 'utf8')[2]\r\n" "Z\r\n>> "} } diff -r d35406201c78 -r 6343166cb0ea njs/test/njs_unit_test.c --- a/njs/test/njs_unit_test.c Fri Mar 30 15:46:38 2018 +0300 +++ b/njs/test/njs_unit_test.c Fri Mar 30 15:46:39 2018 +0300 @@ -447,6 +447,59 @@ static njs_unit_test_t njs_test[] = { nxt_string("undefined - undefined"), nxt_string("NaN") }, + /* String.toString() method. */ + + { nxt_string("'A'.toString()"), + nxt_string("A") }, + + { nxt_string("'A'.toString('hex')"), + nxt_string("TypeError: argument must be a byte string") }, + + { nxt_string("'A'.toBytes().toString('latin1')"), + nxt_string("TypeError: Unknown encoding: 'latin1'") }, + + { nxt_string("'ABCD'.toBytes().toString('hex')"), + nxt_string("41424344") }, + + { nxt_string("'\\x00\\xAA\\xBB\\xFF'.toBytes().toString('hex')"), + nxt_string("00aabbff") }, + + { nxt_string("'\\x00\\xAA\\xBB\\xFF'.toBytes().toString('base64')"), + nxt_string("AKq7/w==") }, + + { nxt_string("'ABCD'.toBytes().toString('base64')"), + nxt_string("QUJDRA==") }, + + { nxt_string("'ABC'.toBytes().toString('base64')"), + nxt_string("QUJD") }, + + { nxt_string("'AB'.toBytes().toString('base64')"), + nxt_string("QUI=") }, + + { nxt_string("'A'.toBytes().toString('base64')"), + nxt_string("QQ==") }, + + { nxt_string("''.toBytes().toString('base64')"), + nxt_string("") }, + + { nxt_string("'\\x00\\xAA\\xBB\\xFF'.toBytes().toString('base64url')"), + nxt_string("AKq7_w") }, + + { nxt_string("'ABCD'.toBytes().toString('base64url')"), + nxt_string("QUJDRA") }, + + { nxt_string("'ABC'.toBytes().toString('base64url')"), + nxt_string("QUJD") }, + + { nxt_string("'AB'.toBytes().toString('base64url')"), + nxt_string("QUI") }, + + { nxt_string("'A'.toBytes().toString('base64url')"), + nxt_string("QQ") }, + + { nxt_string("''.toBytes().toString('base64url')"), + nxt_string("") }, + /* Assignment. */ { nxt_string("var a, b = (a = [2]) * (3 * 4); a +' '+ b"), From xeioex at nginx.com Fri Mar 30 12:47:11 2018 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Fri, 30 Mar 2018 12:47:11 +0000 Subject: [njs] Renamed njs_value_is_true into njs_value_is_boolean. Message-ID: details: http://hg.nginx.org/njs/rev/be43fdd9579e branches: changeset: 476:be43fdd9579e user: Dmitry Volyntsev date: Fri Mar 30 15:46:40 2018 +0300 description: Renamed njs_value_is_true into njs_value_is_boolean. diffstat: njs/njs_vm.c | 11 +++++++++-- njs/njscript.h | 3 ++- 2 files changed, 11 insertions(+), 3 deletions(-) diffs (47 lines): diff -r 6343166cb0ea -r be43fdd9579e njs/njs_vm.c --- a/njs/njs_vm.c Fri Mar 30 15:46:39 2018 +0300 +++ b/njs/njs_vm.c Fri Mar 30 15:46:40 2018 +0300 @@ -3691,6 +3691,13 @@ memory_error: } +nxt_noinline uint8_t +njs_value_bool(njs_value_t *value) +{ + return value->data.truth; +} + + nxt_noinline double njs_value_number(njs_value_t *value) { @@ -3720,9 +3727,9 @@ njs_value_is_void(njs_value_t *value) nxt_noinline nxt_int_t -njs_value_is_true(njs_value_t *value) +njs_value_is_boolean(njs_value_t *value) { - return njs_is_true(value); + return njs_is_boolean(value); } diff -r 6343166cb0ea -r be43fdd9579e njs/njscript.h --- a/njs/njscript.h Fri Mar 30 15:46:39 2018 +0300 +++ b/njs/njscript.h Fri Mar 30 15:46:40 2018 +0300 @@ -182,12 +182,13 @@ NXT_EXPORT void njs_value_number_set(njs NXT_EXPORT void njs_value_error_set(njs_vm_t *vm, njs_value_t *value, const char *fmt, ...); +NXT_EXPORT uint8_t njs_value_bool(njs_value_t *value); NXT_EXPORT double njs_value_number(njs_value_t *value); NXT_EXPORT void *njs_value_data(njs_value_t *value); NXT_EXPORT njs_function_t *njs_value_function(njs_value_t *value); NXT_EXPORT nxt_int_t njs_value_is_void(njs_value_t *value); -NXT_EXPORT nxt_int_t njs_value_is_true(njs_value_t *value); +NXT_EXPORT nxt_int_t njs_value_is_boolean(njs_value_t *value); NXT_EXPORT nxt_int_t njs_value_is_number(njs_value_t *value); NXT_EXPORT nxt_int_t njs_value_is_string(njs_value_t *value); NXT_EXPORT nxt_int_t njs_value_is_object(njs_value_t *value); From rnickb731 at gmail.com Fri Mar 30 14:30:00 2018 From: rnickb731 at gmail.com (Ryan Burn) Date: Fri, 30 Mar 2018 10:30:00 -0400 Subject: Restrictions to modifying request->headers_in.headers in NGX_HTTP_PREACCESS_PHASE? In-Reply-To: References: <20180328195645.GO77253@mdounin.ru> <20180329134035.GP77253@mdounin.ru> Message-ID: The module is intended to support distributed tracing in a pluggable way. The key/values of the headers added are generated from the module. They're used to support cross process tracing (http://opentracing.io/documentation/pages/api/cross-process-tracing.html) so that the performance information recorded by this module can be linked to the performance information reported by any other code that processes the request. Since the specific headers values used to propagate the tracing context across processes vary by tracing system (for example zipkin uses B3 headers https://github.com/openzipkin/b3-propagation), jaeger uses headers like (uber-trace-id, uberctx-*, etc), I'd rather not have any of those details be exposed to the nginx configuration. Is there any way any way an arbitrary number of headers can be added without requiring the configuration writer to know anything about them? On Thu, Mar 29, 2018 at 2:34 PM, Robert Paprocki wrote: > Hello, > > On Thu, Mar 29, 2018 at 11:06 AM, Ryan Burn wrote: >> >> >> This module injects multiple headers and the number, keys, etc aren't >> meant to be exposed to the user. >> >> (This module is mean to support tracing and the injected headers btw >> correspond to a SpanContext: >> http://opentracing.io/documentation/pages/api/cross-process-tracing.html >> which varies by the trace used). >> >> Is there way to accomplish this without having the configuration >> author do anything? > > > > What's the source of these header names/values? If it's passed from the > downstream client, it will automatically be proxied to the upstream server > (with some exceptions, see the proxy module documentation). > > If it's generated on the Nginx node itself, you should be able to use the > proxy_set_header Maxim defined above, in combination with variables which > could be generated via your custom module or some other approach. > > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > http://mailman.nginx.org/mailman/listinfo/nginx-devel From xeioex at nginx.com Fri Mar 30 16:20:59 2018 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Fri, 30 Mar 2018 16:20:59 +0000 Subject: [njs] Nodejs style crypto methods. Message-ID: details: http://hg.nginx.org/njs/rev/d3747fbfce39 branches: changeset: 477:d3747fbfce39 user: Dmitry Volyntsev date: Fri Mar 30 18:50:38 2018 +0300 description: Nodejs style crypto methods. var crypto = require('crypto') var hash = crypto.createHash() available algorithms: 'md5', 'sha1', 'sha256'. hash.update() hash.digest([]) available encodings: 'hex', 'base64'. var hmac = crypto.createHmac(, ) available algorithms: 'md5', 'sha1', 'sha256'. hmac.update() hmac.digest([]) available encodings: 'hex', 'base64'. diffstat: Makefile | 19 + njs/njs_builtin.c | 17 +- njs/njs_crypto.c | 713 ++++++++++++++++++++++++++++++++++++++++ njs/njs_crypto.h | 24 + njs/njs_object.c | 12 +- njs/njs_vm.c | 10 + njs/njs_vm.h | 32 +- njs/njscript.h | 1 + njs/test/njs_interactive_test.c | 22 + njs/test/njs_unit_test.c | 163 +++++++++ nxt/Makefile | 36 ++ nxt/nxt_md5.c | 270 +++++++++++++++ nxt/nxt_md5.h | 23 + nxt/nxt_sha1.c | 298 ++++++++++++++++ nxt/nxt_sha1.h | 24 + nxt/nxt_sha2.c | 320 +++++++++++++++++ nxt/nxt_sha2.h | 24 + 17 files changed, 1996 insertions(+), 12 deletions(-) diffs (truncated from 2313 to 1000 lines): diff -r be43fdd9579e -r d3747fbfce39 Makefile --- a/Makefile Fri Mar 30 15:46:40 2018 +0300 +++ b/Makefile Fri Mar 30 18:50:38 2018 +0300 @@ -24,6 +24,7 @@ NXT_BUILDDIR = build $(NXT_BUILDDIR)/njs_module.o \ $(NXT_BUILDDIR)/njs_event.o \ $(NXT_BUILDDIR)/njs_fs.o \ + $(NXT_BUILDDIR)/njs_crypto.o \ $(NXT_BUILDDIR)/njs_extern.o \ $(NXT_BUILDDIR)/njs_variable.o \ $(NXT_BUILDDIR)/njs_builtin.o \ @@ -40,6 +41,9 @@ NXT_BUILDDIR = build $(NXT_BUILDDIR)/nxt_lvlhsh.o \ $(NXT_BUILDDIR)/nxt_trace.o \ $(NXT_BUILDDIR)/nxt_random.o \ + $(NXT_BUILDDIR)/nxt_md5.o \ + $(NXT_BUILDDIR)/nxt_sha1.o \ + $(NXT_BUILDDIR)/nxt_sha2.o \ $(NXT_BUILDDIR)/nxt_pcre.o \ $(NXT_BUILDDIR)/nxt_malloc.o \ $(NXT_BUILDDIR)/nxt_mem_cache_pool.o \ @@ -62,6 +66,7 @@ NXT_BUILDDIR = build $(NXT_BUILDDIR)/njs_module.o \ $(NXT_BUILDDIR)/njs_event.o \ $(NXT_BUILDDIR)/njs_fs.o \ + $(NXT_BUILDDIR)/njs_crypto.o \ $(NXT_BUILDDIR)/njs_extern.o \ $(NXT_BUILDDIR)/njs_variable.o \ $(NXT_BUILDDIR)/njs_builtin.o \ @@ -78,6 +83,9 @@ NXT_BUILDDIR = build $(NXT_BUILDDIR)/nxt_lvlhsh.o \ $(NXT_BUILDDIR)/nxt_trace.o \ $(NXT_BUILDDIR)/nxt_random.o \ + $(NXT_BUILDDIR)/nxt_md5.o \ + $(NXT_BUILDDIR)/nxt_sha1.o \ + $(NXT_BUILDDIR)/nxt_sha2.o \ $(NXT_BUILDDIR)/nxt_pcre.o \ $(NXT_BUILDDIR)/nxt_malloc.o \ $(NXT_BUILDDIR)/nxt_mem_cache_pool.o \ @@ -350,6 +358,17 @@ dist: -I$(NXT_LIB) -Injs \ njs/njs_fs.c +$(NXT_BUILDDIR)/njs_crypto.o: \ + $(NXT_BUILDDIR)/libnxt.a \ + njs/njscript.h \ + njs/njs_vm.h \ + njs/njs_crypto.h \ + njs/njs_crypto.c \ + + $(NXT_CC) -c -o $(NXT_BUILDDIR)/njs_crypto.o $(NXT_CFLAGS) \ + -I$(NXT_LIB) -Injs \ + njs/njs_crypto.c + $(NXT_BUILDDIR)/njs_extern.o: \ $(NXT_BUILDDIR)/libnxt.a \ njs/njscript.h \ diff -r be43fdd9579e -r d3747fbfce39 njs/njs_builtin.c --- a/njs/njs_builtin.c Fri Mar 30 15:46:40 2018 +0300 +++ b/njs/njs_builtin.c Fri Mar 30 18:50:38 2018 +0300 @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -58,7 +59,8 @@ const njs_object_init_t *njs_object_i const njs_object_init_t *njs_module_init[] = { - &njs_fs_object_init /* fs */ + &njs_fs_object_init, /* fs */ + &njs_crypto_object_init /* crypto */ }; @@ -71,6 +73,8 @@ const njs_object_init_t *njs_prototype_ &njs_function_prototype_init, &njs_regexp_prototype_init, &njs_date_prototype_init, + &njs_hash_prototype_init, + &njs_hmac_prototype_init, &njs_error_prototype_init, &njs_eval_error_prototype_init, &njs_internal_error_prototype_init, @@ -91,6 +95,8 @@ const njs_object_init_t *njs_construc &njs_function_constructor_init, &njs_regexp_constructor_init, &njs_date_constructor_init, + &njs_hash_constructor_init, + &njs_hmac_constructor_init, &njs_error_constructor_init, &njs_eval_error_constructor_init, &njs_internal_error_constructor_init, @@ -192,6 +198,12 @@ njs_builtin_objects_create(njs_vm_t *vm) { .date = { .time = NAN, .object = { .type = NJS_DATE } } }, + { .object_value = { .value = njs_value(NJS_DATA, 0, 0.0), + .object = { .type = NJS_OBJECT } } }, + + { .object_value = { .value = njs_value(NJS_DATA, 0, 0.0), + .object = { .type = NJS_OBJECT } } }, + { .object = { .type = NJS_OBJECT_ERROR } }, { .object = { .type = NJS_OBJECT_EVAL_ERROR } }, { .object = { .type = NJS_OBJECT_INTERNAL_ERROR } }, @@ -214,6 +226,9 @@ njs_builtin_objects_create(njs_vm_t *vm) { njs_regexp_constructor, { NJS_SKIP_ARG, NJS_STRING_ARG, NJS_STRING_ARG } }, { njs_date_constructor, { 0 } }, + { njs_hash_constructor, { NJS_SKIP_ARG, NJS_STRING_ARG } }, + { njs_hmac_constructor, { NJS_SKIP_ARG, NJS_STRING_ARG, + NJS_STRING_ARG } }, { njs_error_constructor, { NJS_SKIP_ARG, NJS_STRING_ARG } }, { njs_eval_error_constructor, { NJS_SKIP_ARG, NJS_STRING_ARG } }, { njs_internal_error_constructor, diff -r be43fdd9579e -r d3747fbfce39 njs/njs_crypto.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/njs/njs_crypto.c Fri Mar 30 18:50:38 2018 +0300 @@ -0,0 +1,713 @@ + +/* + * Copyright (C) Dmitry Volyntsev + * Copyright (C) NGINX, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +typedef void (*njs_hash_init)(void *ctx); +typedef void (*njs_hash_update)(void *ctx, const void *data, size_t size); +typedef void (*njs_hash_final)(u_char *result, void *ctx); + +typedef njs_ret_t (*njs_digest_encode)(njs_vm_t *vm, njs_value_t *value, + const nxt_str_t *src); + + +typedef struct { + nxt_str_t name; + + size_t size; + njs_hash_init init; + njs_hash_update update; + njs_hash_final final; +} njs_hash_alg_t; + +typedef struct { + union { + nxt_md5_t md5; + nxt_sha1_t sha1; + nxt_sha2_t sha2; + } u; + + njs_hash_alg_t *alg; +} njs_digest_t; + +typedef struct { + nxt_str_t key; + u_char opad[64]; + + union { + nxt_md5_t md5; + nxt_sha1_t sha1; + nxt_sha2_t sha2; + } u; + + njs_hash_alg_t *alg; +} njs_hmac_t; + + +typedef struct { + nxt_str_t name; + + njs_digest_encode encode; +} njs_crypto_enc_t; + + +static njs_hash_alg_t njs_hash_algorithms[] = { + + { + nxt_string("md5"), + 16, + (njs_hash_init) nxt_md5_init, + (njs_hash_update) nxt_md5_update, + (njs_hash_final) nxt_md5_final + }, + + { + nxt_string("sha1"), + 20, + (njs_hash_init) nxt_sha1_init, + (njs_hash_update) nxt_sha1_update, + (njs_hash_final) nxt_sha1_final + }, + + { + nxt_string("sha256"), + 32, + (njs_hash_init) nxt_sha2_init, + (njs_hash_update) nxt_sha2_update, + (njs_hash_final) nxt_sha2_final + }, + + { + nxt_null_string, + 0, + NULL, + NULL, + NULL + } + +}; + +static njs_crypto_enc_t njs_encodings[] = { + + { + nxt_string("hex"), + njs_string_hex + }, + + { + nxt_string("base64"), + njs_string_base64 + }, + + { + nxt_null_string, + NULL + } +}; + + +static njs_hash_alg_t *njs_crypto_alg(njs_vm_t *vm, const nxt_str_t *name); +static njs_crypto_enc_t *njs_crypto_encoding(njs_vm_t *vm, + const nxt_str_t *name); + + +static njs_object_value_t * +njs_crypto_object_value_alloc(njs_vm_t *vm, nxt_uint_t proto) +{ + njs_object_value_t *ov; + + ov = nxt_mem_cache_alloc(vm->mem_cache_pool, sizeof(njs_object_value_t)); + + if (nxt_fast_path(ov != NULL)) { + nxt_lvlhsh_init(&ov->object.hash); + nxt_lvlhsh_init(&ov->object.shared_hash); + ov->object.type = NJS_OBJECT_VALUE; + ov->object.shared = 0; + ov->object.extensible = 1; + + ov->object.__proto__ = &vm->prototypes[proto].object; + } + + return ov; +} + + +static njs_ret_t +njs_crypto_create_hash(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + nxt_str_t alg_name; + njs_digest_t *dgst; + njs_hash_alg_t *alg; + njs_object_value_t *hash; + + if (nxt_slow_path(nargs < 2 || !njs_is_string(&args[1]))) { + njs_type_error(vm, "algorithm must be a string", NULL); + return NJS_ERROR; + } + + njs_string_get(&args[1], &alg_name); + + alg = njs_crypto_alg(vm, &alg_name); + if (nxt_slow_path(alg == NULL)) { + return NJS_ERROR; + } + + hash = njs_crypto_object_value_alloc(vm, NJS_PROTOTYPE_CRYPTO_HASH); + if (nxt_slow_path(hash == NULL)) { + goto memory_error; + } + + dgst = nxt_mem_cache_alloc(vm->mem_cache_pool, sizeof(njs_digest_t)); + if (nxt_slow_path(dgst == NULL)) { + goto memory_error; + } + + dgst->alg = alg; + + alg->init(&dgst->u); + + njs_value_data_set(&hash->value, dgst); + + vm->retval.data.u.object_value = hash; + vm->retval.type = NJS_OBJECT_VALUE; + vm->retval.data.truth = 1; + + return NJS_OK; + +memory_error: + + njs_memory_error(vm); + + return NJS_ERROR; +} + + +static njs_ret_t +njs_hash_prototype_update(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + nxt_str_t data; + njs_digest_t *dgst; + njs_object_value_t *hash; + + if (nxt_slow_path(nargs < 2 || !njs_is_string(&args[1]))) { + njs_type_error(vm, "data must be a string", NULL); + return NJS_ERROR; + } + + if (nxt_slow_path(!njs_is_object_value(&args[0]))) { + njs_type_error(vm, "'this' is not an object_value", NULL); + return NJS_ERROR; + } + + if (nxt_slow_path(!njs_is_data(&args[0].data.u.object_value->value))) { + njs_type_error(vm, "value of 'this' is not a data type", NULL); + return NJS_ERROR; + } + + hash = args[0].data.u.object_value; + + njs_string_get(&args[1], &data); + + dgst = njs_value_data(&hash->value); + dgst->alg->update(&dgst->u, data.start, data.length); + + vm->retval = args[0]; + + return NJS_OK; +} + + +static njs_ret_t +njs_hash_prototype_digest(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + u_char digest[32], *p; + njs_ret_t ret; + nxt_str_t enc_name, str; + njs_digest_t *dgst; + njs_hash_alg_t *alg; + njs_crypto_enc_t *enc; + njs_object_value_t *hash; + + if (nxt_slow_path(nargs > 1 && !njs_is_string(&args[1]))) { + njs_type_error(vm, "encoding must be a string", NULL); + return NJS_ERROR; + } + + if (nxt_slow_path(!njs_is_object_value(&args[0]))) { + njs_type_error(vm, "'this' is not an object_value", NULL); + return NJS_ERROR; + } + + if (nxt_slow_path(!njs_is_data(&args[0].data.u.object_value->value))) { + njs_type_error(vm, "value of 'this' is not a data type", NULL); + return NJS_ERROR; + } + + enc = NULL; + + if (nargs > 1) { + njs_string_get(&args[1], &enc_name); + + enc = njs_crypto_encoding(vm, &enc_name); + if (nxt_slow_path(enc == NULL)) { + return NJS_ERROR; + } + } + + hash = args[0].data.u.object_value; + + dgst = njs_value_data(&hash->value); + + if (nxt_slow_path(dgst->alg == NULL)) { + njs_error(vm, "Digest already called", NULL); + return NJS_ERROR; + } + + alg = dgst->alg; + + alg->final(digest, &dgst->u); + + str.start = digest; + str.length = alg->size; + + if (enc == NULL) { + p = njs_string_alloc(vm, &vm->retval, str.length, 0); + + if (nxt_fast_path(p != NULL)) { + memcpy(p, str.start, str.length); + ret = NJS_OK; + + } else { + ret = NJS_ERROR; + } + + } else { + ret = enc->encode(vm, &vm->retval, &str); + } + + dgst->alg = NULL; + + return ret; +} + + +static njs_ret_t +njs_hash_prototype_to_string(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + static const njs_value_t string = njs_string("[object Hash]"); + + vm->retval = string; + + return NJS_OK; +} + + +static const njs_object_prop_t njs_hash_prototype_properties[] = +{ + { + .type = NJS_PROPERTY, + .name = njs_string("name"), + .value = njs_string("Hash"), + }, + + { + .type = NJS_METHOD, + .name = njs_string("toString"), + .value = njs_native_function(njs_hash_prototype_to_string, 0, 0), + }, + + { + .type = NJS_METHOD, + .name = njs_string("update"), + .value = njs_native_function(njs_hash_prototype_update, 0, + NJS_OBJECT_ARG, NJS_SKIP_ARG), + }, + + { + .type = NJS_METHOD, + .name = njs_string("digest"), + .value = njs_native_function(njs_hash_prototype_digest, 0, + NJS_OBJECT_ARG, NJS_SKIP_ARG), + }, +}; + + +const njs_object_init_t njs_hash_prototype_init = { + nxt_string("Hash"), + njs_hash_prototype_properties, + nxt_nitems(njs_hash_prototype_properties), +}; + + +njs_ret_t +njs_hash_constructor(njs_vm_t *vm, njs_value_t *args, + nxt_uint_t nargs, njs_index_t unused) +{ + return njs_crypto_create_hash(vm, args, nargs, unused); +} + + +const njs_object_init_t njs_hash_constructor_init = { + nxt_string("Hash"), + NULL, + 0, +}; + + +static njs_ret_t +njs_crypto_create_hmac(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + u_char digest[32], key_buf[64]; + nxt_str_t alg_name, key; + nxt_uint_t i; + njs_hmac_t *ctx; + njs_hash_alg_t *alg; + njs_object_value_t *hmac; + + if (nxt_slow_path(nargs < 2 || !njs_is_string(&args[1]))) { + njs_type_error(vm, "algorithm must be a string", NULL); + return NJS_ERROR; + } + + if (nxt_slow_path(nargs < 3 || !njs_is_string(&args[2]))) { + njs_type_error(vm, "key must be a string", NULL); + return NJS_ERROR; + } + + njs_string_get(&args[1], &alg_name); + + alg = njs_crypto_alg(vm, &alg_name); + if (nxt_slow_path(alg == NULL)) { + return NJS_ERROR; + } + + njs_string_get(&args[2], &key); + + ctx = nxt_mem_cache_alloc(vm->mem_cache_pool, sizeof(njs_hmac_t)); + if (nxt_slow_path(ctx == NULL)) { + goto memory_error; + } + + ctx->alg = alg; + + if (key.length > 64) { + alg->init(&ctx->u); + alg->update(&ctx->u, key.start, key.length); + alg->final(digest, &ctx->u); + + memcpy(key_buf, digest, alg->size); + memset(key_buf + alg->size, 0, sizeof(key_buf) - alg->size); + + } else if (key.length < alg->size) { + + memcpy(key_buf, key.start, key.length); + memset(key_buf + key.length, 0, sizeof(key_buf) - key.length); + + } else { + memcpy(key_buf, key.start, sizeof(key_buf)); + } + + for (i = 0; i < 64; i++) { + ctx->opad[i] = key_buf[i] ^ 0x5c; + } + + for (i = 0; i < 64; i++) { + key_buf[i] ^= 0x36; + } + + alg->init(&ctx->u); + alg->update(&ctx->u, key_buf, 64); + + hmac = njs_crypto_object_value_alloc(vm, NJS_PROTOTYPE_CRYPTO_HMAC); + if (nxt_slow_path(hmac == NULL)) { + goto memory_error; + } + + njs_value_data_set(&hmac->value, ctx); + + vm->retval.data.u.object_value = hmac; + vm->retval.type = NJS_OBJECT_VALUE; + vm->retval.data.truth = 1; + + return NJS_OK; + +memory_error: + + njs_memory_error(vm); + + return NJS_ERROR; +} + + +static njs_ret_t +njs_hmac_prototype_update(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + nxt_str_t data; + njs_hmac_t *ctx; + njs_object_value_t *hmac; + + if (nxt_slow_path(nargs < 2 || !njs_is_string(&args[1]))) { + njs_type_error(vm, "data must be a string", NULL); + return NJS_ERROR; + } + + if (nxt_slow_path(!njs_is_object_value(&args[0]))) { + njs_type_error(vm, "'this' is not an object_value", NULL); + return NJS_ERROR; + } + + if (nxt_slow_path(!njs_is_data(&args[0].data.u.object_value->value))) { + njs_type_error(vm, "value of 'this' is not a data type", NULL); + return NJS_ERROR; + } + + hmac = args[0].data.u.object_value; + + njs_string_get(&args[1], &data); + + ctx = njs_value_data(&hmac->value); + ctx->alg->update(&ctx->u, data.start, data.length); + + vm->retval = args[0]; + + return NJS_OK; +} + + +static njs_ret_t +njs_hmac_prototype_digest(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + u_char hash1[32], digest[32], *p; + nxt_str_t enc_name, str; + njs_ret_t ret; + njs_hmac_t *ctx; + njs_hash_alg_t *alg; + njs_crypto_enc_t *enc; + njs_object_value_t *hmac; + + if (nxt_slow_path(nargs > 1 && !njs_is_string(&args[1]))) { + njs_type_error(vm, "encoding must be a string", NULL); + return NJS_ERROR; + } + + if (nxt_slow_path(!njs_is_object_value(&args[0]))) { + njs_type_error(vm, "'this' is not an object_value", NULL); + return NJS_ERROR; + } + + if (nxt_slow_path(!njs_is_data(&args[0].data.u.object_value->value))) { + njs_type_error(vm, "value of 'this' is not a data type", NULL); + return NJS_ERROR; + } + + enc = NULL; + + if (nargs > 1) { + njs_string_get(&args[1], &enc_name); + + enc = njs_crypto_encoding(vm, &enc_name); + if (nxt_slow_path(enc == NULL)) { + return NJS_ERROR; + } + } + + hmac = args[0].data.u.object_value; + + ctx = njs_value_data(&hmac->value); + + if (nxt_slow_path(ctx->alg == NULL)) { + njs_error(vm, "Digest already called", NULL); + return NJS_ERROR; + } + + alg = ctx->alg; + + alg->final(hash1, &ctx->u); + + alg->init(&ctx->u); + alg->update(&ctx->u, ctx->opad, 64); + alg->update(&ctx->u, hash1, alg->size); + alg->final(digest, &ctx->u); + + str.start = digest; + str.length = alg->size; + + if (enc == NULL) { + p = njs_string_alloc(vm, &vm->retval, str.length, 0); + + if (nxt_fast_path(p != NULL)) { + memcpy(p, str.start, str.length); + ret = NJS_OK; + + } else { + ret = NJS_ERROR; + } + + } else { + ret = enc->encode(vm, &vm->retval, &str); + } + + ctx->alg = NULL; + + return ret; +} + + +static njs_ret_t +njs_hmac_prototype_to_string(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + static const njs_value_t string = njs_string("[object Hmac]"); + + vm->retval = string; + + return NJS_OK; +} + + +static const njs_object_prop_t njs_hmac_prototype_properties[] = +{ + { + .type = NJS_PROPERTY, + .name = njs_string("name"), + .value = njs_string("Hmac"), + }, + + { + .type = NJS_METHOD, + .name = njs_string("toString"), + .value = njs_native_function(njs_hmac_prototype_to_string, 0, 0), + }, + + { + .type = NJS_METHOD, + .name = njs_string("update"), + .value = njs_native_function(njs_hmac_prototype_update, 0, + NJS_OBJECT_ARG, NJS_SKIP_ARG), + }, + + { + .type = NJS_METHOD, + .name = njs_string("digest"), + .value = njs_native_function(njs_hmac_prototype_digest, 0, + NJS_OBJECT_ARG, NJS_SKIP_ARG), + }, +}; + + +const njs_object_init_t njs_hmac_prototype_init = { + nxt_string("Hmac"), + njs_hmac_prototype_properties, + nxt_nitems(njs_hmac_prototype_properties), +}; + + +njs_ret_t +njs_hmac_constructor(njs_vm_t *vm, njs_value_t *args, + nxt_uint_t nargs, njs_index_t unused) +{ + return njs_crypto_create_hmac(vm, args, nargs, unused); +} + + +const njs_object_init_t njs_hmac_constructor_init = { + nxt_string("Hmac"), + NULL, + 0, +}; + + +static const njs_object_prop_t njs_crypto_object_properties[] = +{ + { + .type = NJS_METHOD, + .name = njs_string("createHash"), + .value = njs_native_function(njs_crypto_create_hash, 0, + NJS_SKIP_ARG), + }, + + { + .type = NJS_METHOD, + .name = njs_string("createHmac"), + .value = njs_native_function(njs_crypto_create_hmac, 0, + NJS_SKIP_ARG), + }, + +}; + + +const njs_object_init_t njs_crypto_object_init = { + nxt_string("crypto"), + njs_crypto_object_properties, + nxt_nitems(njs_crypto_object_properties), +}; + + +static njs_hash_alg_t * +njs_crypto_alg(njs_vm_t *vm, const nxt_str_t *name) +{ + njs_hash_alg_t *e; + + for (e = &njs_hash_algorithms[0]; e->name.length != 0; e++) { + if (nxt_strstr_eq(name, &e->name)) { + return e; + } + } + + njs_type_error(vm, "not supported algorithm: '%.*s'", + (int) name->length, name->start); + + return NULL; +} + + +static njs_crypto_enc_t * +njs_crypto_encoding(njs_vm_t *vm, const nxt_str_t *name) +{ + njs_crypto_enc_t *e; + + for (e = &njs_encodings[0]; e->name.length != 0; e++) { + if (nxt_strstr_eq(name, &e->name)) { + return e; + } + } + + njs_type_error(vm, "Unknown digest encoding: '%.*s'", + (int) name->length, name->start); + + return NULL; +} diff -r be43fdd9579e -r d3747fbfce39 njs/njs_crypto.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/njs/njs_crypto.h Fri Mar 30 18:50:38 2018 +0300 @@ -0,0 +1,24 @@ + +/* + * Copyright (C) Dmitry Volyntsev + * Copyright (C) NGINX, Inc. + */ + +#ifndef _NJS_CRYPTO_H_INCLUDED_ +#define _NJS_CRYPTO_H_INCLUDED_ + +njs_ret_t njs_hash_constructor(njs_vm_t *vm, njs_value_t *args, + nxt_uint_t nargs, njs_index_t unused); +njs_ret_t njs_hmac_constructor(njs_vm_t *vm, njs_value_t *args, + nxt_uint_t nargs, njs_index_t unused); + +extern const njs_object_init_t njs_crypto_object_init; + +extern const njs_object_init_t njs_hash_prototype_init; +extern const njs_object_init_t njs_hmac_prototype_init; + +extern const njs_object_init_t njs_hash_constructor_init; +extern const njs_object_init_t njs_hmac_constructor_init; + + +#endif /* _NJS_CRYPTO_H_INCLUDED_ */ diff -r be43fdd9579e -r d3747fbfce39 njs/njs_object.c --- a/njs/njs_object.c Fri Mar 30 15:46:40 2018 +0300 +++ b/njs/njs_object.c Fri Mar 30 18:50:38 2018 +0300 @@ -1332,6 +1332,10 @@ static const njs_value_t njs_object_num njs_long_string("[object Number]"); static const njs_value_t njs_object_string_string = njs_long_string("[object String]"); +static const njs_value_t njs_object_data_string = + njs_string("[object Data]"); +static const njs_value_t njs_object_exernal_string = + njs_long_string("[object External]"); static const njs_value_t njs_object_object_string = njs_long_string("[object Object]"); static const njs_value_t njs_object_array_string = @@ -1357,6 +1361,9 @@ static const njs_value_t njs_object_typ njs_long_string("[object TypeError]"); static const njs_value_t njs_object_uri_error_string = njs_long_string("[object URIError]"); +static const njs_value_t njs_object_object_value_string = + njs_long_string("[object ObjectValue]"); + njs_ret_t @@ -1375,8 +1382,8 @@ njs_object_prototype_to_string(njs_vm_t &njs_object_number_string, &njs_object_string_string, - &njs_string_empty, - &njs_object_function_string, + &njs_object_data_string, + &njs_object_exernal_string, &njs_string_empty, &njs_string_empty, &njs_string_empty, @@ -1404,6 +1411,7 @@ njs_object_prototype_to_string(njs_vm_t &njs_object_syntax_error_string, &njs_object_type_error_string, &njs_object_uri_error_string, + &njs_object_object_value_string, }; index = args[0].type; diff -r be43fdd9579e -r d3747fbfce39 njs/njs_vm.c --- a/njs/njs_vm.c Fri Mar 30 15:46:40 2018 +0300 +++ b/njs/njs_vm.c Fri Mar 30 18:50:38 2018 +0300 @@ -1036,6 +1036,7 @@ njs_property_query(njs_vm_t *vm, njs_pro case NJS_OBJECT_SYNTAX_ERROR: case NJS_OBJECT_TYPE_ERROR: case NJS_OBJECT_URI_ERROR: + case NJS_OBJECT_VALUE: obj = object->data.u.object; break; @@ -3650,6 +3651,15 @@ njs_value_number_set(njs_value_t *value, } +nxt_noinline void +njs_value_data_set(njs_value_t *value, void *data) +{ + value->data.u.data = data; + value->type = NJS_DATA; + value->data.truth = 1; +} + + void njs_value_error_set(njs_vm_t *vm, njs_value_t *value, const char *fmt, ...) { diff -r be43fdd9579e -r d3747fbfce39 njs/njs_vm.h --- a/njs/njs_vm.h Fri Mar 30 15:46:40 2018 +0300 +++ b/njs/njs_vm.h Fri Mar 30 18:50:38 2018 +0300 @@ -76,7 +76,7 @@ typedef enum { /* The order of the above type is used in njs_is_primitive(). */ - /* Reserved 0x05, */ + NJS_DATA = 0x05, /* The type is external code. */ NJS_EXTERNAL = 0x06, @@ -90,7 +90,7 @@ typedef enum { NJS_INVALID = 0x07, /* - * The object types have the fourth bit set. It is used in njs_is_object(). + * The object types are >= NJS_OBJECT, this is used in njs_is_object(). * NJS_OBJECT_BOOLEAN, NJS_OBJECT_NUMBER, and NJS_OBJECT_STRING must be * in the same order as NJS_BOOLEAN, NJS_NUMBER, and NJS_STRING. It is * used in njs_primitive_prototype_index(). The order of object types @@ -112,6 +112,7 @@ typedef enum { NJS_OBJECT_SYNTAX_ERROR = 0x1d, NJS_OBJECT_TYPE_ERROR = 0x1e, NJS_OBJECT_URI_ERROR = 0x1f, + NJS_OBJECT_VALUE = 0x20, } njs_value_type_t; @@ -154,7 +155,7 @@ union njs_value_s { * the maximum size of short string to 13. */ struct { - njs_value_type_t type:8; /* 5 bits */ + njs_value_type_t type:8; /* 6 bits */ /* * The truth field is set during value assignment and then can be * quickly tested by logical and conditional operations regardless @@ -184,7 +185,7 @@ union njs_value_s { } data; struct { - njs_value_type_t type:8; /* 5 bits */ + njs_value_type_t type:8; /* 6 bits */ #define NJS_STRING_SHORT 14 #define NJS_STRING_LONG 15 @@ -196,7 +197,7 @@ union njs_value_s { } short_string; struct { - njs_value_type_t type:8; /* 5 bits */ + njs_value_type_t type:8; /* 6 bits */ uint8_t truth; /* 0xff if data is external string. */ @@ -208,7 +209,7 @@ union njs_value_s { } long_string; struct { - njs_value_type_t type:8; /* 5 bits */ + njs_value_type_t type:8; /* 6 bits */ uint8_t truth; uint16_t _spare; @@ -217,7 +218,7 @@ union njs_value_s { From xeioex at nginx.com Fri Mar 30 17:11:22 2018 From: xeioex at nginx.com (Dmitry Volyntsev) Date: Fri, 30 Mar 2018 17:11:22 +0000 Subject: [njs] Removed excessive debug log for HTTP req.send(). Message-ID: details: http://hg.nginx.org/njs/rev/7a7160de8098 branches: changeset: 478:7a7160de8098 user: Dmitry Volyntsev date: Fri Mar 30 20:11:10 2018 +0300 description: Removed excessive debug log for HTTP req.send(). diffstat: nginx/ngx_http_js_module.c | 3 --- 1 files changed, 0 insertions(+), 3 deletions(-) diffs (13 lines): diff -r d3747fbfce39 -r 7a7160de8098 nginx/ngx_http_js_module.c --- a/nginx/ngx_http_js_module.c Fri Mar 30 18:50:38 2018 +0300 +++ b/nginx/ngx_http_js_module.c Fri Mar 30 20:11:10 2018 +0300 @@ -1202,9 +1202,6 @@ ngx_http_js_ext_send(njs_vm_t *vm, njs_v /* TODO: njs_value_release(vm, value) in buf completion */ - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http js send: \"%*s\"", s.length, s.start); - b = ngx_calloc_buf(r->pool); if (b == NULL) { return NJS_ERROR; From eran.kornblau at kaltura.com Sat Mar 31 14:37:56 2018 From: eran.kornblau at kaltura.com (Eran Kornblau) Date: Sat, 31 Mar 2018 14:37:56 +0000 Subject: Restrictions to modifying request->headers_in.headers in NGX_HTTP_PREACCESS_PHASE? In-Reply-To: References: <20180328195645.GO77253@mdounin.ru> <20180329134035.GP77253@mdounin.ru> Message-ID: > -----Original Message----- > From: nginx-devel [mailto:nginx-devel-bounces at nginx.org] On Behalf Of Ryan Burn > Sent: Friday, March 30, 2018 5:30 PM > To: nginx-devel at nginx.org; robert at cryptobells.com > Subject: Re: Restrictions to modifying request->headers_in.headers in NGX_HTTP_PREACCESS_PHASE? > > The module is intended to support distributed tracing in a pluggable way. > > The key/values of the headers added are generated from the module. > They're used to support cross process tracing > (http://opentracing.io/documentation/pages/api/cross-process-tracing.html) > so that the performance information recorded by this module can be linked to the performance information reported by any other code that processes the request. > > Since the specific headers values used to propagate the tracing context across processes vary by tracing system (for example zipkin uses B3 headers https://github.com/openzipkin/b3-propagation), jaeger uses headers like (uber-trace-id, uberctx-*, etc), I'd rather not have any of those details be exposed to the nginx configuration. > > Is there any way any way an arbitrary number of headers can be added without requiring the configuration writer to know anything about them? > A bit hacky, but what you can do is copy the function 'ngx_conf_handler' to your module (you can't use the existing one since it's static...), then add a command handler - static char * ngx_http_my_proxy_set_header(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_array_t* old_args; ngx_array_t args_arr; ngx_str_t args[3] = { ngx_string("proxy_set_header"), ngx_string("header-name"), ngx_string("header-value"), }; args_arr.elts = &args; args_arr.nelts = sizeof(args) / sizeof(args[0]); old_args = cf->args; cf->args = &args_arr; if (ngx_conf_handler(cf, 0) != NGX_OK) { return NGX_CONF_ERROR; } cf->args = old_args; return NGX_CONF_OK; } And add this to the commands array of your module - { ngx_string("my_proxy_set_header"), NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_NOARGS, ngx_http_my_proxy_set_header, NGX_HTTP_LOC_CONF_OFFSET, 0, NULL }, nginx.conf will look like this - location /something/ { proxy_pass http://my_upstream; my_proxy_set_header; } In this specific example, the my_proxy_set_header directive will essentially translate to 'proxy_set_header header-name header-value', and you can repeat this multiple times to add multiple headers. Eran