# HG changeset patch # User Igor Sysoev # Date 1323633600 -14400 # Node ID 753f505670e0e06fac8923f7525cdad6bb34ec2b # Parent 8c96af2112c18a20e6206edb1e71998358ceeccb nginx 1.1.11 *) Feature: the "so_keepalive" parameter of the "listen" directive. Thanks to Vsevolod Stakhov. *) Feature: the "if_not_empty" parameter of the "fastcgi/scgi/uwsgi_param" directives. *) Feature: the $https variable. *) Feature: the "proxy_redirect" directive supports variables in the first parameter. *) Feature: the "proxy_redirect" directive supports regular expressions. *) Bugfix: the $sent_http_cache_control variable might contain a wrong value if the "expires" directive was used. Thanks to Yichun Zhang. *) Bugfix: the "read_ahead" directive might not work combined with "try_files" and "open_file_cache". *) Bugfix: a segmentation fault might occur in a worker process if small time was used in the "inactive" parameter of the "proxy_cache_path" directive. *) Bugfix: responses from cache might hang. diff --git a/CHANGES b/CHANGES --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,33 @@ +Changes with nginx 1.1.11 12 Dec 2011 + + *) Feature: the "so_keepalive" parameter of the "listen" directive. + Thanks to Vsevolod Stakhov. + + *) Feature: the "if_not_empty" parameter of the + "fastcgi/scgi/uwsgi_param" directives. + + *) Feature: the $https variable. + + *) Feature: the "proxy_redirect" directive supports variables in the + first parameter. + + *) Feature: the "proxy_redirect" directive supports regular expressions. + + *) Bugfix: the $sent_http_cache_control variable might contain a wrong + value if the "expires" directive was used. + Thanks to Yichun Zhang. + + *) Bugfix: the "read_ahead" directive might not work combined with + "try_files" and "open_file_cache". + + *) Bugfix: a segmentation fault might occur in a worker process if small + time was used in the "inactive" parameter of the "proxy_cache_path" + directive. + + *) Bugfix: responses from cache might hang. + + Changes with nginx 1.1.10 30 Nov 2011 *) Bugfix: a segmentation fault occured in a worker process if AIO was diff --git a/CHANGES.ru b/CHANGES.ru --- a/CHANGES.ru +++ b/CHANGES.ru @@ -1,4 +1,34 @@ +Изменения в nginx 1.1.11 12.12.2011 + + *) Добавление: параметр so_keepalive в директиве listen. + Спасибо Всеволоду Стахову. + + *) Добавление: параметр if_not_empty в директивах + fastcgi/scgi/uwsgi_param. + + *) Добавление: переменная $https. + + *) Добавление: директива proxy_redirect поддерживает переменные в первом + параметре. + + *) Добавление: директива proxy_redirect поддерживает регулярные + выражения. + + *) Исправление: переменная $sent_http_cache_control могла содержать + неверное значение при использовании директивы expires. + Спасибо Yichun Zhang. + + *) Исправление: директива read_ahead могла не работать при использовании + совместно с try_files и open_file_cache. + + *) Исправление: если в параметре inactive директивы proxy_cache_path + было указано малое время, в рабочем процессе мог произойти + segmentation fault. + + *) Исправление: ответы из кэша могли зависать. + + Изменения в nginx 1.1.10 30.11.2011 *) Исправление: при использовании AIO на Linux в рабочем процессе diff --git a/auto/unix b/auto/unix --- a/auto/unix +++ b/auto/unix @@ -328,6 +328,20 @@ ngx_feature_test="setsockopt(0, IPPROTO_ . auto/feature +ngx_feature="TCP_KEEPIDLE, TCP_KEEPINTVL, TCP_KEEPCNT" +ngx_feature_name="NGX_HAVE_KEEPALIVE_TUNABLE" +ngx_feature_run=no +ngx_feature_incs="#include + #include + #include " +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="setsockopt(0, IPPROTO_TCP, TCP_KEEPIDLE, NULL, 0); + setsockopt(0, IPPROTO_TCP, TCP_KEEPINTVL, NULL, 0); + setsockopt(0, IPPROTO_TCP, TCP_KEEPCNT, NULL, 0)" +. auto/feature + + ngx_feature="accept4()" ngx_feature_name="NGX_HAVE_ACCEPT4" ngx_feature_run=no diff --git a/conf/fastcgi_params b/conf/fastcgi_params --- a/conf/fastcgi_params +++ b/conf/fastcgi_params @@ -9,6 +9,7 @@ fastcgi_param REQUEST_URI $reque fastcgi_param DOCUMENT_URI $document_uri; fastcgi_param DOCUMENT_ROOT $document_root; fastcgi_param SERVER_PROTOCOL $server_protocol; +fastcgi_param HTTPS $https if_not_empty; fastcgi_param GATEWAY_INTERFACE CGI/1.1; fastcgi_param SERVER_SOFTWARE nginx/$nginx_version; diff --git a/conf/scgi_params b/conf/scgi_params --- a/conf/scgi_params +++ b/conf/scgi_params @@ -8,6 +8,7 @@ scgi_param DOCUMENT_URI $document scgi_param DOCUMENT_ROOT $document_root; scgi_param SCGI 1; scgi_param SERVER_PROTOCOL $server_protocol; +scgi_param HTTPS $https if_not_empty; scgi_param REMOTE_ADDR $remote_addr; scgi_param REMOTE_PORT $remote_port; diff --git a/conf/uwsgi_params b/conf/uwsgi_params --- a/conf/uwsgi_params +++ b/conf/uwsgi_params @@ -8,6 +8,7 @@ uwsgi_param REQUEST_URI $request uwsgi_param PATH_INFO $document_uri; uwsgi_param DOCUMENT_ROOT $document_root; uwsgi_param SERVER_PROTOCOL $server_protocol; +uwsgi_param HTTPS $https if_not_empty; uwsgi_param REMOTE_ADDR $remote_addr; uwsgi_param REMOTE_PORT $remote_port; diff --git a/src/core/nginx.h b/src/core/nginx.h --- a/src/core/nginx.h +++ b/src/core/nginx.h @@ -8,8 +8,8 @@ #define _NGINX_H_INCLUDED_ -#define nginx_version 1001010 -#define NGINX_VERSION "1.1.10" +#define nginx_version 1001011 +#define NGINX_VERSION "1.1.11" #define NGINX_VER "nginx/" NGINX_VERSION #define NGINX_VAR "NGINX" diff --git a/src/core/ngx_connection.c b/src/core/ngx_connection.c --- a/src/core/ngx_connection.c +++ b/src/core/ngx_connection.c @@ -462,6 +462,7 @@ ngx_open_listening_sockets(ngx_cycle_t * void ngx_configure_listening_sockets(ngx_cycle_t *cycle) { + int keepalive; ngx_uint_t i; ngx_listening_t *ls; @@ -499,6 +500,56 @@ ngx_configure_listening_sockets(ngx_cycl } } + if (ls[i].keepalive) { + keepalive = (ls[i].keepalive == 1) ? 1 : 0; + + if (setsockopt(ls[i].fd, SOL_SOCKET, SO_KEEPALIVE, + (const void *) &keepalive, sizeof(int)) + == -1) + { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, + "setsockopt(SO_KEEPALIVE, %d) %V failed, ignored", + keepalive, &ls[i].addr_text); + } + } + +#if (NGX_HAVE_KEEPALIVE_TUNABLE) + + if (ls[i].keepidle) { + if (setsockopt(ls[i].fd, IPPROTO_TCP, TCP_KEEPIDLE, + (const void *) &ls[i].keepidle, sizeof(int)) + == -1) + { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, + "setsockopt(TCP_KEEPIDLE, %d) %V failed, ignored", + ls[i].keepidle, &ls[i].addr_text); + } + } + + if (ls[i].keepintvl) { + if (setsockopt(ls[i].fd, IPPROTO_TCP, TCP_KEEPINTVL, + (const void *) &ls[i].keepintvl, sizeof(int)) + == -1) + { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, + "setsockopt(TCP_KEEPINTVL, %d) %V failed, ignored", + ls[i].keepintvl, &ls[i].addr_text); + } + } + + if (ls[i].keepcnt) { + if (setsockopt(ls[i].fd, IPPROTO_TCP, TCP_KEEPCNT, + (const void *) &ls[i].keepcnt, sizeof(int)) + == -1) + { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, + "setsockopt(TCP_KEEPCNT, %d) %V failed, ignored", + ls[i].keepcnt, &ls[i].addr_text); + } + } + +#endif + #if (NGX_HAVE_SETFIB) if (ls[i].setfib != -1) { if (setsockopt(ls[i].fd, SOL_SOCKET, SO_SETFIB, diff --git a/src/core/ngx_connection.h b/src/core/ngx_connection.h --- a/src/core/ngx_connection.h +++ b/src/core/ngx_connection.h @@ -27,6 +27,11 @@ struct ngx_listening_s { int backlog; int rcvbuf; int sndbuf; +#if (NGX_HAVE_KEEPALIVE_TUNABLE) + int keepidle; + int keepintvl; + int keepcnt; +#endif /* handler of accepted connection */ ngx_connection_handler_pt handler; @@ -60,6 +65,7 @@ struct ngx_listening_s { #if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) unsigned ipv6only:2; #endif + unsigned keepalive:2; #if (NGX_HAVE_DEFERRED_ACCEPT) unsigned deferred_accept:1; diff --git a/src/core/ngx_regex.c b/src/core/ngx_regex.c --- a/src/core/ngx_regex.c +++ b/src/core/ngx_regex.c @@ -137,23 +137,6 @@ failed: ngx_int_t -ngx_regex_capture_count(ngx_regex_t *re) -{ - int rc, n; - - n = 0; - - rc = pcre_fullinfo(re, NULL, PCRE_INFO_CAPTURECOUNT, &n); - - if (rc < 0) { - return (ngx_int_t) rc; - } - - return (ngx_int_t) n; -} - - -ngx_int_t ngx_regex_exec_array(ngx_array_t *a, ngx_str_t *s, ngx_log_t *log) { ngx_int_t n; diff --git a/src/http/modules/ngx_http_fastcgi_module.c b/src/http/modules/ngx_http_fastcgi_module.c --- a/src/http/modules/ngx_http_fastcgi_module.c +++ b/src/http/modules/ngx_http_fastcgi_module.c @@ -411,8 +411,8 @@ static ngx_command_t ngx_http_fastcgi_c &ngx_http_fastcgi_next_upstream_masks }, { ngx_string("fastcgi_param"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2, - ngx_conf_set_keyval_slot, + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE23, + ngx_http_upstream_param_set_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_fastcgi_loc_conf_t, params_source), NULL }, @@ -708,7 +708,7 @@ ngx_http_fastcgi_create_request(ngx_http u_char ch, *pos, *lowcase_key; size_t size, len, key_len, val_len, padding, allocated; - ngx_uint_t i, n, next, hash, header_params; + ngx_uint_t i, n, next, hash, skip_empty, header_params; ngx_buf_t *b; ngx_chain_t *cl, *body; ngx_list_part_t *part; @@ -739,11 +739,18 @@ ngx_http_fastcgi_create_request(ngx_http lcode = *(ngx_http_script_len_code_pt *) le.ip; key_len = lcode(&le); + lcode = *(ngx_http_script_len_code_pt *) le.ip; + skip_empty = 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 (skip_empty && val_len == 0) { + continue; + } + len += 1 + key_len + ((val_len > 127) ? 4 : 1) + val_len; } } @@ -893,11 +900,28 @@ ngx_http_fastcgi_create_request(ngx_http lcode = *(ngx_http_script_len_code_pt *) le.ip; key_len = (u_char) lcode(&le); + lcode = *(ngx_http_script_len_code_pt *) le.ip; + skip_empty = 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 (skip_empty && val_len == 0) { + e.skip = 1; + + while (*(uintptr_t *) e.ip) { + code = *(ngx_http_script_code_pt *) e.ip; + code((ngx_http_script_engine_t *) &e); + } + e.ip += sizeof(uintptr_t); + + e.skip = 0; + + continue; + } + *e.pos++ = (u_char) key_len; if (val_len > 127) { @@ -2370,9 +2394,9 @@ ngx_http_fastcgi_merge_params(ngx_conf_t #if (NGX_HTTP_CACHE) ngx_array_t params_merged; #endif - ngx_keyval_t *src; ngx_hash_key_t *hk; ngx_hash_init_t hash; + ngx_http_upstream_param_t *src; ngx_http_script_compile_t sc; ngx_http_script_copy_code_t *copy; @@ -2433,9 +2457,11 @@ ngx_http_fastcgi_merge_params(ngx_conf_t #if (NGX_HTTP_CACHE) if (conf->upstream.cache) { - ngx_keyval_t *h, *s; - - if (ngx_array_init(¶ms_merged, cf->temp_pool, 4, sizeof(ngx_keyval_t)) + ngx_keyval_t *h; + ngx_http_upstream_param_t *s; + + if (ngx_array_init(¶ms_merged, cf->temp_pool, 4, + sizeof(ngx_http_upstream_param_t)) != NGX_OK) { return NGX_ERROR; @@ -2469,7 +2495,9 @@ ngx_http_fastcgi_merge_params(ngx_conf_t return NGX_ERROR; } - *s = *h; + s->key = h->key; + s->value = h->value; + s->skip_empty = 0; next: @@ -2511,6 +2539,15 @@ ngx_http_fastcgi_merge_params(ngx_conf_t copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code; copy->len = src[i].key.len; + copy = ngx_array_push_n(conf->params_len, + sizeof(ngx_http_script_copy_code_t)); + if (copy == NULL) { + return NGX_ERROR; + } + + copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code; + copy->len = src[i].skip_empty; + size = (sizeof(ngx_http_script_copy_code_t) + src[i].key.len + sizeof(uintptr_t) - 1) diff --git a/src/http/modules/ngx_http_log_module.c b/src/http/modules/ngx_http_log_module.c --- a/src/http/modules/ngx_http_log_module.c +++ b/src/http/modules/ngx_http_log_module.c @@ -971,7 +971,7 @@ buffer: if (buf == NGX_ERROR) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "invalid parameter \"%V\"", &value[3]); + "invalid buffer value \"%V\"", &name); return NGX_CONF_ERROR; } @@ -1004,6 +1004,12 @@ ngx_http_log_set_format(ngx_conf_t *cf, ngx_uint_t i; ngx_http_log_fmt_t *fmt; + if (cf->cmd_type != NGX_HTTP_MAIN_CONF) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "the \"log_format\" directive may be used " + "only on \"http\" level"); + } + value = cf->args->elts; fmt = lmcf->formats.elts; diff --git a/src/http/modules/ngx_http_proxy_module.c b/src/http/modules/ngx_http_proxy_module.c --- a/src/http/modules/ngx_http_proxy_module.c +++ b/src/http/modules/ngx_http_proxy_module.c @@ -16,18 +16,15 @@ typedef ngx_int_t (*ngx_http_proxy_redir struct ngx_http_proxy_redirect_s { ngx_http_proxy_redirect_pt handler; - ngx_str_t redirect; union { - ngx_str_t text; - - struct { - void *lengths; - void *values; - } vars; - - void *regex; - } replacement; + ngx_http_complex_value_t complex; +#if (NGX_PCRE) + ngx_http_regex_t *regex; +#endif + } redirect; + + ngx_http_complex_value_t replacement; }; @@ -2289,20 +2286,29 @@ ngx_http_proxy_rewrite_redirect(ngx_http static ngx_int_t -ngx_http_proxy_rewrite_redirect_text(ngx_http_request_t *r, ngx_table_elt_t *h, - size_t prefix, ngx_http_proxy_redirect_t *pr) +ngx_http_proxy_rewrite_redirect_complex(ngx_http_request_t *r, + ngx_table_elt_t *h, size_t prefix, ngx_http_proxy_redirect_t *pr) { - size_t len; - u_char *data, *p; - - if (pr->redirect.len > h->value.len - prefix - || ngx_rstrncmp(h->value.data + prefix, pr->redirect.data, - pr->redirect.len) != 0) + size_t len; + u_char *data, *p; + ngx_str_t redirect, replacement; + + if (ngx_http_complex_value(r, &pr->redirect.complex, &redirect) != NGX_OK) { + return NGX_ERROR; + } + + if (redirect.len > h->value.len - prefix + || ngx_rstrncmp(h->value.data + prefix, redirect.data, + redirect.len) != 0) { return NGX_DECLINED; } - len = pr->replacement.text.len + h->value.len - pr->redirect.len; + if (ngx_http_complex_value(r, &pr->replacement, &replacement) != NGX_OK) { + return NGX_ERROR; + } + + len = replacement.len + h->value.len - redirect.len; data = ngx_pnalloc(r->pool, len); if (data == NULL) { @@ -2311,12 +2317,12 @@ ngx_http_proxy_rewrite_redirect_text(ngx p = ngx_copy(data, h->value.data, prefix); - if (pr->replacement.text.len) { - p = ngx_copy(p, pr->replacement.text.data, pr->replacement.text.len); + if (replacement.len) { + p = ngx_copy(p, replacement.data, replacement.len); } - ngx_memcpy(p, h->value.data + prefix + pr->redirect.len, - h->value.len - pr->redirect.len - prefix); + ngx_memcpy(p, h->value.data + prefix + redirect.len, + h->value.len - redirect.len - prefix); h->value.len = len; h->value.data = data; @@ -2325,59 +2331,32 @@ ngx_http_proxy_rewrite_redirect_text(ngx } +#if (NGX_PCRE) + static ngx_int_t -ngx_http_proxy_rewrite_redirect_vars(ngx_http_request_t *r, ngx_table_elt_t *h, +ngx_http_proxy_rewrite_redirect_regex(ngx_http_request_t *r, ngx_table_elt_t *h, size_t prefix, ngx_http_proxy_redirect_t *pr) { - size_t len; - u_char *data, *p; - ngx_http_script_code_pt code; - ngx_http_script_engine_t e; - ngx_http_script_len_code_pt lcode; - - if (pr->redirect.len > h->value.len - prefix - || ngx_rstrncmp(h->value.data + prefix, pr->redirect.data, - pr->redirect.len) != 0) - { + ngx_str_t redirect, replacement; + + redirect.len = h->value.len - prefix; + redirect.data = h->value.data + prefix; + + if (ngx_http_regex_exec(r, pr->redirect.regex, &redirect) != NGX_OK) { return NGX_DECLINED; } - ngx_memzero(&e, sizeof(ngx_http_script_engine_t)); - - e.ip = pr->replacement.vars.lengths; - e.request = r; - - len = h->value.len - pr->redirect.len; - - while (*(uintptr_t *) e.ip) { - lcode = *(ngx_http_script_len_code_pt *) e.ip; - len += lcode(&e); - } - - data = ngx_pnalloc(r->pool, len); - if (data == NULL) { + if (ngx_http_complex_value(r, &pr->replacement, &replacement) != NGX_OK) { return NGX_ERROR; } - p = ngx_copy(data, h->value.data, prefix); - - e.ip = pr->replacement.vars.values; - e.pos = p; - - while (*(uintptr_t *) e.ip) { - code = *(ngx_http_script_code_pt *) e.ip; - code(&e); - } - - ngx_memcpy(e.pos, h->value.data + prefix + pr->redirect.len, - h->value.len - pr->redirect.len - prefix); - - h->value.len = len; - h->value.data = data; + h->value = replacement; return NGX_OK; } +#endif + static ngx_int_t ngx_http_proxy_add_variables(ngx_conf_t *cf) @@ -2749,26 +2728,32 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t return NGX_CONF_ERROR; } - pr->handler = ngx_http_proxy_rewrite_redirect_text; + ngx_memzero(&pr->redirect.complex, + sizeof(ngx_http_complex_value_t)); + + ngx_memzero(&pr->replacement, sizeof(ngx_http_complex_value_t)); + + pr->handler = ngx_http_proxy_rewrite_redirect_complex; if (conf->vars.uri.len) { - pr->redirect = conf->url; - pr->replacement.text = conf->location; + pr->redirect.complex.value = conf->url; + pr->replacement.value = conf->location; } else { - pr->redirect.len = conf->url.len + sizeof("/") - 1; - - p = ngx_pnalloc(cf->pool, pr->redirect.len); + pr->redirect.complex.value.len = conf->url.len + + sizeof("/") - 1; + + p = ngx_pnalloc(cf->pool, pr->redirect.complex.value.len); if (p == NULL) { return NGX_CONF_ERROR; } - pr->redirect.data = p; + pr->redirect.complex.value.data = p; p = ngx_cpymem(p, conf->url.data, conf->url.len); *p = '/'; - ngx_str_set(&pr->replacement.text, "/"); + ngx_str_set(&pr->replacement.value, "/"); } } } @@ -3256,11 +3241,10 @@ ngx_http_proxy_redirect(ngx_conf_t *cf, { ngx_http_proxy_loc_conf_t *plcf = conf; - u_char *p; - ngx_str_t *value; - ngx_array_t *vars_lengths, *vars_values; - ngx_http_script_compile_t sc; - ngx_http_proxy_redirect_t *pr; + u_char *p; + ngx_str_t *value; + ngx_http_proxy_redirect_t *pr; + ngx_http_compile_complex_value_t ccv; if (plcf->redirect == 0) { return NGX_CONF_OK; @@ -3318,60 +3302,96 @@ ngx_http_proxy_redirect(ngx_conf_t *cf, return NGX_CONF_ERROR; } - pr->handler = ngx_http_proxy_rewrite_redirect_text; + pr->handler = ngx_http_proxy_rewrite_redirect_complex; + + ngx_memzero(&pr->redirect.complex, sizeof(ngx_http_complex_value_t)); + + ngx_memzero(&pr->replacement, sizeof(ngx_http_complex_value_t)); if (plcf->vars.uri.len) { - pr->redirect = plcf->url; - pr->replacement.text = plcf->location; + pr->redirect.complex.value = plcf->url; + pr->replacement.value = plcf->location; } else { - pr->redirect.len = plcf->url.len + sizeof("/") - 1; - - p = ngx_pnalloc(cf->pool, pr->redirect.len); + pr->redirect.complex.value.len = plcf->url.len + sizeof("/") - 1; + + p = ngx_pnalloc(cf->pool, pr->redirect.complex.value.len); if (p == NULL) { return NGX_CONF_ERROR; } - pr->redirect.data = p; + pr->redirect.complex.value.data = p; p = ngx_cpymem(p, plcf->url.data, plcf->url.len); *p = '/'; - ngx_str_set(&pr->replacement.text, "/"); + ngx_str_set(&pr->replacement.value, "/"); } return NGX_CONF_OK; } - if (ngx_http_script_variables_count(&value[2]) == 0) { - pr->handler = ngx_http_proxy_rewrite_redirect_text; - pr->redirect = value[1]; - pr->replacement.text = value[2]; - - return NGX_CONF_OK; + + if (value[1].data[0] == '~') { +#if (NGX_PCRE) + u_char errstr[NGX_MAX_CONF_ERRSTR]; + ngx_regex_compile_t rc; + + value[1].len--; + value[1].data++; + + ngx_memzero(&rc, sizeof(ngx_regex_compile_t)); + + if (value[1].data[0] == '*') { + value[1].len--; + value[1].data++; + rc.options = NGX_REGEX_CASELESS; + } + + rc.pattern = value[1]; + rc.err.len = NGX_MAX_CONF_ERRSTR; + rc.err.data = errstr; + + pr->redirect.regex = ngx_http_regex_compile(cf, &rc); + if (pr->redirect.regex == NULL) { + return NGX_CONF_ERROR; + } + + pr->handler = ngx_http_proxy_rewrite_redirect_regex; + +#else + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "using regex \"%V\" requires PCRE library", + &value[1]); + + return NGX_CONF_ERROR; +#endif + } else { + + ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &value[1]; + ccv.complex_value = &pr->redirect.complex; + + if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + + pr->handler = ngx_http_proxy_rewrite_redirect_complex; } - ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); - - vars_lengths = NULL; - vars_values = NULL; - - sc.cf = cf; - sc.source = &value[2]; - sc.lengths = &vars_lengths; - sc.values = &vars_values; - sc.complete_lengths = 1; - sc.complete_values = 1; - - if (ngx_http_script_compile(&sc) != NGX_OK) { + + ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &value[2]; + ccv.complex_value = &pr->replacement; + + if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { return NGX_CONF_ERROR; } - pr->handler = ngx_http_proxy_rewrite_redirect_vars; - pr->redirect = value[1]; - pr->replacement.vars.lengths = vars_lengths->elts; - pr->replacement.vars.values = vars_values->elts; - return NGX_CONF_OK; } diff --git a/src/http/modules/ngx_http_scgi_module.c b/src/http/modules/ngx_http_scgi_module.c --- a/src/http/modules/ngx_http_scgi_module.c +++ b/src/http/modules/ngx_http_scgi_module.c @@ -278,8 +278,8 @@ static ngx_command_t ngx_http_scgi_comma &ngx_http_scgi_next_upstream_masks }, { ngx_string("scgi_param"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2, - ngx_conf_set_keyval_slot, + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE23, + ngx_http_upstream_param_set_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_scgi_loc_conf_t, params_source), NULL }, @@ -519,10 +519,10 @@ static ngx_int_t ngx_http_scgi_create_request(ngx_http_request_t *r) { u_char ch, *key, *val, *lowcase_key; - size_t len, allocated; + size_t len, key_len, val_len, allocated; ngx_buf_t *b; ngx_str_t *content_length; - ngx_uint_t i, n, hash, header_params; + ngx_uint_t i, n, hash, skip_empty, header_params; ngx_chain_t *cl, *body; ngx_list_part_t *part; ngx_table_elt_t *header, **ignored; @@ -554,15 +554,21 @@ ngx_http_scgi_create_request(ngx_http_re while (*(uintptr_t *) le.ip) { lcode = *(ngx_http_script_len_code_pt *) le.ip; - len += lcode(&le); + key_len = lcode(&le); - while (*(uintptr_t *) le.ip) { + lcode = *(ngx_http_script_len_code_pt *) le.ip; + skip_empty = lcode(&le); + + for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) { lcode = *(ngx_http_script_len_code_pt *) le.ip; - len += lcode(&le); } - len++; + le.ip += sizeof(uintptr_t); - le.ip += sizeof(uintptr_t); + if (skip_empty && val_len == 0) { + continue; + } + + len += key_len + val_len + 1; } } @@ -665,7 +671,34 @@ ngx_http_scgi_create_request(ngx_http_re e.request = r; e.flushed = 1; - while (*(uintptr_t *) e.ip) { + le.ip = scf->params_len->elts; + + while (*(uintptr_t *) le.ip) { + + lcode = *(ngx_http_script_len_code_pt *) le.ip; + lcode(&le); /* key length */ + + lcode = *(ngx_http_script_len_code_pt *) le.ip; + skip_empty = 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 (skip_empty && 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; + } #if (NGX_DEBUG) key = e.pos; @@ -1323,9 +1356,9 @@ ngx_http_scgi_merge_params(ngx_conf_t *c #if (NGX_HTTP_CACHE) ngx_array_t params_merged; #endif - ngx_keyval_t *src; ngx_hash_key_t *hk; ngx_hash_init_t hash; + ngx_http_upstream_param_t *src; ngx_http_script_compile_t sc; ngx_http_script_copy_code_t *copy; @@ -1386,9 +1419,11 @@ ngx_http_scgi_merge_params(ngx_conf_t *c #if (NGX_HTTP_CACHE) if (conf->upstream.cache) { - ngx_keyval_t *h, *s; + ngx_keyval_t *h; + ngx_http_upstream_param_t *s; - if (ngx_array_init(¶ms_merged, cf->temp_pool, 4, sizeof(ngx_keyval_t)) + if (ngx_array_init(¶ms_merged, cf->temp_pool, 4, + sizeof(ngx_http_upstream_param_t)) != NGX_OK) { return NGX_ERROR; @@ -1422,7 +1457,9 @@ ngx_http_scgi_merge_params(ngx_conf_t *c return NGX_ERROR; } - *s = *h; + s->key = h->key; + s->value = h->value; + s->skip_empty = 0; next: @@ -1464,6 +1501,15 @@ ngx_http_scgi_merge_params(ngx_conf_t *c copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code; copy->len = src[i].key.len + 1; + copy = ngx_array_push_n(conf->params_len, + sizeof(ngx_http_script_copy_code_t)); + if (copy == NULL) { + return NGX_ERROR; + } + + copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code; + copy->len = src[i].skip_empty; + size = (sizeof(ngx_http_script_copy_code_t) + src[i].key.len + 1 + sizeof(uintptr_t) - 1) diff --git a/src/http/modules/ngx_http_uwsgi_module.c b/src/http/modules/ngx_http_uwsgi_module.c --- a/src/http/modules/ngx_http_uwsgi_module.c +++ b/src/http/modules/ngx_http_uwsgi_module.c @@ -305,8 +305,8 @@ static ngx_command_t ngx_http_uwsgi_comm &ngx_http_uwsgi_next_upstream_masks }, { ngx_string("uwsgi_param"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2, - ngx_conf_set_keyval_slot, + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE23, + ngx_http_upstream_param_set_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_uwsgi_loc_conf_t, params_source), NULL }, @@ -553,7 +553,7 @@ ngx_http_uwsgi_create_request(ngx_http_r { u_char ch, *lowcase_key; size_t key_len, val_len, len, allocated; - ngx_uint_t i, n, hash, header_params; + ngx_uint_t i, n, hash, skip_empty, header_params; ngx_buf_t *b; ngx_chain_t *cl, *body; ngx_list_part_t *part; @@ -583,11 +583,18 @@ ngx_http_uwsgi_create_request(ngx_http_r lcode = *(ngx_http_script_len_code_pt *) le.ip; key_len = lcode(&le); + lcode = *(ngx_http_script_len_code_pt *) le.ip; + skip_empty = 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 (skip_empty && val_len == 0) { + continue; + } + len += 2 + key_len + 2 + val_len; } } @@ -706,11 +713,28 @@ ngx_http_uwsgi_create_request(ngx_http_r lcode = *(ngx_http_script_len_code_pt *) le.ip; key_len = (u_char) lcode (&le); + lcode = *(ngx_http_script_len_code_pt *) le.ip; + skip_empty = 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 (skip_empty && val_len == 0) { + e.skip = 1; + + while (*(uintptr_t *) e.ip) { + code = *(ngx_http_script_code_pt *) e.ip; + code((ngx_http_script_engine_t *) &e); + } + e.ip += sizeof(uintptr_t); + + e.skip = 0; + + continue; + } + *e.pos++ = (u_char) (key_len & 0xff); *e.pos++ = (u_char) ((key_len >> 8) & 0xff); @@ -1379,9 +1403,9 @@ ngx_http_uwsgi_merge_params(ngx_conf_t * #if (NGX_HTTP_CACHE) ngx_array_t params_merged; #endif - ngx_keyval_t *src; ngx_hash_key_t *hk; ngx_hash_init_t hash; + ngx_http_upstream_param_t *src; ngx_http_script_compile_t sc; ngx_http_script_copy_code_t *copy; @@ -1442,9 +1466,11 @@ ngx_http_uwsgi_merge_params(ngx_conf_t * #if (NGX_HTTP_CACHE) if (conf->upstream.cache) { - ngx_keyval_t *h, *s; + ngx_keyval_t *h; + ngx_http_upstream_param_t *s; - if (ngx_array_init(¶ms_merged, cf->temp_pool, 4, sizeof(ngx_keyval_t)) + if (ngx_array_init(¶ms_merged, cf->temp_pool, 4, + sizeof(ngx_http_upstream_param_t)) != NGX_OK) { return NGX_ERROR; @@ -1478,7 +1504,9 @@ ngx_http_uwsgi_merge_params(ngx_conf_t * return NGX_ERROR; } - *s = *h; + s->key = h->key; + s->value = h->value; + s->skip_empty = 0; next: @@ -1520,6 +1548,15 @@ ngx_http_uwsgi_merge_params(ngx_conf_t * copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code; copy->len = src[i].key.len; + copy = ngx_array_push_n(conf->params_len, + sizeof(ngx_http_script_copy_code_t)); + if (copy == NULL) { + return NGX_ERROR; + } + + copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code; + copy->len = src[i].skip_empty; + size = (sizeof(ngx_http_script_copy_code_t) + src[i].key.len + sizeof(uintptr_t) - 1) diff --git a/src/http/modules/perl/nginx.pm b/src/http/modules/perl/nginx.pm --- a/src/http/modules/perl/nginx.pm +++ b/src/http/modules/perl/nginx.pm @@ -48,7 +48,7 @@ our @EXPORT = qw( HTTP_INSUFFICIENT_STORAGE ); -our $VERSION = '1.1.10'; +our $VERSION = '1.1.11'; require XSLoader; XSLoader::load('nginx', $VERSION); diff --git a/src/http/ngx_http.c b/src/http/ngx_http.c --- a/src/http/ngx_http.c +++ b/src/http/ngx_http.c @@ -1762,6 +1762,13 @@ ngx_http_add_listening(ngx_conf_t *cf, n ls->rcvbuf = addr->opt.rcvbuf; ls->sndbuf = addr->opt.sndbuf; + ls->keepalive = addr->opt.so_keepalive; +#if (NGX_HAVE_KEEPALIVE_TUNABLE) + ls->keepidle = addr->opt.tcp_keepidle; + ls->keepintvl = addr->opt.tcp_keepintvl; + ls->keepcnt = addr->opt.tcp_keepcnt; +#endif + #if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER) ls->accept_filter = addr->opt.accept_filter; #endif diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c --- a/src/http/ngx_http_core_module.c +++ b/src/http/ngx_http_core_module.c @@ -1289,6 +1289,7 @@ ngx_http_core_try_files_phase(ngx_http_r ngx_memzero(&of, sizeof(ngx_open_file_info_t)); + of.read_ahead = clcf->read_ahead; of.directio = clcf->directio; of.valid = clcf->open_file_cache_valid; of.min_uses = clcf->open_file_cache_min_uses; @@ -3815,6 +3816,97 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx #endif } + if (ngx_strncmp(value[n].data, "so_keepalive=", 13) == 0) { + + if (ngx_strcmp(&value[n].data[13], "on") == 0) { + lsopt.so_keepalive = 1; + + } else if (ngx_strcmp(&value[n].data[13], "off") == 0) { + lsopt.so_keepalive = 2; + + } else { + +#if (NGX_HAVE_KEEPALIVE_TUNABLE) + u_char *p, *end; + ngx_str_t s; + + end = value[n].data + value[n].len; + s.data = value[n].data + 13; + + p = ngx_strlchr(s.data, end, ':'); + if (p == NULL) { + p = end; + } + + if (p > s.data) { + s.len = p - s.data; + + lsopt.tcp_keepidle = ngx_parse_time(&s, 1); + if (lsopt.tcp_keepidle == NGX_ERROR) { + goto invalid_so_keepalive; + } + } + + s.data = (p < end) ? (p + 1) : end; + + p = ngx_strlchr(s.data, end, ':'); + if (p == NULL) { + p = end; + } + + if (p > s.data) { + s.len = p - s.data; + + lsopt.tcp_keepintvl = ngx_parse_time(&s, 1); + if (lsopt.tcp_keepintvl == NGX_ERROR) { + goto invalid_so_keepalive; + } + } + + s.data = (p < end) ? (p + 1) : end; + + if (s.data < end) { + s.len = end - s.data; + + lsopt.tcp_keepcnt = ngx_atoi(s.data, s.len); + if (lsopt.tcp_keepcnt == NGX_ERROR) { + goto invalid_so_keepalive; + } + } + + if (lsopt.tcp_keepidle == 0 && lsopt.tcp_keepintvl == 0 + && lsopt.tcp_keepcnt == 0) + { + goto invalid_so_keepalive; + } + + lsopt.so_keepalive = 1; + +#else + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the \"so_keepalive\" parameter accepts " + "only \"on\" or \"off\" on this platform"); + return NGX_CONF_ERROR; + +#endif + } + + lsopt.set = 1; + lsopt.bind = 1; + + continue; + +#if (NGX_HAVE_KEEPALIVE_TUNABLE) + invalid_so_keepalive: + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid so_keepalive value: \"%s\"", + &value[n].data[13]); + return NGX_CONF_ERROR; +#endif + } + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"", &value[n]); return NGX_CONF_ERROR; diff --git a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h --- a/src/http/ngx_http_core_module.h +++ b/src/http/ngx_http_core_module.h @@ -77,6 +77,7 @@ typedef struct { #if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) unsigned ipv6only:2; #endif + unsigned so_keepalive:2; int backlog; int rcvbuf; @@ -84,6 +85,11 @@ typedef struct { #if (NGX_HAVE_SETFIB) int setfib; #endif +#if (NGX_HAVE_KEEPALIVE_TUNABLE) + int tcp_keepidle; + int tcp_keepintvl; + int tcp_keepcnt; +#endif #if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER) char *accept_filter; diff --git a/src/http/ngx_http_file_cache.c b/src/http/ngx_http_file_cache.c --- a/src/http/ngx_http_file_cache.c +++ b/src/http/ngx_http_file_cache.c @@ -386,6 +386,13 @@ ngx_http_file_cache_read(ngx_http_reques return NGX_DECLINED; } + if (h->body_start > c->body_start) { + ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0, + "cache file \"%s\" has too long header", + c->file.name.data); + return NGX_DECLINED; + } + c->buf->last += n; c->valid_sec = h->valid_sec; @@ -1106,12 +1113,12 @@ ngx_http_file_cache_expire(ngx_http_file /* * abnormally exited workers may leave locked cache entries, * and although it may be safe to remove them completely, - * we prefer to remove them from inactive queue and rbtree - * only, and to allow other leaks + * we prefer to just move them to the top of the inactive queue */ ngx_queue_remove(q); - ngx_rbtree_delete(&cache->sh->rbtree, &fcn->node); + fcn->expire = ngx_time() + cache->inactive; + ngx_queue_insert_head(&cache->sh->queue, &fcn->queue); ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "ignore long locked inactive cache entry %*s, count:%d", @@ -1752,69 +1759,3 @@ ngx_http_file_cache_valid_set_slot(ngx_c return NGX_CONF_OK; } - - -ngx_int_t -ngx_http_cache(ngx_http_request_t *r, ngx_array_t *no_cache) -{ - ngx_str_t val; - ngx_uint_t i; - ngx_http_complex_value_t *cv; - - cv = no_cache->elts; - - for (i = 0; i < no_cache->nelts; i++) { - if (ngx_http_complex_value(r, &cv[i], &val) != NGX_OK) { - return NGX_ERROR; - } - - if (val.len && val.data[0] != '0') { - return NGX_DECLINED; - } - } - - return NGX_OK; -} - - -char * -ngx_http_no_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) -{ - char *p = conf; - - ngx_str_t *value; - ngx_uint_t i; - ngx_array_t **a; - ngx_http_complex_value_t *cv; - ngx_http_compile_complex_value_t ccv; - - a = (ngx_array_t **) (p + cmd->offset); - - if (*a == NGX_CONF_UNSET_PTR) { - *a = ngx_array_create(cf->pool, 1, sizeof(ngx_http_complex_value_t)); - if (*a == NULL) { - return NGX_CONF_ERROR; - } - } - - value = cf->args->elts; - - for (i = 1; i < cf->args->nelts; i++) { - cv = ngx_array_push(*a); - if (cv == NULL) { - return NGX_CONF_ERROR; - } - - ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); - - ccv.cf = cf; - ccv.value = &value[i]; - ccv.complex_value = cv; - - if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { - return NGX_CONF_ERROR; - } - } - - return NGX_CONF_OK; -} diff --git a/src/http/ngx_http_header_filter_module.c b/src/http/ngx_http_header_filter_module.c --- a/src/http/ngx_http_header_filter_module.c +++ b/src/http/ngx_http_header_filter_module.c @@ -61,8 +61,8 @@ static ngx_str_t ngx_http_status_lines[] /* ngx_null_string, */ /* "207 Multi-Status" */ -#define NGX_HTTP_LAST_LEVEL_200 207 -#define NGX_HTTP_LEVEL_200 (NGX_HTTP_LAST_LEVEL_200 - 200) +#define NGX_HTTP_LAST_2XX 207 +#define NGX_HTTP_OFF_3XX (NGX_HTTP_LAST_2XX - 200) /* ngx_null_string, */ /* "300 Multiple Choices" */ @@ -75,8 +75,8 @@ static ngx_str_t ngx_http_status_lines[] /* ngx_null_string, */ /* "306 unused" */ /* ngx_null_string, */ /* "307 Temporary Redirect" */ -#define NGX_HTTP_LAST_LEVEL_300 305 -#define NGX_HTTP_LEVEL_300 (NGX_HTTP_LAST_LEVEL_300 - 301) +#define NGX_HTTP_LAST_3XX 305 +#define NGX_HTTP_OFF_4XX (NGX_HTTP_LAST_3XX - 301 + NGX_HTTP_OFF_3XX) ngx_string("400 Bad Request"), ngx_string("401 Unauthorized"), @@ -108,8 +108,8 @@ static ngx_str_t ngx_http_status_lines[] /* ngx_null_string, */ /* "423 Locked" */ /* ngx_null_string, */ /* "424 Failed Dependency" */ -#define NGX_HTTP_LAST_LEVEL_400 417 -#define NGX_HTTP_LEVEL_400 (NGX_HTTP_LAST_LEVEL_400 - 400) +#define NGX_HTTP_LAST_4XX 417 +#define NGX_HTTP_OFF_5XX (NGX_HTTP_LAST_4XX - 400 + NGX_HTTP_OFF_4XX) ngx_string("500 Internal Server Error"), ngx_string("501 Method Not Implemented"), @@ -124,7 +124,7 @@ static ngx_str_t ngx_http_status_lines[] /* ngx_null_string, */ /* "509 unused" */ /* ngx_null_string, */ /* "510 Not Extended" */ -#define NGX_HTTP_LAST_LEVEL_500 508 +#define NGX_HTTP_LAST_5XX 508 }; @@ -216,7 +216,7 @@ ngx_http_header_filter(ngx_http_request_ status = r->headers_out.status; if (status >= NGX_HTTP_OK - && status < NGX_HTTP_LAST_LEVEL_200) + && status < NGX_HTTP_LAST_2XX) { /* 2XX */ @@ -234,7 +234,7 @@ ngx_http_header_filter(ngx_http_request_ len += ngx_http_status_lines[status].len; } else if (status >= NGX_HTTP_MOVED_PERMANENTLY - && status < NGX_HTTP_LAST_LEVEL_300) + && status < NGX_HTTP_LAST_3XX) { /* 3XX */ @@ -242,29 +242,26 @@ ngx_http_header_filter(ngx_http_request_ r->header_only = 1; } - status = status - NGX_HTTP_MOVED_PERMANENTLY + NGX_HTTP_LEVEL_200; + status = status - NGX_HTTP_MOVED_PERMANENTLY + NGX_HTTP_OFF_3XX; status_line = &ngx_http_status_lines[status]; len += ngx_http_status_lines[status].len; } else if (status >= NGX_HTTP_BAD_REQUEST - && status < NGX_HTTP_LAST_LEVEL_400) + && status < NGX_HTTP_LAST_4XX) { /* 4XX */ status = status - NGX_HTTP_BAD_REQUEST - + NGX_HTTP_LEVEL_200 - + NGX_HTTP_LEVEL_300; + + NGX_HTTP_OFF_4XX; status_line = &ngx_http_status_lines[status]; len += ngx_http_status_lines[status].len; } else if (status >= NGX_HTTP_INTERNAL_SERVER_ERROR - && status < NGX_HTTP_LAST_LEVEL_500) + && status < NGX_HTTP_LAST_5XX) { /* 5XX */ status = status - NGX_HTTP_INTERNAL_SERVER_ERROR - + NGX_HTTP_LEVEL_200 - + NGX_HTTP_LEVEL_300 - + NGX_HTTP_LEVEL_400; + + NGX_HTTP_OFF_5XX; status_line = &ngx_http_status_lines[status]; len += ngx_http_status_lines[status].len; 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 @@ -4444,6 +4444,50 @@ ngx_http_upstream_bind_set_slot(ngx_conf } +char * +ngx_http_upstream_param_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + char *p = conf; + + ngx_str_t *value; + ngx_array_t **a; + ngx_http_upstream_param_t *param; + + a = (ngx_array_t **) (p + cmd->offset); + + if (*a == NULL) { + *a = ngx_array_create(cf->pool, 4, sizeof(ngx_http_upstream_param_t)); + if (*a == NULL) { + return NGX_CONF_ERROR; + } + } + + param = ngx_array_push(*a); + if (param == NULL) { + return NGX_CONF_ERROR; + } + + value = cf->args->elts; + + param->key = value[1]; + param->value = value[2]; + param->skip_empty = 0; + + if (cf->args->nelts == 4) { + if (ngx_strcmp(value[3].data, "if_not_empty") != 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[3]); + return NGX_CONF_ERROR; + } + + param->skip_empty = 1; + } + + return NGX_CONF_OK; +} + + ngx_int_t ngx_http_upstream_hide_headers_hash(ngx_conf_t *cf, ngx_http_upstream_conf_t *conf, ngx_http_upstream_conf_t *prev, 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 @@ -328,6 +328,13 @@ typedef struct { } ngx_http_upstream_next_t; +typedef struct { + ngx_str_t key; + ngx_str_t value; + ngx_uint_t skip_empty; +} ngx_http_upstream_param_t; + + ngx_int_t ngx_http_upstream_header_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); @@ -337,6 +344,8 @@ ngx_http_upstream_srv_conf_t *ngx_http_u ngx_url_t *u, ngx_uint_t flags); char *ngx_http_upstream_bind_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +char *ngx_http_upstream_param_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); ngx_int_t ngx_http_upstream_hide_headers_hash(ngx_conf_t *cf, ngx_http_upstream_conf_t *conf, ngx_http_upstream_conf_t *prev, ngx_str_t *default_hide_headers, ngx_hash_init_t *hash); diff --git a/src/http/ngx_http_variables.c b/src/http/ngx_http_variables.c --- a/src/http/ngx_http_variables.c +++ b/src/http/ngx_http_variables.c @@ -48,6 +48,8 @@ static ngx_int_t ngx_http_variable_serve ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_scheme(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_https(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_is_args(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_document_root(ngx_http_request_t *r, @@ -157,6 +159,8 @@ static ngx_http_variable_t ngx_http_cor { ngx_string("scheme"), NULL, ngx_http_variable_scheme, 0, 0, 0 }, + { ngx_string("https"), NULL, ngx_http_variable_https, 0, 0, 0 }, + { ngx_string("request_uri"), NULL, ngx_http_variable_request, offsetof(ngx_http_request_t, unparsed_uri), 0, 0 }, @@ -640,8 +644,8 @@ static ngx_int_t ngx_http_variable_headers(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { - ssize_t len; - u_char *p; + size_t len; + u_char *p, *end; ngx_uint_t i, n; ngx_array_t *a; ngx_table_elt_t **h; @@ -649,18 +653,30 @@ ngx_http_variable_headers(ngx_http_reque a = (ngx_array_t *) ((char *) r + data); n = a->nelts; - - if (n == 0) { + h = a->elts; + + len = 0; + + for (i = 0; i < n; i++) { + + if (h[i]->hash == 0) { + continue; + } + + len += h[i]->value.len + sizeof("; ") - 1; + } + + if (len == 0) { v->not_found = 1; return NGX_OK; } + len -= sizeof("; ") - 1; + v->valid = 1; v->no_cacheable = 0; v->not_found = 0; - h = a->elts; - if (n == 1) { v->len = (*h)->value.len; v->data = (*h)->value.data; @@ -668,12 +684,6 @@ ngx_http_variable_headers(ngx_http_reque return NGX_OK; } - len = - (ssize_t) (sizeof("; ") - 1); - - for (i = 0; i < n; i++) { - len += h[i]->value.len + sizeof("; ") - 1; - } - p = ngx_pnalloc(r->pool, len); if (p == NULL) { return NGX_ERROR; @@ -682,10 +692,17 @@ ngx_http_variable_headers(ngx_http_reque v->len = len; v->data = p; + end = p + len; + for (i = 0; /* void */ ; i++) { + + if (h[i]->hash == 0) { + continue; + } + p = ngx_copy(p, h[i]->value.data, h[i]->value.len); - if (i == n - 1) { + if (p == end) { break; } @@ -738,6 +755,10 @@ ngx_http_variable_unknown_header(ngx_htt i = 0; } + if (header[i].hash == 0) { + continue; + } + for (n = 0; n + prefix < var->len && n < header[i].key.len; n++) { ch = header[i].key.data[n]; @@ -1091,6 +1112,30 @@ ngx_http_variable_scheme(ngx_http_reques static ngx_int_t +ngx_http_variable_https(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ +#if (NGX_HTTP_SSL) + + if (r->connection->ssl) { + v->len = sizeof("on") - 1; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = (u_char *) "on"; + + return NGX_OK; + } + +#endif + + *v = ngx_http_variable_null_value; + + return NGX_OK; +} + + +static ngx_int_t ngx_http_variable_is_args(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { diff --git a/src/mail/ngx_mail.c b/src/mail/ngx_mail.c --- a/src/mail/ngx_mail.c +++ b/src/mail/ngx_mail.c @@ -308,6 +308,12 @@ found: addr->ctx = listen->ctx; addr->bind = listen->bind; addr->wildcard = listen->wildcard; + addr->so_keepalive = listen->so_keepalive; +#if (NGX_HAVE_KEEPALIVE_TUNABLE) + addr->tcp_keepidle = listen->tcp_keepidle; + addr->tcp_keepintvl = listen->tcp_keepintvl; + addr->tcp_keepcnt = listen->tcp_keepcnt; +#endif #if (NGX_MAIL_SSL) addr->ssl = listen->ssl; #endif @@ -373,6 +379,13 @@ ngx_mail_optimize_servers(ngx_conf_t *cf ls->log.data = &ls->addr_text; ls->log.handler = ngx_accept_log_error; + ls->keepalive = addr[i].so_keepalive; +#if (NGX_HAVE_KEEPALIVE_TUNABLE) + ls->keepidle = addr[i].tcp_keepidle; + ls->keepintvl = addr[i].tcp_keepintvl; + ls->keepcnt = addr[i].tcp_keepcnt; +#endif + #if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) ls->ipv6only = addr[i].ipv6only; #endif diff --git a/src/mail/ngx_mail.h b/src/mail/ngx_mail.h --- a/src/mail/ngx_mail.h +++ b/src/mail/ngx_mail.h @@ -40,6 +40,12 @@ typedef struct { #if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) unsigned ipv6only:2; #endif + unsigned so_keepalive:2; +#if (NGX_HAVE_KEEPALIVE_TUNABLE) + int tcp_keepidle; + int tcp_keepintvl; + int tcp_keepcnt; +#endif } ngx_mail_listen_t; @@ -95,6 +101,12 @@ typedef struct { #if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) unsigned ipv6only:2; #endif + unsigned so_keepalive:2; +#if (NGX_HAVE_KEEPALIVE_TUNABLE) + int tcp_keepidle; + int tcp_keepintvl; + int tcp_keepcnt; +#endif } ngx_mail_conf_addr_t; diff --git a/src/mail/ngx_mail_core_module.c b/src/mail/ngx_mail_core_module.c --- a/src/mail/ngx_mail_core_module.c +++ b/src/mail/ngx_mail_core_module.c @@ -24,6 +24,12 @@ static char *ngx_mail_core_resolver(ngx_ void *conf); +static ngx_conf_deprecated_t ngx_conf_deprecated_so_keepalive = { + ngx_conf_deprecated, "so_keepalive", + "so_keepalive\" parameter of the \"listen" +}; + + static ngx_command_t ngx_mail_core_commands[] = { { ngx_string("server"), @@ -52,7 +58,7 @@ static ngx_command_t ngx_mail_core_comm ngx_conf_set_flag_slot, NGX_MAIL_SRV_CONF_OFFSET, offsetof(ngx_mail_core_srv_conf_t, so_keepalive), - NULL }, + &ngx_conf_deprecated_so_keepalive }, { ngx_string("timeout"), NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, @@ -446,6 +452,96 @@ ngx_mail_core_listen(ngx_conf_t *cf, ngx #endif } + if (ngx_strncmp(value[i].data, "so_keepalive=", 13) == 0) { + + if (ngx_strcmp(&value[i].data[13], "on") == 0) { + ls->so_keepalive = 1; + + } else if (ngx_strcmp(&value[i].data[13], "off") == 0) { + ls->so_keepalive = 2; + + } else { + +#if (NGX_HAVE_KEEPALIVE_TUNABLE) + u_char *p, *end; + ngx_str_t s; + + end = value[i].data + value[i].len; + s.data = value[i].data + 13; + + p = ngx_strlchr(s.data, end, ':'); + if (p == NULL) { + p = end; + } + + if (p > s.data) { + s.len = p - s.data; + + ls->tcp_keepidle = ngx_parse_time(&s, 1); + if (ls->tcp_keepidle == NGX_ERROR) { + goto invalid_so_keepalive; + } + } + + s.data = (p < end) ? (p + 1) : end; + + p = ngx_strlchr(s.data, end, ':'); + if (p == NULL) { + p = end; + } + + if (p > s.data) { + s.len = p - s.data; + + ls->tcp_keepintvl = ngx_parse_time(&s, 1); + if (ls->tcp_keepintvl == NGX_ERROR) { + goto invalid_so_keepalive; + } + } + + s.data = (p < end) ? (p + 1) : end; + + if (s.data < end) { + s.len = end - s.data; + + ls->tcp_keepcnt = ngx_atoi(s.data, s.len); + if (ls->tcp_keepcnt == NGX_ERROR) { + goto invalid_so_keepalive; + } + } + + if (ls->tcp_keepidle == 0 && ls->tcp_keepintvl == 0 + && ls->tcp_keepcnt == 0) + { + goto invalid_so_keepalive; + } + + ls->so_keepalive = 1; + +#else + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the \"so_keepalive\" parameter accepts " + "only \"on\" or \"off\" on this platform"); + return NGX_CONF_ERROR; + +#endif + } + + ls->bind = 1; + + continue; + +#if (NGX_HAVE_KEEPALIVE_TUNABLE) + invalid_so_keepalive: + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid so_keepalive value: \"%s\"", + &value[i].data[13]); + return NGX_CONF_ERROR; +#endif + } + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "the invalid \"%V\" parameter", &value[i]); return NGX_CONF_ERROR; diff --git a/src/os/unix/ngx_freebsd_sendfile_chain.c b/src/os/unix/ngx_freebsd_sendfile_chain.c --- a/src/os/unix/ngx_freebsd_sendfile_chain.c +++ b/src/os/unix/ngx_freebsd_sendfile_chain.c @@ -246,9 +246,14 @@ ngx_freebsd_sendfile_chain(ngx_connectio } } - hdtr.headers = (struct iovec *) header.elts; + /* + * sendfile() does unneeded work if sf_hdtr's count is 0, + * but corresponding pointer is not NULL + */ + + hdtr.headers = header.nelts ? (struct iovec *) header.elts: NULL; hdtr.hdr_cnt = header.nelts; - hdtr.trailers = (struct iovec *) trailer.elts; + hdtr.trailers = trailer.nelts ? (struct iovec *) trailer.elts: NULL; hdtr.trl_cnt = trailer.nelts; /*