diff --git a/auto/install b/auto/install index 07535b745..3496c5b12 100644 --- a/auto/install +++ b/auto/install @@ -30,6 +30,8 @@ install: $NGX_OBJS${ngx_dirsep}nginx${ngx_binext} \ || mkdir -p '`dirname "$NGX_CONF_PATH"`' cp conf/koi-win '`dirname "$NGX_CONF_PATH"`' + cp conf/koi-utf '`dirname "$NGX_CONF_PATH"`' + cp conf/win-utf '`dirname "$NGX_CONF_PATH"`' test -f '`dirname "$NGX_CONF_PATH"`/mime.types' || \ cp conf/mime.types '`dirname "$NGX_CONF_PATH"`/mime.types' diff --git a/auto/types/sizeof b/auto/types/sizeof index 2cd1fb89d..4d65dca16 100644 --- a/auto/types/sizeof +++ b/auto/types/sizeof @@ -54,7 +54,7 @@ case $ngx_size in ngx_max_value=2147483647 fi - ngx_max_len='sizeof("-2147483648") - 1' + ngx_max_len='(sizeof("-2147483648") - 1)' ;; 8) @@ -64,7 +64,7 @@ case $ngx_size in ngx_max_value=9223372036854775807L fi - ngx_max_len='sizeof("-9223372036854775808") - 1' + ngx_max_len='(sizeof("-9223372036854775808") - 1)' ;; *) diff --git a/conf/koi-utf b/conf/koi-utf new file mode 100644 index 000000000..2faca0006 --- /dev/null +++ b/conf/koi-utf @@ -0,0 +1,103 @@ + +charset_map koi8-r utf-8 { + + 80 E282AC ; # euro + + 95 E280A2 ; # bullet + + 9A C2A0 ; #   + + 9E C2B7 ; # · + + A3 D191 ; # small yo + A4 D194 ; # small Ukrainian ye + + A6 D196 ; # small Ukrainian i + A7 D197 ; # small Ukrainian yi + + AD D291 ; # small Ukrainian soft g + AE D19E ; # small Byelorussian short u + + B0 C2B0 ; # ° + + B3 D081 ; # capital YO + B4 D084 ; # capital Ukrainian YE + + B6 D086 ; # capital Ukrainian I + B7 D087 ; # capital Ukrainian YI + + B9 E28496 ; # numero sign + + BD D290 ; # capital Ukrainian soft G + BE D18E ; # capital Byelorussian short U + + BF C2A9 ; # (C) + + C0 D18E ; # small yu + C1 D0B0 ; # small a + C2 D0B1 ; # small b + C3 D186 ; # small ts + C4 D0B4 ; # small d + C5 D0B5 ; # small ye + C6 D184 ; # small f + C7 D0B3 ; # small g + C8 D185 ; # small kh + C9 D0B8 ; # small i + CA D0B9 ; # small j + CB D0BA ; # small k + CC D0BB ; # small l + CD D0BC ; # small m + CE D0BD ; # small n + CF D0BE ; # small o + + D0 D0BF ; # small p + D1 D18F ; # small ya + D2 D180 ; # small r + D3 D181 ; # small s + D4 D182 ; # small t + D5 D183 ; # small u + D6 D0B6 ; # small zh + D7 D0B2 ; # small v + D8 D18C ; # small soft sign + D9 D18B ; # small y + DA D0B7 ; # small z + DB D188 ; # small sh + DC D18D ; # small e + DD D189 ; # small shch + DE D187 ; # small ch + DF D18A ; # small hard sign + + E0 D0AE ; # capital YU + E1 D090 ; # capital A + E2 D091 ; # capital B + E3 D0A6 ; # capital TS + E4 D094 ; # capital D + E5 D095 ; # capital YE + E6 D0A4 ; # capital F + E7 D093 ; # capital G + E8 D0A5 ; # capital KH + E9 D098 ; # capital I + EA D099 ; # capital J + EB D09A ; # capital K + EC D09B ; # capital L + ED D09C ; # capital M + EE D09D ; # capital N + EF D09E ; # capital O + + F0 D09F ; # capital P + F1 D0AF ; # capital YA + F2 D0A0 ; # capital R + F3 D0A1 ; # capital S + F4 D0A2 ; # capital T + F5 D0A3 ; # capital U + F6 D096 ; # capital ZH + F7 D092 ; # capital V + F8 D0AC ; # capital soft sign + F9 D0AB ; # capital Y + FA D097 ; # capital Z + FB D0A8 ; # capital SH + FC D0AD ; # capital E + FD D0A9 ; # capital SHCH + FE D0A7 ; # capital CH + FF D0AA ; # capital hard sign +} diff --git a/conf/koi-win b/conf/koi-win index 99edb1b2b..72afabe89 100644 --- a/conf/koi-win +++ b/conf/koi-win @@ -13,7 +13,7 @@ charset_map koi8-r windows-1251 { A4 BA ; # small Ukrainian ye A6 B3 ; # small Ukrainian i - A7 BF ; # small Ukrainian j + A7 BF ; # small Ukrainian yi AD B4 ; # small Ukrainian soft g AE A2 ; # small Byelorussian short u @@ -24,9 +24,9 @@ charset_map koi8-r windows-1251 { B4 AA ; # capital Ukrainian YE B6 B2 ; # capital Ukrainian I - B7 AF ; # capital Ukrainian J + B7 AF ; # capital Ukrainian YI - B9 B9 ; # No + B9 B9 ; # numero sign BD A5 ; # capital Ukrainian soft G BE A1 ; # capital Byelorussian short U diff --git a/conf/nginx.conf b/conf/nginx.conf index 2f5785a02..3487318f4 100644 --- a/conf/nginx.conf +++ b/conf/nginx.conf @@ -20,7 +20,7 @@ http { #log_format main '$remote_addr - $remote_user [$time_local] $status ' # '"$request" $body_bytes_sent "$http_referer" ' - # '"$http_user_agent" "http_x_forwarded_for"'; + # '"$http_user_agent" "$http_x_forwarded_for"'; #access_log logs/access.log main; @@ -46,6 +46,15 @@ http { index index.html index.htm; } + #error_page 404 /404.html; + + # redirect server error pages to the static page /50x.html + # + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root html; + } + # proxy the PHP scripts to Apache listening on 127.0.0.1:80 # #location ~ \.php$ { @@ -67,9 +76,6 @@ http { #location ~ /\.ht { # deny all; #} - - #error_page 404 /404.html; - #error_page 500 502 503 504 /50x.html; } diff --git a/conf/win-utf b/conf/win-utf new file mode 100644 index 000000000..e8e830e0f --- /dev/null +++ b/conf/win-utf @@ -0,0 +1,122 @@ + +charset_map windows-1251 utf-8 { + + 82 E2809A ; # single low-9 quotation mark + + 84 E2809E ; # double low-9 quotation mark + 85 E280A6 ; # ellipsis + 86 E280A0 ; # dagger + 87 E280A1 ; # double dagger + 88 E282AC ; # euro + 89 E280B0 ; # per mille + + 91 E28098 ; # left single quotation mark + 92 E28099 ; # right single quotation mark + 93 E2809C ; # left double quotation mark + 94 E2809D ; # right double quotation mark + 95 E280A2 ; # bullet + 96 E28093 ; # en dash + 97 E28094 ; # em dash + + 99 E284A2 ; # trade mark sign + + A0 C2A0 ; #   + A1 D18E ; # capital Byelorussian short U + A2 D19E ; # small Byelorussian short u + + A4 C2A4 ; # currency sign + A5 D290 ; # capital Ukrainian soft G + A6 C2A6 ; # borken bar + A7 C2A7 ; # section sign + A8 D081 ; # capital YO + A9 C2A9 ; # (C) + AA D084 ; # capital Ukrainian YE + AB C2AB ; # left-pointing double angle quotation mark + AC C2AC ; # not sign + AD C2AD ; # soft hypen + AE C2AE ; # (R) + AF D087 ; # capital Ukrainian YI + + B0 C2B0 ; # ° + B1 C2B1 ; # plus-minus sign + B2 D086 ; # capital Ukrainian I + B3 D196 ; # small Ukrainian i + B4 D291 ; # small Ukrainian soft g + B5 C2B5 ; # micro sign + B6 C2B6 ; # pilcrow sign + B7 C2B7 ; # · + B8 D191 ; # small yo + B9 E28496 ; # numero sign + BA D194 ; # small Ukrainian ye + BB C2BB ; # right-pointing double angle quotation mark + + BF D197 ; # small Ukrainian yi + + C0 D090 ; # capital A + C1 D091 ; # capital B + C2 D092 ; # capital V + C3 D093 ; # capital G + C4 D094 ; # capital D + C5 D095 ; # capital YE + C6 D096 ; # capital ZH + C7 D097 ; # capital Z + C8 D098 ; # capital I + C9 D099 ; # capital J + CA D09A ; # capital K + CB D09B ; # capital L + CC D09C ; # capital M + CD D09D ; # capital N + CE D09E ; # capital O + CF D09F ; # capital P + + D0 D0A0 ; # capital R + D1 D0A1 ; # capital S + D2 D0A2 ; # capital T + D3 D0A3 ; # capital U + D4 D0A4 ; # capital F + D5 D0A5 ; # capital KH + D6 D0A6 ; # capital TS + D7 D0A7 ; # capital CH + D8 D0A8 ; # capital SH + D9 D0A9 ; # capital SHCH + DA D0AA ; # capital hard sign + DB D0AB ; # capital Y + DC D0AC ; # capital soft sign + DD D0AD ; # capital E + DE D0AE ; # capital YU + DF D0AF ; # capital YA + + E0 D0B0 ; # small a + E1 D0B1 ; # small b + E2 D0B2 ; # small v + E3 D0B3 ; # small g + E4 D0B4 ; # small d + E5 D0B5 ; # small ye + E6 D0B6 ; # small zh + E7 D0B7 ; # small z + E8 D0B8 ; # small i + E9 D0B9 ; # small j + EA D0BA ; # small k + EB D0BB ; # small l + EC D0BC ; # small m + ED D0BD ; # small n + EE D0BE ; # small o + EF D0BF ; # small p + + F0 D180 ; # small r + F1 D181 ; # small s + F2 D182 ; # small t + F3 D183 ; # small u + F4 D184 ; # small f + F5 D185 ; # small kh + F6 D186 ; # small ts + F7 D187 ; # small ch + F8 D188 ; # small sh + F9 D189 ; # small shch + FA D18A ; # small hard sign + FB D18B ; # small y + FC D18C ; # small soft sign + FD D18D ; # small e + FE D18E ; # small yu + FF D18F ; # small ya +} diff --git a/docs/html/50x.html b/docs/html/50x.html new file mode 100644 index 000000000..b180ed281 --- /dev/null +++ b/docs/html/50x.html @@ -0,0 +1,14 @@ + + +The page is temporarily unavailable + + + +

+The page you are looking for is temporarily unavailable.
+Please try again later. +

+ + diff --git a/docs/xml/nginx/changes.xml b/docs/xml/nginx/changes.xml index b641d3973..879da4f14 100644 --- a/docs/xml/nginx/changes.xml +++ b/docs/xml/nginx/changes.xml @@ -9,6 +9,80 @@ nginx changelog + + + + +директивы proxy_redirect_errors и fastcgi_redirect_errors +переименованы соответственно в proxy_intercept_errors и +fastcgi_intercept_errors. + + +the "proxy_redirect_errors" and "fastcgi_redirect_errors" directives +was renamed to the "proxy_intercept_errors" and +"fastcgi_intercept_errors" directives. + + + + + +модуль ngx_http_charset_module поддерживает перекодирование из +однобайтных кодировок в UTF-8 и обратно. + + +the ngx_http_charset_module supports the recoding from the single byte +encodings to the UTF-8 encoding and back. + + + + + +в режиме прокси и FastCGI поддерживается строка заголовка "X-Accel-Charset" +в ответе бэкенда. + + +the "X-Accel-Charset" response header line is supported in proxy +and FastCGI mode. + + + + + +символ "\" в парах "\"" и "\'" в SSI командах убирался только, если +также использовался символ "$". + + +the "\" escape symbol in the "\"" and "\'" pairs in the SSI command +was removed only if the command also has the "$" symbol. + + + + + +при некоторых условиях в SSI после вставки могла быть добавлена +строка "<!--". + + +the "<!--" string might be added on some conditions +in the SSI after inclusion. + + + + + +если в заголовке ответа была строка "Content-Length: 0", +при использовании небуферизированного проксировании не закрывалось соединение +с клиентом. + + +if the "Content-Length: 0" header line was in response, then +in nonbuffered proxying mode the client connection was not closed. + + + + + + @@ -3211,8 +3285,8 @@ the "limit_rate" directive is supported in in proxy and FastCGI mode. в ответе бэкенда. -the "X-Accel-Limit-Rate" response header line is supported in proxy and FastCGI -mode. +the "X-Accel-Limit-Rate" response header line is supported in proxy +and FastCGI mode. diff --git a/src/core/nginx.h b/src/core/nginx.h index cd1917fec..834a4c895 100644 --- a/src/core/nginx.h +++ b/src/core/nginx.h @@ -8,7 +8,7 @@ #define _NGINX_H_INCLUDED_ -#define NGINX_VER "nginx/0.3.49" +#define NGINX_VER "nginx/0.3.50" #define NGINX_VAR "NGINX" #define NGX_OLDPID_EXT ".oldbin" diff --git a/src/core/ngx_inet.h b/src/core/ngx_inet.h index 25f923230..7600ed801 100644 --- a/src/core/ngx_inet.h +++ b/src/core/ngx_inet.h @@ -99,7 +99,6 @@ typedef struct { unsigned uri_part:1; unsigned port_only:1; - unsigned virtual:1; } ngx_inet_upstream_t; diff --git a/src/core/ngx_string.c b/src/core/ngx_string.c index 6a81984dd..3716df661 100644 --- a/src/core/ngx_string.c +++ b/src/core/ngx_string.c @@ -750,16 +750,82 @@ ngx_decode_base64(ngx_str_t *dst, ngx_str_t *src) } +/* + * ngx_utf_decode() decodes two and more bytes UTF sequences only + * the return values: + * 0x80 - 0x10ffff valid character + * 0x10ffff - 0xfffffffd invalid sequence + * 0xfffffffe incomplete sequence + * 0xffffffff error + */ + +uint32_t +ngx_utf_decode(u_char **p, size_t n) +{ + size_t len; + uint32_t u, i, valid; + + u = **p; + + if (u > 0xf0) { + + u &= 0x07; + valid = 0xffff; + len = 3; + + } else if (u > 0xe0) { + + u &= 0x0f; + valid = 0x7ff; + len = 2; + + } else if (u > 0xc0) { + + u &= 0x1f; + valid = 0x7f; + len = 1; + + } else { + (*p)++; + return 0xffffffff; + } + + if (n - 1 < len) { + return 0xfffffffe; + } + + (*p)++; + + while (len) { + i = *(*p)++; + + if (i < 0x80) { + return 0xffffffff; + } + + u = (u << 6) | (i & 0x3f); + + len--; + } + + if (u > valid) { + return u; + } + + return 0xffffffff; +} + + size_t -ngx_utf_length(ngx_str_t *utf) +ngx_utf_length(u_char *p, size_t n) { u_char c; size_t len; ngx_uint_t i; - for (len = 0, i = 0; i < utf->len; len++, i++) { + for (len = 0, i = 0; i < n; len++, i++) { - c = utf->data[i]; + c = p[i]; if (c < 0x80) { continue; @@ -775,7 +841,7 @@ ngx_utf_length(ngx_str_t *utf) /* invalid utf */ - return utf->len; + return n; } return len; diff --git a/src/core/ngx_string.h b/src/core/ngx_string.h index 818a290d1..10f87ae93 100644 --- a/src/core/ngx_string.h +++ b/src/core/ngx_string.h @@ -146,7 +146,8 @@ void ngx_md5_text(u_char *text, u_char *md5); void ngx_encode_base64(ngx_str_t *dst, ngx_str_t *src); ngx_int_t ngx_decode_base64(ngx_str_t *dst, ngx_str_t *src); -size_t ngx_utf_length(ngx_str_t *utf); +uint32_t ngx_utf_decode(u_char **p, size_t n); +size_t ngx_utf_length(u_char *p, size_t n); u_char * ngx_utf_cpystrn(u_char *dst, u_char *src, size_t n); diff --git a/src/http/modules/ngx_http_autoindex_module.c b/src/http/modules/ngx_http_autoindex_module.c index b1ba178b9..535456827 100644 --- a/src/http/modules/ngx_http_autoindex_module.c +++ b/src/http/modules/ngx_http_autoindex_module.c @@ -319,7 +319,7 @@ ngx_http_autoindex_handler(ngx_http_request_t *r) NGX_ESCAPE_HTML); if (r->utf8) { - entry->utf_len = ngx_utf_length(&entry->name); + entry->utf_len = ngx_utf_length(entry->name.data, entry->name.len); } else { entry->utf_len = len; } diff --git a/src/http/modules/ngx_http_charset_filter_module.c b/src/http/modules/ngx_http_charset_filter_module.c index c7aa14cfb..635e6c521 100644 --- a/src/http/modules/ngx_http_charset_filter_module.c +++ b/src/http/modules/ngx_http_charset_filter_module.c @@ -9,56 +9,94 @@ #include -#define NGX_HTTP_NO_CHARSET -2 +#define NGX_HTTP_NO_CHARSET -2 + +/* 1 byte length and up to 3 bytes for the UTF-8 encoding of the UCS-2 */ +#define NGX_UTF_LEN 4 + +#define NGX_HTML_ENTITY_LEN (sizeof("􏿿") - 1) typedef struct { - u_char **tables; - ngx_str_t name; + u_char **tables; + ngx_str_t name; - ngx_uint_t utf8; /* unsigned utf8:1; */ + unsigned length:16; + unsigned utf8:1; } ngx_http_charset_t; typedef struct { - ngx_int_t src; - ngx_int_t dst; + ngx_int_t src; + ngx_int_t dst; } ngx_http_charset_recode_t; typedef struct { - ngx_int_t src; - ngx_int_t dst; - u_char *src2dst; - u_char *dst2src; + ngx_int_t src; + ngx_int_t dst; + u_char *src2dst; + u_char *dst2src; } ngx_http_charset_tables_t; typedef struct { - ngx_array_t charsets; /* ngx_http_charset_t */ - ngx_array_t tables; /* ngx_http_charset_tables_t */ - ngx_array_t recodes; /* ngx_http_charset_recode_t */ + ngx_array_t charsets; /* ngx_http_charset_t */ + ngx_array_t tables; /* ngx_http_charset_tables_t */ + ngx_array_t recodes; /* ngx_http_charset_recode_t */ } ngx_http_charset_main_conf_t; typedef struct { - ngx_int_t charset; - ngx_int_t source_charset; - ngx_flag_t override_charset; + ngx_int_t charset; + ngx_int_t source_charset; + ngx_flag_t override_charset; } ngx_http_charset_loc_conf_t; typedef struct { - u_char *table; - ngx_int_t charset; + u_char *table; + ngx_int_t charset; + + ngx_chain_t *busy; + ngx_chain_t *free_bufs; + ngx_chain_t *free_buffers; + + size_t saved_len; + u_char saved[NGX_UTF_LEN]; + + unsigned length:16; + unsigned from_utf8:1; + unsigned to_utf8:1; } ngx_http_charset_ctx_t; -static ngx_uint_t ngx_http_charset_recode(ngx_buf_t *b, u_char *table); +typedef struct { + ngx_http_charset_tables_t *table; + ngx_http_charset_t *charset; + ngx_uint_t characters; +} ngx_http_charset_conf_ctx_t; -static char *ngx_charset_map_block(ngx_conf_t *cf, ngx_command_t *cmd, + +static ngx_int_t ngx_http_charset_get_charset(ngx_http_charset_t *charsets, + ngx_uint_t n, u_char *charset); +static ngx_int_t ngx_http_charset_set_charset(ngx_http_request_t *r, + ngx_http_charset_t *charsets, ngx_int_t charset, ngx_int_t source_charset); +static ngx_uint_t ngx_http_charset_recode(ngx_buf_t *b, u_char *table); +static ngx_chain_t *ngx_http_charset_recode_from_utf8(ngx_pool_t *pool, + ngx_buf_t *buf, ngx_http_charset_ctx_t *ctx); +static ngx_chain_t *ngx_http_charset_recode_to_utf8(ngx_pool_t *pool, + ngx_buf_t *buf, ngx_http_charset_ctx_t *ctx); + +static ngx_chain_t *ngx_http_charset_get_buf(ngx_pool_t *pool, + ngx_http_charset_ctx_t *ctx); +static ngx_chain_t *ngx_http_charset_get_buffer(ngx_pool_t *pool, + ngx_http_charset_ctx_t *ctx, size_t size); + +static char *ngx_http_charset_map_block(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_http_charset_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf); -static char *ngx_charset_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf); static char *ngx_http_set_charset_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); @@ -101,7 +139,7 @@ static ngx_command_t ngx_http_charset_filter_commands[] = { { ngx_string("charset_map"), NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE2, - ngx_charset_map_block, + ngx_http_charset_map_block, NGX_HTTP_MAIN_CONF_OFFSET, 0, NULL }, @@ -148,10 +186,10 @@ static ngx_http_output_body_filter_pt ngx_http_next_body_filter; static ngx_int_t ngx_http_charset_header_filter(ngx_http_request_t *r) { - size_t len; - u_char *p; + u_char *ct; ngx_int_t charset, source_charset; - ngx_uint_t i; + ngx_str_t *mc; + ngx_uint_t n; ngx_http_charset_t *charsets; ngx_http_charset_ctx_t *ctx; ngx_http_charset_loc_conf_t *lcf, *mlcf; @@ -159,112 +197,153 @@ ngx_http_charset_header_filter(ngx_http_request_t *r) mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module); - ctx = ngx_http_get_module_ctx(r->main, ngx_http_charset_filter_module); + charsets = mcf->charsets.elts; + n = mcf->charsets.nelts; - if (ctx == NULL) { - mlcf = ngx_http_get_module_loc_conf(r->main, - ngx_http_charset_filter_module); - charset = mlcf->charset; + /* destination charset */ + + if (r == r->main) { + + if (r->headers_out.content_type.len == 0) { + return ngx_http_next_header_filter(r); + } + + if (r->headers_out.override_charset + && r->headers_out.override_charset->len) + { + charset = ngx_http_charset_get_charset(charsets, n, + r->headers_out.override_charset->data); + + if (charset == NGX_HTTP_NO_CHARSET) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "unknown charset \"%V\" to override", + &r->headers_out.override_charset); + + return ngx_http_next_header_filter(r); + } + + } else { + mlcf = ngx_http_get_module_loc_conf(r, + ngx_http_charset_filter_module); + charset = mlcf->charset; + + if (charset == NGX_HTTP_NO_CHARSET) { + return ngx_http_next_header_filter(r); + } + + if (r->headers_out.charset.len) { + if (mlcf->override_charset == 0) { + return ngx_http_next_header_filter(r); + } + + } else { + ct = r->headers_out.content_type.data; + + if (ngx_strncasecmp(ct, "text/", 5) != 0 + && ngx_strncasecmp(ct, "application/x-javascript", 24) != 0) + { + return ngx_http_next_header_filter(r); + } + } + } + + } else { + ctx = ngx_http_get_module_ctx(r->main, ngx_http_charset_filter_module); + + if (ctx == NULL) { + + mc = &r->main->headers_out.charset; + + if (mc->len == 0) { + return ngx_http_next_header_filter(r); + } + + ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_charset_ctx_t)); + if (ctx == NULL) { + return NGX_ERROR; + } + + ngx_http_set_ctx(r->main, ctx, ngx_http_charset_filter_module); + + charset = ngx_http_charset_get_charset(charsets, n, mc->data); + + ctx->charset = charset; + + if (charset == NGX_HTTP_NO_CHARSET) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "unknown charset \"%V\" of main request", mc); + + return ngx_http_next_header_filter(r); + } + } + + charset = ctx->charset; if (charset == NGX_HTTP_NO_CHARSET) { return ngx_http_next_header_filter(r); } - - } else { - charset = ctx->charset; } - charsets = mcf->charsets.elts; + /* source charset */ - if (r == r->main) { - if (r->headers_out.content_type.len == 0) { - return ngx_http_next_header_filter(r); - } + if (r->headers_out.charset.len == 0) { + lcf = ngx_http_get_module_loc_conf(r, ngx_http_charset_filter_module); - if (ngx_strncasecmp(r->headers_out.content_type.data, "text/", 5) != 0 - && ngx_strncasecmp(r->headers_out.content_type.data, - "application/x-javascript", 24) != 0) - { - return ngx_http_next_header_filter(r); - } - - } else { - if (r->headers_out.content_type.len == 0) { - mlcf = ngx_http_get_module_loc_conf(r->main, - ngx_http_charset_filter_module); - source_charset = mlcf->source_charset; - - goto found; - } + return ngx_http_charset_set_charset(r, mcf->charsets.elts, charset, + lcf->source_charset); } - lcf = ngx_http_get_module_loc_conf(r, ngx_http_charset_filter_module); + source_charset = ngx_http_charset_get_charset(charsets, n, + r->headers_out.charset.data); - len = 0; - - for (p = r->headers_out.content_type.data; *p; p++) { - if (*p == ';') { - len = p - r->headers_out.content_type.data; - } - - if (ngx_strncasecmp(p, "charset=", 8) != 0) { - continue; - } - - p += 8; - - for (i = 0; i < mcf->charsets.nelts; i++) { - - if (ngx_strcasecmp(p, charsets[i].name.data) == 0) { - - if (r == r->main && lcf->override_charset == 0) { - ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_charset_ctx_t)); - if (ctx == NULL) { - return NGX_ERROR; - } - - ngx_http_set_ctx(r, ctx, ngx_http_charset_filter_module); - - ctx->charset = i; - - return ngx_http_next_header_filter(r); - } - - if (i != (ngx_uint_t) charset - && (charsets[i].tables == NULL - || charsets[i].tables[charset] == NULL)) - { - ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, - "no \"charset_map\" between the charsets " - "\"%V\" and \"%V\"", - &charsets[i].name, &charsets[charset].name); - - return ngx_http_next_header_filter(r); - } - - r->headers_out.content_type.len = len; - - if (r->headers_out.status == NGX_HTTP_MOVED_PERMANENTLY - || r->headers_out.status == NGX_HTTP_MOVED_TEMPORARILY) - { - /* - * do not set charset for the redirect because NN 4.x - * uses this charset instead of the next page charset - */ - - r->headers_out.charset.len = 0; - return ngx_http_next_header_filter(r); - } - - source_charset = i; - - goto found; - } - } + if (source_charset == NGX_HTTP_NO_CHARSET) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "unknown source charset \"%V\"", &r->headers_out.charset); return ngx_http_next_header_filter(r); } + if (source_charset != charset + && (charsets[source_charset].tables == NULL + || charsets[source_charset].tables[charset] == NULL)) + { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "no \"charset_map\" between the charsets " + "\"%V\" and \"%V\"", + &charsets[source_charset].name, &charsets[charset].name); + + return ngx_http_next_header_filter(r); + } + + r->headers_out.content_type.len = r->headers_out.content_type_len; + + return ngx_http_charset_set_charset(r, mcf->charsets.elts, charset, + source_charset); +} + + +static ngx_int_t +ngx_http_charset_get_charset(ngx_http_charset_t *charsets, ngx_uint_t n, + u_char *charset) +{ + ngx_uint_t i; + + for (i = 0; i < n; i++) { + if (ngx_strcasecmp(charsets[i].name.data, charset) == 0) { + return i; + } + } + + return NGX_HTTP_NO_CHARSET; +} + + +static ngx_int_t +ngx_http_charset_set_charset(ngx_http_request_t *r, + ngx_http_charset_t *charsets, ngx_int_t charset, ngx_int_t source_charset) +{ + ngx_http_charset_ctx_t *ctx; + if (r->headers_out.status == NGX_HTTP_MOVED_PERMANENTLY || r->headers_out.status == NGX_HTTP_MOVED_TEMPORARILY) { @@ -274,17 +353,10 @@ ngx_http_charset_header_filter(ngx_http_request_t *r) */ r->headers_out.charset.len = 0; + return ngx_http_next_header_filter(r); } - if (r->headers_out.charset.len) { - return ngx_http_next_header_filter(r); - } - - source_charset = lcf->source_charset; - -found: - r->headers_out.charset = charsets[charset].name; r->utf8 = charsets[charset].utf8; @@ -301,6 +373,13 @@ found: ctx->table = charsets[source_charset].tables[charset]; ctx->charset = charset; + ctx->length = charsets[charset].length; + ctx->from_utf8 = charsets[source_charset].utf8; + ctx->to_utf8 = charsets[charset].utf8; + + if ((ctx->to_utf8 || ctx->from_utf8) && r == r->main) { + ngx_http_clear_content_length(r); + } r->filter_need_in_memory = 1; @@ -311,7 +390,9 @@ found: static ngx_int_t ngx_http_charset_body_filter(ngx_http_request_t *r, ngx_chain_t *in) { - ngx_chain_t *cl; + ngx_int_t rc; + ngx_buf_t *b; + ngx_chain_t *cl, *out, **ll; ngx_http_charset_ctx_t *ctx; ctx = ngx_http_get_module_ctx(r, ngx_http_charset_filter_module); @@ -320,6 +401,84 @@ ngx_http_charset_body_filter(ngx_http_request_t *r, ngx_chain_t *in) return ngx_http_next_body_filter(r, in); } + if ((ctx->to_utf8 || ctx->from_utf8) || ctx->busy) { + + out = NULL; + ll = &out; + + for (cl = in; cl; cl = cl->next) { + b = cl->buf; + + if (ngx_buf_size(b) == 0) { + continue; + } + + if (ctx->to_utf8) { + *ll = ngx_http_charset_recode_to_utf8(r->pool, b, ctx); + + } else { + *ll = ngx_http_charset_recode_from_utf8(r->pool, b, ctx); + } + + if (*ll == NULL) { + return NGX_ERROR; + } + + while (*ll) { + ll = &(*ll)->next; + } + } + + rc = ngx_http_next_body_filter(r, out); + + if (out) { + if (ctx->busy == NULL) { + ctx->busy = out; + + } else { + for (cl = ctx->busy; cl->next; cl = cl->next) { /* void */ } + cl->next = out; + } + } + + while (ctx->busy) { + + cl = ctx->busy; + b = cl->buf; + + if (ngx_buf_size(b) != 0) { + break; + } + +#if (NGX_HAVE_WRITE_ZEROCOPY) + if (b->zerocopy_busy) { + break; + } +#endif + + ctx->busy = cl->next; + + if (b->tag != (ngx_buf_tag_t) &ngx_http_charset_filter_module) { + continue; + } + + if (b->shadow) { + b->shadow->pos = b->shadow->last; + } + + if (b->pos) { + cl->next = ctx->free_buffers; + ctx->free_buffers = cl; + continue; + } + + cl->next = ctx->free_bufs; + ctx->free_bufs = cl; + } + + return rc; + } + for (cl = in; cl; cl = cl->next) { (void) ngx_http_charset_recode(cl->buf, ctx->table); } @@ -353,17 +512,506 @@ ngx_http_charset_recode(ngx_buf_t *b, u_char *table) } +static ngx_chain_t * +ngx_http_charset_recode_from_utf8(ngx_pool_t *pool, ngx_buf_t *buf, + ngx_http_charset_ctx_t *ctx) +{ + size_t len, size; + u_char c, *p, *src, *dst, *saved, **table; + uint32_t n; + ngx_buf_t *b; + ngx_uint_t i; + ngx_chain_t *out, *cl, **ll; + + src = buf->pos; + + if (ctx->saved_len == 0) { + + for ( /* void */ ; src < buf->last; src++) { + + if (*src < 0x80) { + continue; + } + + len = src - buf->pos; + + if (len > 512) { + out = ngx_http_charset_get_buf(pool, ctx); + if (out == NULL) { + return NULL; + } + + b = out->buf; + + b->temporary = buf->temporary; + b->memory = buf->memory; + b->mmap = buf->mmap; + b->flush = buf->flush; + + b->pos = buf->pos; + b->last = src; + + out->buf = b; + out->next = NULL; + + size = buf->last - src; + + saved = src; + n = ngx_utf_decode(&saved, size); + + if (n == 0xfffffffe) { + /* incomplete UTF-8 symbol */ + + ngx_memcpy(ctx->saved, src, size); + ctx->saved_len = size; + + b->shadow = buf; + + return out; + } + + } else { + out = NULL; + size = len + buf->last - src; + src = buf->pos; + } + + if (size < NGX_HTML_ENTITY_LEN) { + size += NGX_HTML_ENTITY_LEN; + } + + cl = ngx_http_charset_get_buffer(pool, ctx, size); + if (cl == NULL) { + return NULL; + } + + if (out) { + out->next = cl; + + } else { + out = cl; + } + + b = cl->buf; + dst = b->pos; + + goto recode; + } + + out = ngx_alloc_chain_link(pool); + if (out == NULL) { + return NULL; + } + + out->buf = buf; + out->next = NULL; + + return out; + } + + /* process incomplete UTF sequence from previous buffer */ + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pool->log, 0, + "http charset utf saved: %z", ctx->saved_len); + + p = src; + + for (i = ctx->saved_len; i < NGX_UTF_LEN; i++) { + ctx->saved[i] = *p++; + + if (p == buf->last) { + break; + } + } + + saved = ctx->saved; + n = ngx_utf_decode(&saved, i); + + c = '\0'; + + if (n < 0x10000) { + table = (u_char **) ctx->table; + p = table[n >> 8]; + + if (p) { + c = p[n & 0xff]; + } + + } else if (n == 0xfffffffe) { + + /* incomplete UTF-8 symbol */ + + if (i < NGX_UTF_LEN) { + out = ngx_http_charset_get_buf(pool, ctx); + if (out == NULL) { + return NULL; + } + + b = out->buf; + + b->pos = buf->pos; + b->last = buf->last; + b->sync = 1; + b->shadow = buf; + + ngx_memcpy(&ctx->saved[ctx->saved_len], src, i); + ctx->saved_len += i; + + return out; + } + } + + size = buf->last - buf->pos; + + if (size < NGX_HTML_ENTITY_LEN) { + size += NGX_HTML_ENTITY_LEN; + } + + cl = ngx_http_charset_get_buffer(pool, ctx, size); + if (cl == NULL) { + return NULL; + } + + out = cl; + + b = cl->buf; + dst = b->pos; + + if (c) { + *dst++ = c; + + } else if (n == 0xfffffffe) { + *dst++ = '?'; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pool->log, 0, + "http charset invalid utf 0"); + + saved = &ctx->saved[NGX_UTF_LEN]; + + } else if (n > 0x10ffff) { + *dst++ = '?'; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pool->log, 0, + "http charset invalid utf 1"); + + } else { + dst = ngx_sprintf(dst, "&#%uD;", n); + } + + src += (saved - ctx->saved) - ctx->saved_len; + ctx->saved_len = 0; + +recode: + + ll = &cl->next; + + table = (u_char **) ctx->table; + + while (src < buf->last) { + + if ((size_t) (b->end - dst) < NGX_HTML_ENTITY_LEN) { + b->last = dst; + + size = buf->last - src + NGX_HTML_ENTITY_LEN; + + cl = ngx_http_charset_get_buffer(pool, ctx, size); + if (cl == NULL) { + return NULL; + } + + *ll = cl; + ll = &cl->next; + + b = cl->buf; + dst = b->pos; + } + + if (*src < 0x80) { + *dst++ = *src++; + continue; + } + + len = buf->last - src; + + n = ngx_utf_decode(&src, len); + + if (n < 0x10000) { + + p = table[n >> 8]; + + if (p) { + c = p[n & 0xff]; + + if (c) { + *dst++ = c; + continue; + } + } + + dst = ngx_sprintf(dst, "&#%uD;", n); + + continue; + } + + if (n == 0xfffffffe) { + /* incomplete UTF-8 symbol */ + + ngx_memcpy(ctx->saved, src, len); + ctx->saved_len = len; + + if (b->pos == dst) { + b->sync = 1; + b->temporary = 0; + } + + break; + } + + if (n > 0x10ffff) { + *dst++ = '?'; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pool->log, 0, + "http charset invalid utf 2"); + + continue; + } + + /* n > 0xffff */ + + dst = ngx_sprintf(dst, "&#%uD;", n); + } + + b->last = dst; + + b->last_buf = buf->last_buf; + b->last_in_chain = buf->last_in_chain; + b->flush = buf->flush; + + b->shadow = buf; + + return out; +} + + +static ngx_chain_t * +ngx_http_charset_recode_to_utf8(ngx_pool_t *pool, ngx_buf_t *buf, + ngx_http_charset_ctx_t *ctx) +{ + size_t len, size; + u_char *p, *src, *dst, *table; + ngx_buf_t *b; + ngx_chain_t *out, *cl, **ll; + + table = ctx->table; + + for (src = buf->pos; src < buf->last; src++) { + if (table[*src * NGX_UTF_LEN] == '\1') { + continue; + } + + goto recode; + } + + out = ngx_alloc_chain_link(pool); + if (out == NULL) { + return NULL; + } + + out->buf = buf; + out->next = NULL; + + return out; + +recode: + + /* + * we assume that there are about half of characters to be recoded, + * so we preallocate "size / 2 + size / 2 * ctx->length" + */ + + len = src - buf->pos; + + if (len > 512) { + out = ngx_http_charset_get_buf(pool, ctx); + if (out == NULL) { + return NULL; + } + + b = out->buf; + + b->temporary = buf->temporary; + b->memory = buf->memory; + b->mmap = buf->mmap; + b->flush = buf->flush; + + b->pos = buf->pos; + b->last = src; + + out->buf = b; + out->next = NULL; + + size = buf->last - src; + size = size / 2 + size / 2 * ctx->length; + + } else { + out = NULL; + + size = buf->last - src; + size = len + size / 2 + size / 2 * ctx->length; + + src = buf->pos; + } + + cl = ngx_http_charset_get_buffer(pool, ctx, size); + if (cl == NULL) { + return NULL; + } + + if (out) { + out->next = cl; + + } else { + out = cl; + } + + ll = &cl->next; + + b = cl->buf; + dst = b->pos; + + while (src < buf->last) { + + p = &table[*src++ * NGX_UTF_LEN]; + len = *p++; + + if ((size_t) (b->end - dst) < len) { + b->last = dst; + + size = buf->last - src; + size = len + size / 2 + size / 2 * ctx->length; + + cl = ngx_http_charset_get_buffer(pool, ctx, size); + if (cl == NULL) { + return NULL; + } + + *ll = cl; + ll = &cl->next; + + b = cl->buf; + dst = b->pos; + } + + while (len) { + *dst++ = *p++; + len--; + } + } + + b->last = dst; + + b->last_buf = buf->last_buf; + b->last_in_chain = buf->last_in_chain; + b->flush = buf->flush; + + b->shadow = buf; + + return out; +} + + +static ngx_chain_t * +ngx_http_charset_get_buf(ngx_pool_t *pool, ngx_http_charset_ctx_t *ctx) +{ + ngx_chain_t *cl; + + cl = ctx->free_bufs; + + if (cl) { + ctx->free_bufs = cl->next; + + cl->buf->shadow = NULL; + cl->next = NULL; + + return cl; + } + + cl = ngx_alloc_chain_link(pool); + if (cl == NULL) { + return NULL; + } + + cl->buf = ngx_calloc_buf(pool); + if (cl->buf == NULL) { + return NULL; + } + + cl->next = NULL; + + cl->buf->tag = (ngx_buf_tag_t) &ngx_http_charset_filter_module; + + return cl; +} + + +static ngx_chain_t * +ngx_http_charset_get_buffer(ngx_pool_t *pool, ngx_http_charset_ctx_t *ctx, + size_t size) +{ + ngx_buf_t *b; + ngx_chain_t *cl, **ll; + + for (ll = &ctx->free_buffers, cl = ctx->free_buffers; + cl; + ll = &cl->next, cl = cl->next) + { + b = cl->buf; + + if ((size_t) (b->end - b->start) >= size) { + *ll = cl->next; + cl->next = NULL; + + b->pos = b->start; + b->temporary = 1; + b->shadow = NULL; + + return cl; + } + } + + cl = ngx_alloc_chain_link(pool); + if (cl == NULL) { + return NULL; + } + + cl->buf = ngx_create_temp_buf(pool, size); + if (cl->buf == NULL) { + return NULL; + } + + cl->next = NULL; + + cl->buf->temporary = 1; + cl->buf->tag = (ngx_buf_tag_t) &ngx_http_charset_filter_module; + + return cl; +} + + static char * -ngx_charset_map_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +ngx_http_charset_map_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_charset_main_conf_t *mcf = conf; - char *rv; - ngx_int_t src, dst; - ngx_uint_t i; - ngx_str_t *value; - ngx_conf_t pvcf; - ngx_http_charset_tables_t *table; + char *rv; + u_char *p, *dst2src, **pp; + ngx_int_t src, dst; + ngx_uint_t i, n; + ngx_str_t *value; + ngx_conf_t pvcf; + ngx_http_charset_t *charset; + ngx_http_charset_tables_t *table; + ngx_http_charset_conf_ctx_t ctx; value = cf->args->elts; @@ -404,45 +1052,98 @@ ngx_charset_map_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) table->src = src; table->dst = dst; - table->src2dst = ngx_palloc(cf->pool, 256); - if (table->src2dst == NULL) { - return NGX_CONF_ERROR; + if (ngx_strcasecmp(value[2].data, "utf-8") == 0) { + table->src2dst = ngx_pcalloc(cf->pool, 256 * NGX_UTF_LEN); + if (table->src2dst == NULL) { + return NGX_CONF_ERROR; + } + + table->dst2src = ngx_pcalloc(cf->pool, 256 * sizeof(void *)); + if (table->dst2src == NULL) { + return NGX_CONF_ERROR; + } + + dst2src = ngx_pcalloc(cf->pool, 256); + if (dst2src == NULL) { + return NGX_CONF_ERROR; + } + + pp = (u_char **) &table->dst2src[0]; + pp[0] = dst2src; + + for (i = 0; i < 128; i++) { + p = &table->src2dst[i * NGX_UTF_LEN]; + p[0] = '\1'; + p[1] = (u_char) i; + dst2src[i] = (u_char) i; + } + + for (/* void */; i < 256; i++) { + p = &table->src2dst[i * NGX_UTF_LEN]; + p[0] = '\1'; + p[1] = '?'; + } + + } else { + table->src2dst = ngx_palloc(cf->pool, 256); + if (table->src2dst == NULL) { + return NGX_CONF_ERROR; + } + + table->dst2src = ngx_palloc(cf->pool, 256); + if (table->dst2src == NULL) { + return NGX_CONF_ERROR; + } + + for (i = 0; i < 128; i++) { + table->src2dst[i] = (u_char) i; + table->dst2src[i] = (u_char) i; + } + + for (/* void */; i < 256; i++) { + table->src2dst[i] = '?'; + table->dst2src[i] = '?'; + } } - table->dst2src = ngx_palloc(cf->pool, 256); - if (table->dst2src == NULL) { - return NGX_CONF_ERROR; - } + charset = mcf->charsets.elts; - for (i = 0; i < 128; i++) { - table->src2dst[i] = (u_char) i; - table->dst2src[i] = (u_char) i; - } - - for (/* void */; i < 256; i++) { - table->src2dst[i] = '?'; - table->dst2src[i] = '?'; - } + ctx.table = table; + ctx.charset = &charset[dst]; + ctx.characters = 0; pvcf = *cf; - cf->ctx = table; - cf->handler = ngx_charset_map; + cf->ctx = &ctx; + cf->handler = ngx_http_charset_map; cf->handler_conf = conf; rv = ngx_conf_parse(cf, NULL); *cf = pvcf; + if (ctx.characters) { + n = ctx.charset->length; + ctx.charset->length /= ctx.characters; + + if (((n * 10) / ctx.characters) % 10 > 4) { + ctx.charset->length++; + } + } + return rv; } static char * -ngx_charset_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) +ngx_http_charset_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) { - ngx_int_t src, dst; - ngx_str_t *value; - ngx_http_charset_tables_t *table; + u_char *p, *dst2src, **pp; + uint32_t n; + ngx_int_t src, dst; + ngx_str_t *value; + ngx_uint_t i; + ngx_http_charset_tables_t *table; + ngx_http_charset_conf_ctx_t *ctx; if (cf->args->nelts != 2) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameters number"); @@ -458,18 +1159,67 @@ ngx_charset_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) return NGX_CONF_ERROR; } - dst = ngx_hextoi(value[1].data, value[1].len); - if (dst == NGX_ERROR || dst > 255) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "invalid value \"%V\"", &value[1]); - return NGX_CONF_ERROR; + ctx = cf->ctx; + table = ctx->table; + + if (ctx->charset->utf8) { + p = &table->src2dst[src * NGX_UTF_LEN]; + + *p++ = (u_char) (value[1].len / 2); + + for (i = 0; i < value[1].len; i += 2) { + dst = ngx_hextoi(&value[1].data[i], 2); + if (dst == NGX_ERROR || dst > 255) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid value \"%V\"", &value[1]); + return NGX_CONF_ERROR; + } + + *p++ = (u_char) dst; + } + + i /= 2; + + ctx->charset->length += i; + ctx->characters++; + + p = &table->src2dst[src * NGX_UTF_LEN] + 1; + + n = ngx_utf_decode(&p, i); + + if (n > 0xffff) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid value \"%V\"", &value[1]); + return NGX_CONF_ERROR; + } + + pp = (u_char **) &table->dst2src[0]; + + dst2src = pp[n >> 8]; + + if (dst2src == NULL) { + dst2src = ngx_pcalloc(cf->pool, 256); + if (dst2src == NULL) { + return NGX_CONF_ERROR; + } + + pp[n >> 8] = dst2src; + } + + dst2src[n & 0xff] = (u_char) src; + + } else { + dst = ngx_hextoi(value[1].data, value[1].len); + if (dst == NGX_ERROR || dst > 255) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid value \"%V\"", &value[1]); + return NGX_CONF_ERROR; + } + + table->src2dst[src] = (u_char) dst; + table->dst2src[dst] = (u_char) src; } - table = cf->ctx; - - table->src2dst[src] = (u_char) dst; - table->dst2src[dst] = (u_char) src; - return NGX_CONF_OK; } @@ -538,6 +1288,7 @@ ngx_http_add_charset(ngx_array_t *charsets, ngx_str_t *name) c->tables = NULL; c->name = *name; + c->length = 0; if (ngx_strcasecmp(name->data, "utf-8") == 0) { c->utf8 = 1; diff --git a/src/http/modules/ngx_http_fastcgi_module.c b/src/http/modules/ngx_http_fastcgi_module.c index 8663f881e..085fb47e4 100644 --- a/src/http/modules/ngx_http_fastcgi_module.c +++ b/src/http/modules/ngx_http_fastcgi_module.c @@ -156,6 +156,10 @@ static ngx_conf_deprecated_t ngx_conf_deprecated_fastcgi_header_buffer_size = { ngx_conf_deprecated, "fastcgi_header_buffer_size", "fastcgi_buffer_size" }; +static ngx_conf_deprecated_t ngx_conf_deprecated_fastcgi_redirect_errors = { + ngx_conf_deprecated, "fastcgi_redirect_errors", "fastcgi_intercept_errors" +}; + static ngx_conf_bitmask_t ngx_http_fastcgi_next_upstream_masks[] = { { ngx_string("error"), NGX_HTTP_UPSTREAM_FT_ERROR }, @@ -240,12 +244,19 @@ static ngx_command_t ngx_http_fastcgi_commands[] = { offsetof(ngx_http_fastcgi_loc_conf_t, upstream.pass_request_body), NULL }, + { ngx_string("fastcgi_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_fastcgi_loc_conf_t, upstream.intercept_errors), + NULL }, + { ngx_string("fastcgi_redirect_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_fastcgi_loc_conf_t, upstream.redirect_errors), - NULL }, + offsetof(ngx_http_fastcgi_loc_conf_t, upstream.intercept_errors), + &ngx_conf_deprecated_fastcgi_redirect_errors }, { ngx_string("fastcgi_read_timeout"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, @@ -1534,7 +1545,7 @@ ngx_http_fastcgi_create_loc_conf(ngx_conf_t *cf) conf->upstream.pass_request_headers = NGX_CONF_UNSET; conf->upstream.pass_request_body = NGX_CONF_UNSET; - conf->upstream.redirect_errors = NGX_CONF_UNSET; + conf->upstream.intercept_errors = NGX_CONF_UNSET; /* "fastcgi_cyclic_temp_file" is disabled */ conf->upstream.cyclic_temp_file = 0; @@ -1708,8 +1719,8 @@ ngx_http_fastcgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_value(conf->upstream.pass_request_body, prev->upstream.pass_request_body, 1); - ngx_conf_merge_value(conf->upstream.redirect_errors, - prev->upstream.redirect_errors, 0); + ngx_conf_merge_value(conf->upstream.intercept_errors, + prev->upstream.intercept_errors, 0); ngx_conf_merge_str_value(conf->index, prev->index, ""); diff --git a/src/http/modules/ngx_http_log_module.c b/src/http/modules/ngx_http_log_module.c index 3afb6a3db..5e1636385 100644 --- a/src/http/modules/ngx_http_log_module.c +++ b/src/http/modules/ngx_http_log_module.c @@ -16,17 +16,20 @@ typedef struct { ngx_array_t *ops; /* array of ngx_http_log_op_t */ } ngx_http_log_fmt_t; + typedef struct { ngx_array_t formats; /* array of ngx_http_log_fmt_t */ ngx_uint_t combined_used; /* unsigned combined_used:1 */ } ngx_http_log_main_conf_t; + typedef struct { ngx_open_file_t *file; time_t disk_full_time; ngx_array_t *ops; /* array of ngx_http_log_op_t */ } ngx_http_log_t; + typedef struct { ngx_array_t *logs; /* array of ngx_http_log_t */ ngx_uint_t off; /* unsigned off:1 */ diff --git a/src/http/modules/ngx_http_memcached_module.c b/src/http/modules/ngx_http_memcached_module.c index 6384af03e..5b07dbc52 100644 --- a/src/http/modules/ngx_http_memcached_module.c +++ b/src/http/modules/ngx_http_memcached_module.c @@ -524,7 +524,7 @@ ngx_http_memcached_create_loc_conf(ngx_conf_t *cf) conf->upstream.busy_buffers_size = 0; conf->upstream.max_temp_file_size = 0; conf->upstream.temp_file_write_size = 0; - conf->upstream.redirect_errors = 1; + conf->upstream.intercept_errors = 1; conf->upstream.redirect_404 = 1; conf->upstream.pass_request_headers = 0; conf->upstream.pass_request_body = 0; diff --git a/src/http/modules/ngx_http_proxy_module.c b/src/http/modules/ngx_http_proxy_module.c index 31a4355e5..f14151ae8 100644 --- a/src/http/modules/ngx_http_proxy_module.c +++ b/src/http/modules/ngx_http_proxy_module.c @@ -115,6 +115,10 @@ static ngx_conf_deprecated_t ngx_conf_deprecated_proxy_header_buffer_size = { ngx_conf_deprecated, "proxy_header_buffer_size", "proxy_buffer_size" }; +static ngx_conf_deprecated_t ngx_conf_deprecated_proxy_redirect_errors = { + ngx_conf_deprecated, "proxy_redirect_errors", "proxy_intercept_errors" +}; + static ngx_conf_bitmask_t ngx_http_proxy_next_upstream_masks[] = { { ngx_string("error"), NGX_HTTP_UPSTREAM_FT_ERROR }, @@ -178,12 +182,19 @@ static ngx_command_t ngx_http_proxy_commands[] = { offsetof(ngx_http_proxy_loc_conf_t, upstream.send_lowat), &ngx_http_proxy_lowat_post }, + { ngx_string("proxy_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_proxy_loc_conf_t, upstream.intercept_errors), + NULL }, + { ngx_string("proxy_redirect_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_proxy_loc_conf_t, upstream.redirect_errors), - NULL }, + offsetof(ngx_http_proxy_loc_conf_t, upstream.intercept_errors), + &ngx_conf_deprecated_proxy_redirect_errors }, { ngx_string("proxy_set_header"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2, @@ -1486,7 +1497,7 @@ ngx_http_proxy_create_loc_conf(ngx_conf_t *cf) conf->upstream.pass_request_headers = NGX_CONF_UNSET; conf->upstream.pass_request_body = NGX_CONF_UNSET; - conf->upstream.redirect_errors = NGX_CONF_UNSET; + conf->upstream.intercept_errors = NGX_CONF_UNSET; /* "proxy_cyclic_temp_file" is disabled */ conf->upstream.cyclic_temp_file = 0; @@ -1670,8 +1681,8 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_value(conf->upstream.pass_request_body, prev->upstream.pass_request_body, 1); - ngx_conf_merge_value(conf->upstream.redirect_errors, - prev->upstream.redirect_errors, 0); + ngx_conf_merge_value(conf->upstream.intercept_errors, + prev->upstream.intercept_errors, 0); ngx_conf_merge_value(conf->redirect, prev->redirect, 1); diff --git a/src/http/modules/ngx_http_range_filter_module.c b/src/http/modules/ngx_http_range_filter_module.c index a76b67a87..7beb091ca 100644 --- a/src/http/modules/ngx_http_range_filter_module.c +++ b/src/http/modules/ngx_http_range_filter_module.c @@ -145,7 +145,6 @@ ngx_http_range_header_filter(ngx_http_request_t *r) || r->headers_in.range->value.len < 7 || ngx_strncasecmp(r->headers_in.range->value.data, "bytes=", 6) != 0) { - r->headers_out.accept_ranges = ngx_list_push(&r->headers_out.headers); if (r->headers_out.accept_ranges == NULL) { return NGX_ERROR; diff --git a/src/http/modules/ngx_http_ssi_filter_module.c b/src/http/modules/ngx_http_ssi_filter_module.c index 817045e95..63c2dd138 100644 --- a/src/http/modules/ngx_http_ssi_filter_module.c +++ b/src/http/modules/ngx_http_ssi_filter_module.c @@ -788,7 +788,8 @@ ngx_http_ssi_output(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx) while (ctx->busy) { - b = ctx->busy->buf; + cl = ctx->busy; + b = cl->buf; if (ngx_buf_size(b) != 0) { break; @@ -804,7 +805,6 @@ ngx_http_ssi_output(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx) b->shadow->pos = b->shadow->last; } - cl = ctx->busy; ctx->busy = cl->next; if (ngx_buf_in_memory(b) || b->in_file) { @@ -942,9 +942,7 @@ ngx_http_ssi_parse(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx) case ssi_sharp_state: switch (ch) { case '#': - if (ctx->copy_start) { - ctx->saved = 0; - } + ctx->saved = 0; looked = 0; state = ssi_precommand_state; break; @@ -1417,11 +1415,11 @@ ngx_http_ssi_evaluate_string(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx, if (n == 0) { - if (!(flags & NGX_HTTP_SSI_ADD_PREFIX)) { - return NGX_OK; - } + data = text->data; + p = data; + + if ((flags & NGX_HTTP_SSI_ADD_PREFIX) && text->data[0] != '/') { - if (text->data[0] != '/') { for (prefix = r->uri.len; prefix; prefix--) { if (r->uri.data[prefix - 1] == '/') { break; @@ -1437,13 +1435,35 @@ ngx_http_ssi_evaluate_string(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx, } p = ngx_copy(data, r->uri.data, prefix); - ngx_memcpy(p, text->data, text->len); - - text->len = len; - text->data = data; } } + quoted = 0; + + for (i = 0 ; i < text->len; i++) { + ch = text->data[i]; + + if (!quoted) { + + if (ch == '\\') { + quoted = 1; + continue; + } + + } else { + quoted = 0; + + if (ch != '\\' && ch != '\'' && ch != '"' && ch != '$') { + *p++ = '\\'; + } + } + + *p++ = ch; + } + + text->len = p - data; + text->data = data; + return NGX_OK; } @@ -2140,6 +2160,7 @@ ngx_http_ssi_date_gmt_local_variable(ngx_http_request_t *r, v->len = ngx_sprintf(v->data, "%T", tp->sec + (gmt ? 0 : tp->gmtoff)) - v->data; + return NGX_OK; } diff --git a/src/http/modules/ngx_http_userid_filter_module.c b/src/http/modules/ngx_http_userid_filter_module.c index 810eb3ad1..92c53f83e 100644 --- a/src/http/modules/ngx_http_userid_filter_module.c +++ b/src/http/modules/ngx_http_userid_filter_module.c @@ -594,10 +594,6 @@ ngx_http_userid_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, ngx_http_userid_ctx_t *ctx; ngx_http_userid_conf_t *conf; - v->valid = 1; - v->no_cachable = 0; - v->not_found = 0; - ctx = ngx_http_get_module_ctx(r, ngx_http_userid_filter_module); uid = (uint32_t *) ((char *) ctx + data); @@ -615,6 +611,10 @@ ngx_http_userid_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, return NGX_ERROR; } + v->valid = 1; + v->no_cachable = 0; + v->not_found = 0; + ngx_sprintf(v->data, "%V=%08XD%08XD%08XD%08XD", &conf->name, uid[0], uid[1], uid[2], uid[3]); diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c index f08ec458d..ca963abe4 100644 --- a/src/http/ngx_http_core_module.c +++ b/src/http/ngx_http_core_module.c @@ -519,6 +519,7 @@ static void ngx_http_core_run_phases(ngx_http_request_t *r) { ngx_int_t rc; + ngx_str_t path; ngx_http_handler_pt *h; ngx_http_core_loc_conf_t *clcf; ngx_http_core_main_conf_t *cmcf; @@ -642,11 +643,10 @@ ngx_http_core_run_phases(ngx_http_request_t *r) if (r->uri.data[r->uri.len - 1] == '/' && !r->zero_in_uri) { - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "directory index of \"%V%V\" is forbidden", - &clcf->root, &r->uri); + if (ngx_http_map_uri_to_path(r, &path, 0) != NULL) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "directory index of \"%V\" is forbidden", &path); + } ngx_http_finalize_request(r, NGX_HTTP_FORBIDDEN); return; @@ -960,11 +960,14 @@ ngx_http_set_content_type(ngx_http_request_t *r) r->exten.data, r->exten.len); if (type) { + r->headers_out.content_type_len = type->len; r->headers_out.content_type = *type; + return NGX_OK; } } + r->headers_out.content_type_len = clcf->default_type.len; r->headers_out.content_type = clcf->default_type; return NGX_OK; @@ -1156,6 +1159,14 @@ ngx_http_subrequest(ngx_http_request_t *r, ngx_http_core_srv_conf_t *cscf; ngx_http_postponed_request_t *pr, *p; + r->main->subrequests--; + + if (r->main->subrequests == 0) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "subrequests cycle"); + return NGX_ERROR; + } + sr = ngx_pcalloc(r->pool, sizeof(ngx_http_request_t)); if (sr == NULL) { return NGX_ERROR; diff --git a/src/http/ngx_http_header_filter_module.c b/src/http/ngx_http_header_filter_module.c index 77a9224f5..36ed6e1f1 100644 --- a/src/http/ngx_http_header_filter_module.c +++ b/src/http/ngx_http_header_filter_module.c @@ -247,7 +247,9 @@ ngx_http_header_filter(ngx_http_request_t *r) len += sizeof("Content-Type: ") - 1 + r->headers_out.content_type.len + 2; - if (r->headers_out.charset.len) { + if (r->headers_out.content_type_len == r->headers_out.content_type.len + && r->headers_out.charset.len) + { len += sizeof("; charset=") - 1 + r->headers_out.charset.len; } } @@ -380,7 +382,9 @@ ngx_http_header_filter(ngx_http_request_t *r) b->last = ngx_copy(b->last, r->headers_out.content_type.data, r->headers_out.content_type.len); - if (r->headers_out.charset.len) { + if (r->headers_out.content_type_len == r->headers_out.content_type.len + && r->headers_out.charset.len) + { b->last = ngx_cpymem(b->last, "; charset=", sizeof("; charset=") - 1); b->last = ngx_copy(b->last, r->headers_out.charset.data, diff --git a/src/http/ngx_http_parse.c b/src/http/ngx_http_parse.c index 426aa8a0e..24b7f8c80 100644 --- a/src/http/ngx_http_parse.c +++ b/src/http/ngx_http_parse.c @@ -337,13 +337,13 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) r->quoted_uri = 1; state = sw_uri; break; - case '+': - r->plus_in_uri = 1; - break; case '?': r->args_start = p + 1; state = sw_uri; break; + case '+': + r->plus_in_uri = 1; + break; case '\0': r->zero_in_uri = 1; break; @@ -366,9 +366,6 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) r->uri_end = p; r->http_minor = 9; goto done; - case '+': - r->plus_in_uri = 1; - break; case '\0': r->zero_in_uri = 1; break; @@ -828,6 +825,8 @@ ngx_http_parse_complex_uri(ngx_http_request_t *r) r->uri_ext = u + 1; *u++ = ch; break; + case '+': + r->plus_in_uri = 1; default: *u++ = ch; break; @@ -853,6 +852,8 @@ ngx_http_parse_complex_uri(ngx_http_request_t *r) case '?': r->args_start = p; goto done; + case '+': + r->plus_in_uri = 1; default: state = sw_usual; *u++ = ch; @@ -881,6 +882,8 @@ ngx_http_parse_complex_uri(ngx_http_request_t *r) case '?': r->args_start = p; goto done; + case '+': + r->plus_in_uri = 1; default: state = sw_usual; *u++ = ch; @@ -917,6 +920,8 @@ ngx_http_parse_complex_uri(ngx_http_request_t *r) *u++ = ch; break; #endif + case '+': + r->plus_in_uri = 1; default: state = sw_usual; *u++ = ch; @@ -952,6 +957,8 @@ ngx_http_parse_complex_uri(ngx_http_request_t *r) case '?': r->args_start = p; goto done; + case '+': + r->plus_in_uri = 1; default: state = sw_usual; *u++ = ch; @@ -992,8 +999,6 @@ ngx_http_parse_complex_uri(ngx_http_request_t *r) if (ch == '\0') { r->zero_in_uri = 1; - *u++ = ch; - ch = *p++; } state = quoted_state; @@ -1003,10 +1008,15 @@ ngx_http_parse_complex_uri(ngx_http_request_t *r) c = (u_char) (ch | 0x20); if (c >= 'a' && c <= 'f') { ch = (u_char) ((decoded << 4) + c - 'a' + 10); + if (ch == '?') { *u++ = ch; ch = *p++; + + } else if (ch == '+') { + r->plus_in_uri = 1; } + state = quoted_state; break; } diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index c606ec81a..5b947ff76 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -667,6 +667,7 @@ ngx_http_process_request_line(ngx_event_t *rev) r->read_event_handler = ngx_http_block_read; r->uri_changes = NGX_HTTP_MAX_URI_CHANGES + 1; + r->subrequests = NGX_HTTP_MAX_SUBREQUESTS + 1; ngx_http_handler(r); diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h index a694ee553..0bec20446 100644 --- a/src/http/ngx_http_request.h +++ b/src/http/ngx_http_request.h @@ -9,6 +9,7 @@ #define NGX_HTTP_MAX_URI_CHANGES 10 +#define NGX_HTTP_MAX_SUBREQUESTS 50 /* must be 2^n */ #define NGX_HTTP_LC_HEADER_LEN 32 @@ -228,10 +229,13 @@ typedef struct { ngx_table_elt_t *expires; ngx_table_elt_t *etag; + ngx_str_t *override_charset; + + size_t content_type_len; ngx_str_t content_type; ngx_str_t charset; - ngx_array_t ranges; + ngx_array_t ranges; ngx_array_t cache_control; off_t content_length_n; @@ -446,6 +450,8 @@ struct ngx_http_request_s { unsigned stat_writing:1; #endif + unsigned subrequests:8; + /* used to parse HTTP headers */ ngx_uint_t state; u_char *uri_start; diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c index e53d32c1b..cf9d27b70 100644 --- a/src/http/ngx_http_upstream.c +++ b/src/http/ngx_http_upstream.c @@ -49,6 +49,8 @@ static ngx_int_t ngx_http_upstream_process_limit_rate(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); static ngx_int_t ngx_http_upstream_process_buffering(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); +static ngx_int_t ngx_http_upstream_process_charset(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset); static ngx_int_t ngx_http_upstream_copy_header_line(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); static ngx_int_t @@ -199,6 +201,10 @@ ngx_http_upstream_header_t ngx_http_upstream_headers_in[] = { ngx_http_upstream_process_buffering, 0, ngx_http_upstream_ignore_header_line, 0, 0 }, + { ngx_string("X-Accel-Charset"), + ngx_http_upstream_process_charset, 0, + ngx_http_upstream_ignore_header_line, 0, 0 }, + #if (NGX_HTTP_GZIP) { ngx_string("Content-Encoding"), ngx_http_upstream_process_header_line, @@ -1080,7 +1086,7 @@ ngx_http_upstream_process_header(ngx_event_t *rev) if (u->headers_in.status_n >= NGX_HTTP_BAD_REQUEST - && u->conf->redirect_errors + && u->conf->intercept_errors && r->err_ctx == NULL) { clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); @@ -1515,7 +1521,7 @@ ngx_http_upstream_process_non_buffered_body(ngx_event_t *ev) clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - do_write = ev->write; + do_write = ev->write || u->length == 0; for ( ;; ) { @@ -1559,6 +1565,7 @@ ngx_http_upstream_process_non_buffered_body(ngx_event_t *ev) } if (size && u->peer.connection->read->ready) { + n = u->peer.connection->recv(u->peer.connection, b->last, size); if (n == NGX_AGAIN) { @@ -2125,6 +2132,16 @@ ngx_http_upstream_process_buffering(ngx_http_request_t *r, ngx_table_elt_t *h, } +static ngx_int_t +ngx_http_upstream_process_charset(ngx_http_request_t *r, ngx_table_elt_t *h, + ngx_uint_t offset) +{ + r->headers_out.override_charset = &h->value; + + return NGX_OK; +} + + static ngx_int_t ngx_http_upstream_copy_header_line(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset) @@ -2184,8 +2201,33 @@ static ngx_int_t ngx_http_upstream_copy_content_type(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset) { + u_char *p, *last; + + r->headers_out.content_type_len = h->value.len; r->headers_out.content_type = h->value; + for (p = h->value.data; *p; p++) { + + if (*p != ';') { + continue; + } + + last = p; + + while (*++p == ' ') { /* void */ } + + if (ngx_strncasecmp(p, "charset=", 8) != 0) { + continue; + } + + p += 8; + + r->headers_out.content_type_len = last - h->value.data; + + r->headers_out.charset.len = h->value.data + h->value.len - p; + r->headers_out.charset.data = p; + } + return NGX_OK; } diff --git a/src/http/ngx_http_upstream.h b/src/http/ngx_http_upstream.h index f8668307a..53705fc24 100644 --- a/src/http/ngx_http_upstream.h +++ b/src/http/ngx_http_upstream.h @@ -90,7 +90,7 @@ typedef struct { ngx_flag_t pass_request_body; ngx_flag_t ignore_client_abort; - ngx_flag_t redirect_errors; + ngx_flag_t intercept_errors; ngx_flag_t cyclic_temp_file; ngx_path_t *temp_path; diff --git a/src/http/ngx_http_variables.c b/src/http/ngx_http_variables.c index 83f2adc0b..8aaee4595 100644 --- a/src/http/ngx_http_variables.c +++ b/src/http/ngx_http_variables.c @@ -354,6 +354,9 @@ ngx_http_get_indexed_variable(ngx_http_request_t *r, ngx_uint_t index) return &r->variables[index]; } + r->variables[index].valid = 0; + r->variables[index].not_found = 1; + return NULL; }