# HG changeset patch # User Igor Sysoev # Date 1226264400 -10800 # Node ID b4f69f2ef02c188a31125d0774191becd3c3a10d # Parent 5410f1e19796ccfcc08cf3d1c996537328a4fb42 nginx 0.7.20 *) Changes in the ngx_http_gzip_filter_module. *) Feature: the ngx_http_limit_req_module. *) Bugfix: worker processes might exit on a SIGBUS signal on sparc and ppc platforms; the bug had appeared in 0.7.3. Thanks to Maxim Dounin. *) Bugfix: the "proxy_pass http://host/some:uri" directives did not work; the bug had appeared in 0.7.12. *) Bugfix: in HTTPS mode requests might fail with the "bad write retry" error. *) Bugfix: the ngx_http_secure_link_module did not work inside locations, whose names are less than 3 characters. *) Bugfix: $server_addr variable might have no value. diff --git a/CHANGES b/CHANGES --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,26 @@ +Changes with nginx 0.7.20 10 Nov 2008 + + *) Changes in the ngx_http_gzip_filter_module. + + *) Feature: the ngx_http_limit_req_module. + + *) Bugfix: worker processes might exit on a SIGBUS signal on sparc and + ppc platforms; the bug had appeared in 0.7.3. + Thanks to Maxim Dounin. + + *) Bugfix: the "proxy_pass http://host/some:uri" directives did not + work; the bug had appeared in 0.7.12. + + *) Bugfix: in HTTPS mode requests might fail with the "bad write retry" + error. + + *) Bugfix: the ngx_http_secure_link_module did not work inside + locations, whose names are less than 3 characters. + + *) Bugfix: $server_addr variable might have no value. + + Changes with nginx 0.7.19 13 Oct 2008 *) Bugfix: version number update. diff --git a/CHANGES.ru b/CHANGES.ru --- a/CHANGES.ru +++ b/CHANGES.ru @@ -1,4 +1,26 @@ +Изменения в nginx 0.7.20 10.11.2008 + + *) Изменения в модуле ngx_http_gzip_filter_module. + + *) Добавление: модуль ngx_http_limit_req_module + + *) Исправление: на платформах sparc и ppc рабочие процессы могли + выходить по сигналу SIGBUS; ошибка появилась в 0.7.3. + Спасибо Максиму Дунину. + + *) Исправление: директивы вида "proxy_pass http://host/some:uri" не + работали; ошибка появилась в 0.7.12. + + *) Исправление: при использовании HTTPS запросы могли завершаться с + ошибкой "bad write retry". + + *) Исправление: модуль ngx_http_secure_link_module не работал внутри + location'ов с именами меньше 3 символов. + + *) Исправление: переменная $server_addr могла не иметь значения. + + Изменения в nginx 0.7.19 13.10.2008 *) Исправление: обновление номера версии. diff --git a/auto/modules b/auto/modules --- a/auto/modules +++ b/auto/modules @@ -203,6 +203,11 @@ if [ $HTTP_LIMIT_ZONE = YES ]; then HTTP_SRCS="$HTTP_SRCS $HTTP_LIMIT_ZONE_SRCS" fi +if [ $HTTP_LIMIT_REQ = YES ]; then + HTTP_MODULES="$HTTP_MODULES $HTTP_LIMIT_REQ_MODULE" + HTTP_SRCS="$HTTP_SRCS $HTTP_LIMIT_REQ_SRCS" +fi + if [ $HTTP_REALIP = YES ]; then have=NGX_HTTP_REALIP . auto/have HTTP_MODULES="$HTTP_MODULES $HTTP_REALIP_MODULE" diff --git a/auto/options b/auto/options --- a/auto/options +++ b/auto/options @@ -75,6 +75,7 @@ HTTP_FASTCGI=YES HTTP_PERL=NO HTTP_MEMCACHED=YES HTTP_LIMIT_ZONE=YES +HTTP_LIMIT_REQ=YES HTTP_EMPTY_GIF=YES HTTP_BROWSER=YES HTTP_SECURE_LINK=NO @@ -192,6 +193,7 @@ do --without-http_fastcgi_module) HTTP_FASTCGI=NO ;; --without-http_memcached_module) HTTP_MEMCACHED=NO ;; --without-http_limit_zone_module) HTTP_LIMIT_ZONE=NO ;; + --without-http_limit_req_module) HTTP_LIMIT_REQ=NO ;; --without-http_empty_gif_module) HTTP_EMPTY_GIF=NO ;; --without-http_browser_module) HTTP_BROWSER=NO ;; --without-http_upstream_ip_hash_module) HTTP_UPSTREAM_IP_HASH=NO ;; @@ -310,6 +312,7 @@ cat << END --without-http_fastcgi_module disable ngx_http_fastcgi_module --without-http_memcached_module disable ngx_http_memcached_module --without-http_limit_zone_module disable ngx_http_limit_zone_module + --without-http_limit_req_module disable ngx_http_limit_req_module --without-http_empty_gif_module disable ngx_http_empty_gif_module --without-http_browser_module disable ngx_http_browser_module --without-http_upstream_ip_hash_module diff --git a/auto/sources b/auto/sources --- a/auto/sources +++ b/auto/sources @@ -417,6 +417,10 @@ HTTP_LIMIT_ZONE_MODULE=ngx_http_limit_zo HTTP_LIMIT_ZONE_SRCS=src/http/modules/ngx_http_limit_zone_module.c +HTTP_LIMIT_REQ_MODULE=ngx_http_limit_req_module +HTTP_LIMIT_REQ_SRCS=src/http/modules/ngx_http_limit_req_module.c + + HTTP_EMPTY_GIF_MODULE=ngx_http_empty_gif_module HTTP_EMPTY_GIF_SRCS=src/http/modules/ngx_http_empty_gif_module.c diff --git a/conf/nginx.conf b/conf/nginx.conf --- a/conf/nginx.conf +++ b/conf/nginx.conf @@ -18,7 +18,7 @@ http { include mime.types; default_type application/octet-stream; - #log_format main '$remote_addr - $remote_user [$time_local] $request ' + #log_format main '$remote_addr - $remote_user [$time_local] "$request" ' # '"$status" $body_bytes_sent "$http_referer" ' # '"$http_user_agent" "$http_x_forwarded_for"'; diff --git a/src/core/nginx.h b/src/core/nginx.h --- a/src/core/nginx.h +++ b/src/core/nginx.h @@ -8,7 +8,7 @@ #define _NGINX_H_INCLUDED_ -#define NGINX_VERSION "0.7.19" +#define NGINX_VERSION "0.7.20" #define NGINX_VER "nginx/" NGINX_VERSION #define NGINX_VAR "NGINX" diff --git a/src/core/ngx_inet.c b/src/core/ngx_inet.c --- a/src/core/ngx_inet.c +++ b/src/core/ngx_inet.c @@ -251,7 +251,7 @@ ngx_parse_unix_domain_url(ngx_pool_t *po static ngx_int_t ngx_parse_inet_url(ngx_pool_t *pool, ngx_url_t *u) { - u_char *p, *host, *port, *last, *uri; + u_char *p, *host, *port, *last, *uri, *args; size_t len; ngx_int_t n; struct hostent *h; @@ -264,7 +264,18 @@ ngx_parse_inet_url(ngx_pool_t *pool, ngx port = ngx_strlchr(host, last, ':'); - uri = ngx_strlchr(port ? port : host, last, '/'); + uri = ngx_strlchr(host, last, '/'); + + args = ngx_strlchr(host, last, '?'); + + if (args) { + if (uri == NULL) { + uri = args; + + } else if (args < uri) { + uri = args; + } + } if (uri) { if (u->listen || !u->uri_part) { @@ -276,6 +287,10 @@ ngx_parse_inet_url(ngx_pool_t *pool, ngx u->uri.data = uri; last = uri; + + if (uri < port) { + port = NULL; + } } if (port) { diff --git a/src/core/ngx_open_file_cache.h b/src/core/ngx_open_file_cache.h --- a/src/core/ngx_open_file_cache.h +++ b/src/core/ngx_open_file_cache.h @@ -12,6 +12,9 @@ #define _NGX_OPEN_FILE_CACHE_H_INCLUDED_ +#define NGX_OPEN_FILE_DIRECTIO_OFF NGX_MAX_OFF_T_VALUE + + typedef struct { ngx_fd_t fd; ngx_file_uniq_t uniq; diff --git a/src/core/ngx_palloc.c b/src/core/ngx_palloc.c --- a/src/core/ngx_palloc.c +++ b/src/core/ngx_palloc.c @@ -171,6 +171,7 @@ ngx_palloc_block(ngx_pool_t *pool, size_ new->d.next = NULL; m += sizeof(ngx_pool_data_t); + m = ngx_align_ptr(m, NGX_ALIGNMENT); new->d.last = m + size; current = pool->current; diff --git a/src/core/ngx_resolver.c b/src/core/ngx_resolver.c --- a/src/core/ngx_resolver.c +++ b/src/core/ngx_resolver.c @@ -956,11 +956,14 @@ ngx_resolver_process_response(ngx_resolv { char *err; size_t len; - ngx_uint_t i, ident, flags, code, nqs, nan, qtype, qclass; + ngx_uint_t i, times, ident, qident, flags, code, nqs, nan, + qtype, qclass; + ngx_queue_t *q; ngx_resolver_qs_t *qs; + ngx_resolver_node_t *rn; ngx_resolver_query_t *query; - if ((size_t) n < sizeof(ngx_resolver_query_t) + 1) { + if ((size_t) n < sizeof(ngx_resolver_query_t)) { goto short_response; } @@ -985,11 +988,31 @@ ngx_resolver_process_response(ngx_resolv code = flags & 0x7f; - if (code == NGX_RESOLVE_FORMERR || code > NGX_RESOLVE_REFUSED) { - ngx_log_error(r->log_level, r->log, 0, - "DNS error (%ui: %s), query id:%ui", - code, ngx_resolver_strerror(code), ident); - return; + if (code == NGX_RESOLVE_FORMERR) { + + times = 0; + + for (q = ngx_queue_head(&r->name_resend_queue); + q != ngx_queue_sentinel(&r->name_resend_queue) || times++ < 100; + q = ngx_queue_next(q)) + { + rn = ngx_queue_data(q, ngx_resolver_node_t, queue); + qident = (rn->query[0] << 8) + rn->query[1]; + + if (qident == ident) { + ngx_log_error(r->log_level, r->log, 0, + "DNS error (%ui: %s), query id:%ui, name:\"%*s\"", + code, ngx_resolver_strerror(code), ident, + rn->nlen, rn->name); + return; + } + } + + goto dns_error; + } + + if (code > NGX_RESOLVE_REFUSED) { + goto dns_error; } if (nqs != 1) { @@ -1069,6 +1092,13 @@ done: ngx_log_error(r->log_level, r->log, 0, err); return; + +dns_error: + + ngx_log_error(r->log_level, r->log, 0, + "DNS error (%ui: %s), query id:%ui", + code, ngx_resolver_strerror(code), ident); + return; } diff --git a/src/core/ngx_string.c b/src/core/ngx_string.c --- a/src/core/ngx_string.c +++ b/src/core/ngx_string.c @@ -8,6 +8,10 @@ #include +static u_char *ngx_sprintf_num(u_char *buf, u_char *last, uint64_t ui64, + u_char zero, ngx_uint_t hexadecimal, ngx_uint_t width); + + void ngx_strlow(u_char *dst, u_char *src, size_t n) { @@ -67,6 +71,7 @@ ngx_pstrdup(ngx_pool_t *pool, ngx_str_t * %[0][width][u][x|X]D int32_t/uint32_t * %[0][width][u][x|X]L int64_t/uint64_t * %[0][width|m][u][x|X]A ngx_atomic_int_t/ngx_atomic_uint_t + * %[0][width][.width]f float * %P ngx_pid_t * %M ngx_msec_t * %r rlim_t @@ -118,22 +123,16 @@ ngx_snprintf(u_char *buf, size_t max, co u_char * ngx_vsnprintf(u_char *buf, size_t max, const char *fmt, va_list args) { - u_char *p, zero, *last, temp[NGX_INT64_LEN + 1]; - /* - * really we need temp[NGX_INT64_LEN] only, - * but icc issues the warning - */ + u_char *p, zero, *last; int d; + float f, scale; size_t len, slen; - uint32_t ui32; int64_t i64; uint64_t ui64; ngx_msec_t ms; - ngx_uint_t width, sign, hexadecimal, max_width; + ngx_uint_t width, sign, hex, max_width, frac_width, i; ngx_str_t *v; ngx_variable_value_t *vv; - static u_char hex[] = "0123456789abcdef"; - static u_char HEX[] = "0123456789ABCDEF"; if (max == 0) { return buf; @@ -156,12 +155,11 @@ ngx_vsnprintf(u_char *buf, size_t max, c zero = (u_char) ((*++fmt == '0') ? '0' : ' '); width = 0; sign = 1; - hexadecimal = 0; + hex = 0; max_width = 0; + frac_width = 0; slen = (size_t) -1; - p = temp + NGX_INT64_LEN; - while (*fmt >= '0' && *fmt <= '9') { width = width * 10 + *fmt++ - '0'; } @@ -181,17 +179,26 @@ ngx_vsnprintf(u_char *buf, size_t max, c continue; case 'X': - hexadecimal = 2; + hex = 2; sign = 0; fmt++; continue; case 'x': - hexadecimal = 1; + hex = 1; sign = 0; fmt++; continue; + case '.': + fmt++; + + while (*fmt >= '0' && *fmt <= '9') { + frac_width = frac_width * 10 + *fmt++ - '0'; + } + + break; + case '*': slen = va_arg(args, size_t); fmt++; @@ -339,6 +346,43 @@ ngx_vsnprintf(u_char *buf, size_t max, c break; + case 'f': + f = (float) va_arg(args, double); + + if (f < 0) { + *buf++ = '-'; + f = -f; + } + + ui64 = (int64_t) f; + + buf = ngx_sprintf_num(buf, last, ui64, zero, 0, width); + + if (frac_width) { + + if (buf < last) { + *buf++ = '.'; + } + + scale = 1.0; + + for (i = 0; i < frac_width; i++) { + scale *= 10.0; + } + + /* + * (int64_t) cast is required for msvc6: + * it can not convert uint64_t to double + */ + ui64 = (uint64_t) ((f - (int64_t) ui64) * scale); + + buf = ngx_sprintf_num(buf, last, ui64, '0', 0, frac_width); + } + + fmt++; + + continue; + #if !(NGX_WIN32) case 'r': i64 = (int64_t) va_arg(args, rlim_t); @@ -348,7 +392,7 @@ ngx_vsnprintf(u_char *buf, size_t max, c case 'p': ui64 = (uintptr_t) va_arg(args, void *); - hexadecimal = 2; + hex = 2; sign = 0; zero = '0'; width = NGX_PTR_SIZE * 2; @@ -398,63 +442,7 @@ ngx_vsnprintf(u_char *buf, size_t max, c } } - if (hexadecimal == 1) { - do { - - /* the "(uint32_t)" cast disables the BCC's warning */ - *--p = hex[(uint32_t) (ui64 & 0xf)]; - - } while (ui64 >>= 4); - - } else if (hexadecimal == 2) { - do { - - /* the "(uint32_t)" cast disables the BCC's warning */ - *--p = HEX[(uint32_t) (ui64 & 0xf)]; - - } while (ui64 >>= 4); - - } else if (ui64 <= NGX_MAX_UINT32_VALUE) { - - /* - * To divide 64-bit number and to find the remainder - * on the x86 platform gcc and icc call the libc functions - * [u]divdi3() and [u]moddi3(), they call another function - * in its turn. On FreeBSD it is the qdivrem() function, - * its source code is about 170 lines of the code. - * The glibc counterpart is about 150 lines of the code. - * - * For 32-bit numbers and some divisors gcc and icc use - * the inlined multiplication and shifts. For example, - * unsigned "i32 / 10" is compiled to - * - * (i32 * 0xCCCCCCCD) >> 35 - */ - - ui32 = (uint32_t) ui64; - - do { - *--p = (u_char) (ui32 % 10 + '0'); - } while (ui32 /= 10); - - } else { - do { - *--p = (u_char) (ui64 % 10 + '0'); - } while (ui64 /= 10); - } - - len = (temp + NGX_INT64_LEN) - p; - - while (len++ < width && buf < last) { - *buf++ = zero; - } - - len = (temp + NGX_INT64_LEN) - p; - if (buf + len > last) { - len = last - buf; - } - - buf = ngx_cpymem(buf, p, len); + buf = ngx_sprintf_num(buf, last, ui64, zero, hex, width); fmt++; @@ -467,6 +455,92 @@ ngx_vsnprintf(u_char *buf, size_t max, c } +static u_char * +ngx_sprintf_num(u_char *buf, u_char *last, uint64_t ui64, u_char zero, + ngx_uint_t hexadecimal, ngx_uint_t width) +{ + u_char *p, temp[NGX_INT64_LEN + 1]; + /* + * we need temp[NGX_INT64_LEN] only, + * but icc issues the warning + */ + size_t len; + uint32_t ui32; + static u_char hex[] = "0123456789abcdef"; + static u_char HEX[] = "0123456789ABCDEF"; + + p = temp + NGX_INT64_LEN; + + if (hexadecimal == 0) { + + if (ui64 <= NGX_MAX_UINT32_VALUE) { + + /* + * To divide 64-bit numbers and to find remainders + * on the x86 platform gcc and icc call the libc functions + * [u]divdi3() and [u]moddi3(), they call another function + * in its turn. On FreeBSD it is the qdivrem() function, + * its source code is about 170 lines of the code. + * The glibc counterpart is about 150 lines of the code. + * + * For 32-bit numbers and some divisors gcc and icc use + * a inlined multiplication and shifts. For example, + * unsigned "i32 / 10" is compiled to + * + * (i32 * 0xCCCCCCCD) >> 35 + */ + + ui32 = (uint32_t) ui64; + + do { + *--p = (u_char) (ui32 % 10 + '0'); + } while (ui32 /= 10); + + } else { + do { + *--p = (u_char) (ui64 % 10 + '0'); + } while (ui64 /= 10); + } + + } else if (hexadecimal == 1) { + + do { + + /* the "(uint32_t)" cast disables the BCC's warning */ + *--p = hex[(uint32_t) (ui64 & 0xf)]; + + } while (ui64 >>= 4); + + } else { /* hexadecimal == 2 */ + + do { + + /* the "(uint32_t)" cast disables the BCC's warning */ + *--p = HEX[(uint32_t) (ui64 & 0xf)]; + + } while (ui64 >>= 4); + } + + /* zero or space padding */ + + len = (temp + NGX_INT64_LEN) - p; + + while (len++ < width && buf < last) { + *buf++ = zero; + } + + /* number safe copy */ + + len = (temp + NGX_INT64_LEN) - p; + + if (buf + len > last) { + len = last - buf; + } + + return ngx_cpymem(buf, p, len); +} + + /* * We use ngx_strcasecmp()/ngx_strncasecmp() for 7-bit ASCII strings only, * and implement our own ngx_strcasecmp()/ngx_strncasecmp() diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -188,13 +188,6 @@ ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_ SSL_CTX_set_options(ssl->ctx, ngx_ssl_protocols[protocols >> 1]); } - /* - * we need this option because in ngx_ssl_send_chain() - * we may switch to a buffered write and may copy leftover part of - * previously unbuffered data to our internal buffer - */ - SSL_CTX_set_mode(ssl->ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); - SSL_CTX_set_read_ahead(ssl->ctx, 1); return NGX_OK; @@ -860,14 +853,7 @@ ngx_ssl_send_chain(ngx_connection_t *c, ssize_t send, size; ngx_buf_t *buf; - if (!c->ssl->buffer - || (in && in->next == NULL && !(c->buffered & NGX_SSL_BUFFERED))) - { - /* - * we avoid a buffer copy if - * we do not need to buffer the output - * or the incoming buf is a single and our buffer is empty - */ + if (!c->ssl->buffer) { while (in) { if (ngx_buf_special(in->buf)) { diff --git a/src/http/modules/ngx_http_gzip_filter_module.c b/src/http/modules/ngx_http_gzip_filter_module.c --- a/src/http/modules/ngx_http_gzip_filter_module.c +++ b/src/http/modules/ngx_http_gzip_filter_module.c @@ -47,6 +47,8 @@ typedef struct { unsigned flush:4; unsigned redo:1; unsigned done:1; + unsigned nomem:1; + unsigned gzheader:1; size_t zin; size_t zout; @@ -57,10 +59,39 @@ typedef struct { } ngx_http_gzip_ctx_t; +#if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED) + +struct gztrailer { + uint32_t crc32; + uint32_t zlen; +}; + +#else /* NGX_HAVE_BIG_ENDIAN || !NGX_HAVE_NONALIGNED */ + +struct gztrailer { + u_char crc32[4]; + u_char zlen[4]; +}; + +#endif + + +static ngx_int_t ngx_http_gzip_filter_deflate_start(ngx_http_request_t *r, + ngx_http_gzip_ctx_t *ctx); +static ngx_int_t ngx_http_gzip_filter_gzheader(ngx_http_request_t *r, + ngx_http_gzip_ctx_t *ctx); +static ngx_int_t ngx_http_gzip_filter_add_data(ngx_http_request_t *r, + ngx_http_gzip_ctx_t *ctx); +static ngx_int_t ngx_http_gzip_filter_get_buf(ngx_http_request_t *r, + ngx_http_gzip_ctx_t *ctx); +static ngx_int_t ngx_http_gzip_filter_deflate(ngx_http_request_t *r, + ngx_http_gzip_ctx_t *ctx); +static ngx_int_t ngx_http_gzip_filter_deflate_end(ngx_http_request_t *r, + ngx_http_gzip_ctx_t *ctx); + static void *ngx_http_gzip_filter_alloc(void *opaque, u_int items, u_int size); static void ngx_http_gzip_filter_free(void *opaque, void *address); -static void ngx_http_gzip_error(ngx_http_gzip_ctx_t *ctx); static ngx_int_t ngx_http_gzip_add_variables(ngx_conf_t *cf); static ngx_int_t ngx_http_gzip_ratio_variable(ngx_http_request_t *r, @@ -176,25 +207,6 @@ ngx_module_t ngx_http_gzip_filter_modul }; -static u_char gzheader[10] = { 0x1f, 0x8b, Z_DEFLATED, 0, 0, 0, 0, 0, 0, 3 }; - -#if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED) - -struct gztrailer { - uint32_t crc32; - uint32_t zlen; -}; - -#else /* NGX_HAVE_BIG_ENDIAN || !NGX_HAVE_NONALIGNED */ - -struct gztrailer { - u_char crc32[4]; - u_char zlen[4]; -}; - -#endif - - static ngx_str_t ngx_http_gzip_ratio = ngx_string("gzip_ratio"); static ngx_http_output_header_filter_pt ngx_http_next_header_filter; @@ -261,13 +273,9 @@ ngx_http_gzip_header_filter(ngx_http_req static ngx_int_t ngx_http_gzip_body_filter(ngx_http_request_t *r, ngx_chain_t *in) { - int rc, wbits, memlevel; - ngx_int_t last; - struct gztrailer *trailer; - ngx_buf_t *b; - ngx_chain_t *cl, out; - ngx_http_gzip_ctx_t *ctx; - ngx_http_gzip_conf_t *conf; + int rc; + ngx_chain_t *cl; + ngx_http_gzip_ctx_t *ctx; ctx = ngx_http_get_module_ctx(r, ngx_http_gzip_filter_module); @@ -275,392 +283,498 @@ ngx_http_gzip_body_filter(ngx_http_reque return ngx_http_next_body_filter(r, in); } - conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module); - if (ctx->preallocated == NULL) { - wbits = conf->wbits; - memlevel = conf->memlevel; - - if (ctx->length > 0) { - - /* the actual zlib window size is smaller by 262 bytes */ - - while (ctx->length < ((1 << (wbits - 1)) - 262)) { - wbits--; - memlevel--; - } - } - - /* - * We preallocate a memory for zlib in one buffer (200K-400K), this - * decreases a number of malloc() and free() calls and also probably - * decreases a number of syscalls (sbrk() and so on). - * Besides we free this memory as soon as the gzipping will complete - * and do not wait while a whole response will be sent to a client. - * - * 8K is for zlib deflate_state, it takes - * *) 5816 bytes on i386 and sparc64 (32-bit mode) - * *) 5920 bytes on amd64 and sparc64 - */ - - ctx->allocated = 8192 + (1 << (wbits + 2)) + (1 << (memlevel + 9)); - - ctx->preallocated = ngx_palloc(r->pool, ctx->allocated); - if (ctx->preallocated == NULL) { - return NGX_ERROR; + if (ngx_http_gzip_filter_deflate_start(r, ctx) != NGX_OK) { + goto failed; } - - ctx->free_mem = ctx->preallocated; - - ctx->zstream.zalloc = ngx_http_gzip_filter_alloc; - ctx->zstream.zfree = ngx_http_gzip_filter_free; - ctx->zstream.opaque = ctx; - - rc = deflateInit2(&ctx->zstream, (int) conf->level, Z_DEFLATED, - -wbits, memlevel, Z_DEFAULT_STRATEGY); - - if (rc != Z_OK) { - ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, - "deflateInit2() failed: %d", rc); - ngx_http_gzip_error(ctx); - return NGX_ERROR; - } - - b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); - if (b == NULL) { - ngx_http_gzip_error(ctx); - return NGX_ERROR; - } - - b->memory = 1; - b->pos = gzheader; - b->last = b->pos + 10; - - out.buf = b; - out.next = NULL; - - /* - * We pass the gzheader to the next filter now to avoid its linking - * to the ctx->busy chain. zlib does not usually output the compressed - * data in the initial iterations, so the gzheader that was linked - * to the ctx->busy chain would be flushed by ngx_http_write_filter(). - */ - - if (ngx_http_next_body_filter(r, &out) == NGX_ERROR) { - ngx_http_gzip_error(ctx); - return NGX_ERROR; - } - - r->connection->buffered |= NGX_HTTP_GZIP_BUFFERED; - - ctx->last_out = &ctx->out; - - ctx->crc32 = crc32(0L, Z_NULL, 0); - ctx->flush = Z_NO_FLUSH; } if (in) { if (ngx_chain_add_copy(r->pool, &ctx->in, in) == NGX_ERROR) { - ngx_http_gzip_error(ctx); - return NGX_ERROR; + goto failed; } } - last = NGX_NONE; + if (ctx->nomem) { + + /* flush busy buffers */ + + if (ngx_http_next_body_filter(r, NULL) == NGX_ERROR) { + goto failed; + } + + cl = NULL; + + ngx_chain_update_chains(&ctx->free, &ctx->busy, &cl, + (ngx_buf_tag_t) &ngx_http_gzip_filter_module); + ctx->nomem = 0; + } for ( ;; ) { + /* cycle while we can write to a client */ + for ( ;; ) { - /* does zlib need a new data ? */ - - if (ctx->zstream.avail_in == 0 - && ctx->flush == Z_NO_FLUSH - && !ctx->redo) - { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "gzip in: %p", ctx->in); + /* cycle while there is data to feed zlib and ... */ - if (ctx->in == NULL) { - break; - } - - ctx->in_buf = ctx->in->buf; - ctx->in = ctx->in->next; - - ctx->zstream.next_in = ctx->in_buf->pos; - ctx->zstream.avail_in = ctx->in_buf->last - ctx->in_buf->pos; - - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "gzip in_buf:%p ni:%p ai:%ud", - ctx->in_buf, - ctx->zstream.next_in, ctx->zstream.avail_in); + rc = ngx_http_gzip_filter_add_data(r, ctx); - /* STUB */ - if (ctx->in_buf->last < ctx->in_buf->pos) { - ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, - "zstream.avail_in is huge"); - ctx->done = 1; - return NGX_ERROR; - } - /**/ - - if (ctx->in_buf->last_buf) { - ctx->flush = Z_FINISH; + if (rc == NGX_DECLINED) { + break; + } - } else if (ctx->in_buf->flush) { - ctx->flush = Z_SYNC_FLUSH; - } - - if (ctx->zstream.avail_in == 0) { - if (ctx->flush == Z_NO_FLUSH) { - continue; - } - - } else { - ctx->crc32 = crc32(ctx->crc32, ctx->zstream.next_in, - ctx->zstream.avail_in); - } + if (rc == NGX_AGAIN) { + continue; } - /* is there a space for the gzipped data ? */ - - if (ctx->zstream.avail_out == 0) { - - if (ctx->free) { - ctx->out_buf = ctx->free->buf; - ctx->free = ctx->free->next; - - } else if (ctx->bufs < conf->bufs.num) { - ctx->out_buf = ngx_create_temp_buf(r->pool, - conf->bufs.size); - if (ctx->out_buf == NULL) { - ngx_http_gzip_error(ctx); - return NGX_ERROR; - } - - ctx->out_buf->tag = (ngx_buf_tag_t) - &ngx_http_gzip_filter_module; - ctx->out_buf->recycled = 1; - ctx->bufs++; + /* ... there are buffers to write zlib output */ - } else { - break; - } - - ctx->zstream.next_out = ctx->out_buf->pos; - ctx->zstream.avail_out = conf->bufs.size; - } + rc = ngx_http_gzip_filter_get_buf(r, ctx); - ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "deflate in: ni:%p no:%p ai:%ud ao:%ud fl:%d redo:%d", - ctx->zstream.next_in, ctx->zstream.next_out, - ctx->zstream.avail_in, ctx->zstream.avail_out, - ctx->flush, ctx->redo); - - rc = deflate(&ctx->zstream, ctx->flush); - - if (rc != Z_OK && rc != Z_STREAM_END) { - ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, - "deflate() failed: %d, %d", ctx->flush, rc); - ngx_http_gzip_error(ctx); - return NGX_ERROR; + if (rc == NGX_DECLINED) { + break; } - ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "deflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d", - ctx->zstream.next_in, ctx->zstream.next_out, - ctx->zstream.avail_in, ctx->zstream.avail_out, - rc); - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "gzip in_buf:%p pos:%p", - ctx->in_buf, ctx->in_buf->pos); - - - if (ctx->zstream.next_in) { - ctx->in_buf->pos = ctx->zstream.next_in; - - if (ctx->zstream.avail_in == 0) { - ctx->zstream.next_in = NULL; - } + if (rc == NGX_ERROR) { + goto failed; } - ctx->out_buf->last = ctx->zstream.next_out; - - if (ctx->zstream.avail_out == 0) { - - /* zlib wants to output some more gzipped data */ - - cl = ngx_alloc_chain_link(r->pool); - if (cl == NULL) { - ngx_http_gzip_error(ctx); - return NGX_ERROR; - } - - cl->buf = ctx->out_buf; - cl->next = NULL; - *ctx->last_out = cl; - ctx->last_out = &cl->next; - - ctx->redo = 1; - continue; - } - - ctx->redo = 0; - - if (ctx->flush == Z_SYNC_FLUSH) { - - ctx->zstream.avail_out = 0; - ctx->out_buf->flush = 1; - ctx->flush = Z_NO_FLUSH; + rc = ngx_http_gzip_filter_deflate(r, ctx); - cl = ngx_alloc_chain_link(r->pool); - if (cl == NULL) { - ngx_http_gzip_error(ctx); - return NGX_ERROR; - } - - cl->buf = ctx->out_buf; - cl->next = NULL; - *ctx->last_out = cl; - ctx->last_out = &cl->next; - + if (rc == NGX_OK) { break; } - if (rc == Z_STREAM_END) { - - ctx->zin = ctx->zstream.total_in; - ctx->zout = 10 + ctx->zstream.total_out + 8; - - rc = deflateEnd(&ctx->zstream); - - if (rc != Z_OK) { - ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, - "deflateEnd() failed: %d", rc); - ngx_http_gzip_error(ctx); - return NGX_ERROR; - } - - ngx_pfree(r->pool, ctx->preallocated); - - cl = ngx_alloc_chain_link(r->pool); - if (cl == NULL) { - ngx_http_gzip_error(ctx); - return NGX_ERROR; - } - - cl->buf = ctx->out_buf; - cl->next = NULL; - *ctx->last_out = cl; - ctx->last_out = &cl->next; - - if (ctx->zstream.avail_out >= 8) { - trailer = (struct gztrailer *) ctx->out_buf->last; - ctx->out_buf->last += 8; - ctx->out_buf->last_buf = 1; - - } else { - b = ngx_create_temp_buf(r->pool, 8); - if (b == NULL) { - ngx_http_gzip_error(ctx); - return NGX_ERROR; - } - - b->last_buf = 1; - - cl = ngx_alloc_chain_link(r->pool); - if (cl == NULL) { - ngx_http_gzip_error(ctx); - return NGX_ERROR; - } - - cl->buf = b; - cl->next = NULL; - *ctx->last_out = cl; - ctx->last_out = &cl->next; - trailer = (struct gztrailer *) b->pos; - b->last += 8; - } - -#if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED) - - trailer->crc32 = ctx->crc32; - trailer->zlen = ctx->zin; - -#else - trailer->crc32[0] = (u_char) (ctx->crc32 & 0xff); - trailer->crc32[1] = (u_char) ((ctx->crc32 >> 8) & 0xff); - trailer->crc32[2] = (u_char) ((ctx->crc32 >> 16) & 0xff); - trailer->crc32[3] = (u_char) ((ctx->crc32 >> 24) & 0xff); - - trailer->zlen[0] = (u_char) (ctx->zin & 0xff); - trailer->zlen[1] = (u_char) ((ctx->zin >> 8) & 0xff); - trailer->zlen[2] = (u_char) ((ctx->zin >> 16) & 0xff); - trailer->zlen[3] = (u_char) ((ctx->zin >> 24) & 0xff); -#endif - - ctx->zstream.avail_in = 0; - ctx->zstream.avail_out = 0; - - ctx->done = 1; - - r->connection->buffered &= ~NGX_HTTP_GZIP_BUFFERED; - - break; + if (rc == NGX_ERROR) { + goto failed; } - if (conf->no_buffer && ctx->in == NULL) { + /* rc == NGX_AGAIN */ + } - cl = ngx_alloc_chain_link(r->pool); - if (cl == NULL) { - ngx_http_gzip_error(ctx); - return NGX_ERROR; - } + if (ctx->out == NULL) { + return ctx->busy ? NGX_AGAIN : NGX_OK; + } - cl->buf = ctx->out_buf; - cl->next = NULL; - *ctx->last_out = cl; - ctx->last_out = &cl->next; - - break; + if (!ctx->gzheader) { + if (ngx_http_gzip_filter_gzheader(r, ctx) != NGX_OK) { + goto failed; } } - if (ctx->out == NULL) { - - if (last == NGX_AGAIN) { - return NGX_AGAIN; - } - - if (ctx->busy == NULL) { - return NGX_OK; - } - } + rc = ngx_http_next_body_filter(r, ctx->out); - last = ngx_http_next_body_filter(r, ctx->out); - - /* - * we do not check NGX_AGAIN here because the downstream filters - * may free some buffers and zlib may compress some data into them - */ - - if (last == NGX_ERROR) { - ngx_http_gzip_error(ctx); - return NGX_ERROR; + if (rc == NGX_ERROR) { + goto failed; } ngx_chain_update_chains(&ctx->free, &ctx->busy, &ctx->out, (ngx_buf_tag_t) &ngx_http_gzip_filter_module); ctx->last_out = &ctx->out; + ctx->nomem = 0; + if (ctx->done) { - return last; + return rc; + } + } + + /* unreachable */ + +failed: + + ctx->done = 1; + + if (ctx->preallocated) { + deflateEnd(&ctx->zstream); + + ngx_pfree(r->pool, ctx->preallocated); + } + + return NGX_ERROR; +} + + +static ngx_int_t +ngx_http_gzip_filter_deflate_start(ngx_http_request_t *r, + ngx_http_gzip_ctx_t *ctx) +{ + int rc, wbits, memlevel; + ngx_http_gzip_conf_t *conf; + + conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module); + + wbits = conf->wbits; + memlevel = conf->memlevel; + + if (ctx->length > 0) { + + /* the actual zlib window size is smaller by 262 bytes */ + + while (ctx->length < ((1 << (wbits - 1)) - 262)) { + wbits--; + memlevel--; } } + + /* + * We preallocate a memory for zlib in one buffer (200K-400K), this + * decreases a number of malloc() and free() calls and also probably + * decreases a number of syscalls (sbrk()/mmap() and so on). + * Besides we free the memory as soon as a gzipping will complete + * and do not wait while a whole response will be sent to a client. + * + * 8K is for zlib deflate_state, it takes + * *) 5816 bytes on i386 and sparc64 (32-bit mode) + * *) 5920 bytes on amd64 and sparc64 + */ + + ctx->allocated = 8192 + (1 << (wbits + 2)) + (1 << (memlevel + 9)); + + ctx->preallocated = ngx_palloc(r->pool, ctx->allocated); + if (ctx->preallocated == NULL) { + return NGX_ERROR; + } + + ctx->free_mem = ctx->preallocated; + + ctx->zstream.zalloc = ngx_http_gzip_filter_alloc; + ctx->zstream.zfree = ngx_http_gzip_filter_free; + ctx->zstream.opaque = ctx; + + rc = deflateInit2(&ctx->zstream, (int) conf->level, Z_DEFLATED, + -wbits, memlevel, Z_DEFAULT_STRATEGY); + + if (rc != Z_OK) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "deflateInit2() failed: %d", rc); + return NGX_ERROR; + } + + r->connection->buffered |= NGX_HTTP_GZIP_BUFFERED; + + ctx->last_out = &ctx->out; + ctx->crc32 = crc32(0L, Z_NULL, 0); + ctx->flush = Z_NO_FLUSH; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_gzip_filter_gzheader(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx) +{ + ngx_buf_t *b; + ngx_chain_t *cl; + static u_char gzheader[10] = + { 0x1f, 0x8b, Z_DEFLATED, 0, 0, 0, 0, 0, 0, 3 }; + + b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); + if (b == NULL) { + return NGX_ERROR; + } + + b->memory = 1; + b->pos = gzheader; + b->last = b->pos + 10; + + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { + return NGX_ERROR; + } + + cl->buf = b; + cl->next = ctx->out; + ctx->out = cl; + + ctx->gzheader = 1; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_gzip_filter_add_data(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx) +{ + if (ctx->zstream.avail_in || ctx->flush != Z_NO_FLUSH || ctx->redo) { + return NGX_OK; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "gzip in: %p", ctx->in); + + if (ctx->in == NULL) { + return NGX_DECLINED; + } + + ctx->in_buf = ctx->in->buf; + ctx->in = ctx->in->next; + + ctx->zstream.next_in = ctx->in_buf->pos; + ctx->zstream.avail_in = ctx->in_buf->last - ctx->in_buf->pos; + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "gzip in_buf:%p ni:%p ai:%ud", + ctx->in_buf, + ctx->zstream.next_in, ctx->zstream.avail_in); + + if (ctx->in_buf->last_buf) { + ctx->flush = Z_FINISH; + + } else if (ctx->in_buf->flush) { + ctx->flush = Z_SYNC_FLUSH; + } + + if (ctx->zstream.avail_in) { + + ctx->crc32 = crc32(ctx->crc32, ctx->zstream.next_in, + ctx->zstream.avail_in); + + } else if (ctx->flush == Z_NO_FLUSH) { + return NGX_AGAIN; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_gzip_filter_get_buf(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx) +{ + ngx_http_gzip_conf_t *conf; + + if (ctx->zstream.avail_out) { + return NGX_OK; + } + + conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module); + + if (ctx->free) { + ctx->out_buf = ctx->free->buf; + ctx->free = ctx->free->next; + + } else if (ctx->bufs < conf->bufs.num) { + + ctx->out_buf = ngx_create_temp_buf(r->pool, conf->bufs.size); + if (ctx->out_buf == NULL) { + return NGX_ERROR; + } + + ctx->out_buf->tag = (ngx_buf_tag_t) &ngx_http_gzip_filter_module; + ctx->out_buf->recycled = 1; + ctx->bufs++; + + } else { + ctx->nomem = 1; + return NGX_DECLINED; + } + + ctx->zstream.next_out = ctx->out_buf->pos; + ctx->zstream.avail_out = conf->bufs.size; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_gzip_filter_deflate(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx) +{ + int rc; + ngx_chain_t *cl; + ngx_http_gzip_conf_t *conf; + + ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "deflate in: ni:%p no:%p ai:%ud ao:%ud fl:%d redo:%d", + ctx->zstream.next_in, ctx->zstream.next_out, + ctx->zstream.avail_in, ctx->zstream.avail_out, + ctx->flush, ctx->redo); + + rc = deflate(&ctx->zstream, ctx->flush); + + if (rc != Z_OK && rc != Z_STREAM_END) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "deflate() failed: %d, %d", ctx->flush, rc); + return NGX_ERROR; + } + + ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "deflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d", + ctx->zstream.next_in, ctx->zstream.next_out, + ctx->zstream.avail_in, ctx->zstream.avail_out, + rc); + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "gzip in_buf:%p pos:%p", + ctx->in_buf, ctx->in_buf->pos); + + if (ctx->zstream.next_in) { + ctx->in_buf->pos = ctx->zstream.next_in; + + if (ctx->zstream.avail_in == 0) { + ctx->zstream.next_in = NULL; + } + } + + ctx->out_buf->last = ctx->zstream.next_out; + + if (ctx->zstream.avail_out == 0) { + + /* zlib wants to output some more gzipped data */ + + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { + return NGX_ERROR; + } + + cl->buf = ctx->out_buf; + cl->next = NULL; + *ctx->last_out = cl; + ctx->last_out = &cl->next; + + ctx->redo = 1; + + return NGX_AGAIN; + } + + ctx->redo = 0; + + if (ctx->flush == Z_SYNC_FLUSH) { + + ctx->zstream.avail_out = 0; + ctx->out_buf->flush = 1; + ctx->flush = Z_NO_FLUSH; + + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { + return NGX_ERROR; + } + + cl->buf = ctx->out_buf; + cl->next = NULL; + *ctx->last_out = cl; + ctx->last_out = &cl->next; + + return NGX_OK; + } + + if (rc == Z_STREAM_END) { + + if (ngx_http_gzip_filter_deflate_end(r, ctx) != NGX_OK) { + return NGX_ERROR; + } + + return NGX_OK; + } + + conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module); + + if (conf->no_buffer && ctx->in == NULL) { + + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { + return NGX_ERROR; + } + + cl->buf = ctx->out_buf; + cl->next = NULL; + *ctx->last_out = cl; + ctx->last_out = &cl->next; + + return NGX_OK; + } + + return NGX_AGAIN; +} + + +static ngx_int_t +ngx_http_gzip_filter_deflate_end(ngx_http_request_t *r, + ngx_http_gzip_ctx_t *ctx) +{ + int rc; + ngx_buf_t *b; + ngx_chain_t *cl; + struct gztrailer *trailer; + + ctx->zin = ctx->zstream.total_in; + ctx->zout = 10 + ctx->zstream.total_out + 8; + + rc = deflateEnd(&ctx->zstream); + + if (rc != Z_OK) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "deflateEnd() failed: %d", rc); + return NGX_ERROR; + } + + ngx_pfree(r->pool, ctx->preallocated); + + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { + return NGX_ERROR; + } + + cl->buf = ctx->out_buf; + cl->next = NULL; + *ctx->last_out = cl; + ctx->last_out = &cl->next; + + if (ctx->zstream.avail_out >= 8) { + trailer = (struct gztrailer *) ctx->out_buf->last; + ctx->out_buf->last += 8; + ctx->out_buf->last_buf = 1; + + } else { + b = ngx_create_temp_buf(r->pool, 8); + if (b == NULL) { + return NGX_ERROR; + } + + b->last_buf = 1; + + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { + return NGX_ERROR; + } + + cl->buf = b; + cl->next = NULL; + *ctx->last_out = cl; + ctx->last_out = &cl->next; + trailer = (struct gztrailer *) b->pos; + b->last += 8; + } + +#if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED) + + trailer->crc32 = ctx->crc32; + trailer->zlen = ctx->zin; + +#else + + trailer->crc32[0] = (u_char) (ctx->crc32 & 0xff); + trailer->crc32[1] = (u_char) ((ctx->crc32 >> 8) & 0xff); + trailer->crc32[2] = (u_char) ((ctx->crc32 >> 16) & 0xff); + trailer->crc32[3] = (u_char) ((ctx->crc32 >> 24) & 0xff); + + trailer->zlen[0] = (u_char) (ctx->zin & 0xff); + trailer->zlen[1] = (u_char) ((ctx->zin >> 8) & 0xff); + trailer->zlen[2] = (u_char) ((ctx->zin >> 16) & 0xff); + trailer->zlen[3] = (u_char) ((ctx->zin >> 24) & 0xff); + +#endif + + ctx->zstream.avail_in = 0; + ctx->zstream.avail_out = 0; + + ctx->done = 1; + + r->connection->buffered &= ~NGX_HTTP_GZIP_BUFFERED; + + return NGX_OK; } @@ -718,24 +832,6 @@ ngx_http_gzip_filter_free(void *opaque, } -static void -ngx_http_gzip_error(ngx_http_gzip_ctx_t *ctx) -{ - deflateEnd(&ctx->zstream); - - if (ctx->preallocated) { - ngx_pfree(ctx->request->pool, ctx->preallocated); - } - - ctx->zstream.avail_in = 0; - ctx->zstream.avail_out = 0; - - ctx->done = 1; - - return; -} - - static ngx_int_t ngx_http_gzip_add_variables(ngx_conf_t *cf) { @@ -871,9 +967,9 @@ ngx_http_gzip_filter_init(ngx_conf_t *cf static char * ngx_http_gzip_window(ngx_conf_t *cf, void *post, void *data) { - int *np = data; + size_t *np = data; - int wbits, wsize; + size_t wbits, wsize; wbits = 15; @@ -895,9 +991,9 @@ ngx_http_gzip_window(ngx_conf_t *cf, voi static char * ngx_http_gzip_hash(ngx_conf_t *cf, void *post, void *data) { - int *np = data; + size_t *np = data; - int memlevel, hsize; + size_t memlevel, hsize; memlevel = 9; diff --git a/src/http/modules/ngx_http_limit_req_module.c b/src/http/modules/ngx_http_limit_req_module.c new file mode 100644 --- /dev/null +++ b/src/http/modules/ngx_http_limit_req_module.c @@ -0,0 +1,794 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +typedef struct { + u_char color; + u_char dummy; + u_short len; + ngx_queue_t queue; + ngx_msec_t last; + float rate; + u_char data[1]; +} ngx_http_limit_req_node_t; + + +typedef struct { + ngx_rbtree_t *rbtree; + ngx_queue_t *queue; + ngx_slab_pool_t *shpool; + float rate; + ngx_int_t index; + ngx_str_t var; +} ngx_http_limit_req_ctx_t; + + +typedef struct { + ngx_shm_zone_t *shm_zone; + float burst; + ngx_msec_t delay; +} ngx_http_limit_req_conf_t; + + +static void ngx_http_limit_req_delay(ngx_http_request_t *r); +static ngx_int_t ngx_http_limit_req_lookup(ngx_http_limit_req_conf_t *lzcf, + ngx_uint_t hash, u_char *data, size_t len, ngx_http_limit_req_node_t **lzp); +static void ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx, + ngx_uint_t n); + +static void *ngx_http_limit_req_create_conf(ngx_conf_t *cf); +static char *ngx_http_limit_req_merge_conf(ngx_conf_t *cf, void *parent, + void *child); +static char *ngx_http_limit_req_zone(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_http_limit_req(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static ngx_int_t ngx_http_limit_req_init(ngx_conf_t *cf); + + +static ngx_command_t ngx_http_limit_req_commands[] = { + + { ngx_string("limit_req_zone"), + NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE3, + ngx_http_limit_req_zone, + 0, + 0, + NULL }, + + { ngx_string("limit_req"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123, + ngx_http_limit_req, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + + ngx_null_command +}; + + +static ngx_http_module_t ngx_http_limit_req_module_ctx = { + NULL, /* preconfiguration */ + ngx_http_limit_req_init, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL, /* merge server configuration */ + + ngx_http_limit_req_create_conf, /* create location configration */ + ngx_http_limit_req_merge_conf /* merge location configration */ +}; + + +ngx_module_t ngx_http_limit_req_module = { + NGX_MODULE_V1, + &ngx_http_limit_req_module_ctx, /* module context */ + ngx_http_limit_req_commands, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static ngx_int_t +ngx_http_limit_req_handler(ngx_http_request_t *r) +{ + float rate; + size_t len, n; + uint32_t hash; + ngx_int_t rc; + ngx_time_t *tp; + ngx_rbtree_node_t *node; + ngx_http_variable_value_t *vv; + ngx_http_limit_req_ctx_t *ctx; + ngx_http_limit_req_node_t *lz; + ngx_http_limit_req_conf_t *lzcf; + + if (r->main->limit_req_set) { + return NGX_DECLINED; + } + + lzcf = ngx_http_get_module_loc_conf(r, ngx_http_limit_req_module); + + if (lzcf->shm_zone == NULL) { + return NGX_DECLINED; + } + + ctx = lzcf->shm_zone->data; + + vv = ngx_http_get_indexed_variable(r, ctx->index); + + if (vv == NULL || vv->not_found) { + return NGX_DECLINED; + } + + len = vv->len; + + if (len == 0) { + return NGX_DECLINED; + } + + if (len > 65535) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "the value of the \"%V\" variable " + "is more than 65535 bytes: \"%v\"", + &ctx->var, vv); + return NGX_DECLINED; + } + + r->main->limit_req_set = 1; + + hash = ngx_crc32_short(vv->data, len); + + ngx_shmtx_lock(&ctx->shpool->mutex); + + ngx_http_limit_req_expire(ctx, 1); + + rc = ngx_http_limit_req_lookup(lzcf, hash, vv->data, len, &lz); + + if (lz) { + ngx_queue_remove(&lz->queue); + + ngx_queue_insert_head(ctx->queue, &lz->queue); + + rate = lz->rate; + + } else { + rate = 0.0; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "limit_req: %i %.3f", rc, rate); + + if (rc == NGX_BUSY) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "limiting requests, %.3f r/s", rate); + + return NGX_HTTP_SERVICE_UNAVAILABLE; + } + + if (rc == NGX_AGAIN) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + + if (lzcf->delay) { + ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, + "delaying requests, %.3f r/s", rate); + + if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + r->read_event_handler = ngx_http_test_reading; + r->write_event_handler = ngx_http_limit_req_delay; + ngx_add_timer(r->connection->write, lzcf->delay); + + return NGX_AGAIN; + } + + return NGX_DECLINED; + } + + if (rc == NGX_OK) { + goto done; + } + + /* rc == NGX_DECLINED */ + + n = offsetof(ngx_rbtree_node_t, color) + + offsetof(ngx_http_limit_req_node_t, data) + + len; + + node = ngx_slab_alloc_locked(ctx->shpool, n); + if (node == NULL) { + + ngx_http_limit_req_expire(ctx, 0); + + node = ngx_slab_alloc_locked(ctx->shpool, n); + if (node == NULL) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + return NGX_HTTP_SERVICE_UNAVAILABLE; + } + } + + lz = (ngx_http_limit_req_node_t *) &node->color; + + node->key = hash; + lz->len = (u_char) len; + + tp = ngx_timeofday(); + lz->last = (ngx_msec_t) (tp->sec * 1000 + tp->msec); + + lz->rate = 0.0; + ngx_memcpy(lz->data, vv->data, len); + + ngx_rbtree_insert(ctx->rbtree, node); + + ngx_queue_insert_head(ctx->queue, &lz->queue); + +done: + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + return NGX_DECLINED; +} + + +static void +ngx_http_limit_req_delay(ngx_http_request_t *r) +{ + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "limit_req delay"); + + if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) { + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + r->read_event_handler = ngx_http_block_reading; + r->write_event_handler = ngx_http_core_run_phases; + + ngx_http_core_run_phases(r); +} + + +static void +ngx_http_limit_req_rbtree_insert_value(ngx_rbtree_node_t *temp, + ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) +{ + ngx_rbtree_node_t **p; + ngx_http_limit_req_node_t *lzn, *lznt; + + for ( ;; ) { + + if (node->key < temp->key) { + + p = &temp->left; + + } else if (node->key > temp->key) { + + p = &temp->right; + + } else { /* node->key == temp->key */ + + lzn = (ngx_http_limit_req_node_t *) &node->color; + lznt = (ngx_http_limit_req_node_t *) &temp->color; + + p = (ngx_memn2cmp(lzn->data, lznt->data, lzn->len, lznt->len) < 0) + ? &temp->left : &temp->right; + } + + if (*p == sentinel) { + break; + } + + temp = *p; + } + + *p = node; + node->parent = temp; + node->left = sentinel; + node->right = sentinel; + ngx_rbt_red(node); +} + + +static ngx_int_t +ngx_http_limit_req_lookup(ngx_http_limit_req_conf_t *lzcf, ngx_uint_t hash, + u_char *data, size_t len, ngx_http_limit_req_node_t **lzp) +{ + ngx_int_t rc; + ngx_time_t *tp; + ngx_msec_t now; + ngx_msec_int_t ms; + ngx_rbtree_node_t *node, *sentinel; + ngx_http_limit_req_ctx_t *ctx; + ngx_http_limit_req_node_t *lz; + + ctx = lzcf->shm_zone->data; + + node = ctx->rbtree->root; + sentinel = ctx->rbtree->sentinel; + + while (node != sentinel) { + + if (hash < node->key) { + node = node->left; + continue; + } + + if (hash > node->key) { + node = node->right; + continue; + } + + /* hash == node->key */ + + do { + lz = (ngx_http_limit_req_node_t *) &node->color; + + rc = ngx_memn2cmp(data, lz->data, len, (size_t) lz->len); + + if (rc == 0) { + + tp = ngx_timeofday(); + + now = (ngx_msec_t) (tp->sec * 1000 + tp->msec); + ms = (ngx_msec_int_t) (now - lz->last); + + lz->rate = lz->rate - ctx->rate * ngx_abs(ms) / 1000 + 1; + + if (lz->rate < 0.0) { + lz->rate = 0.0; + } + + lz->last = now; + + *lzp = lz; + + if (lz->rate > lzcf->burst) { + return NGX_BUSY; + } + + if (lz->rate > ctx->rate) { + return NGX_AGAIN; + } + + return NGX_OK; + } + + node = (rc < 0) ? node->left : node->right; + + } while (node != sentinel && hash == node->key); + + break; + } + + *lzp = NULL; + + return NGX_DECLINED; +} + + +static void +ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx, ngx_uint_t n) +{ + float rate; + ngx_time_t *tp; + ngx_msec_t now; + ngx_queue_t *q; + ngx_msec_int_t ms; + ngx_rbtree_node_t *node; + ngx_http_limit_req_node_t *lz; + + tp = ngx_timeofday(); + + now = (ngx_msec_t) (tp->sec * 1000 + tp->msec); + + /* + * n == 1 deletes one or two zero rate entries + * n == 0 deletes oldest entry by force + * and one or two zero rate entries + */ + + while (n < 3) { + + if (ngx_queue_empty(ctx->queue)) { + return; + } + + q = ngx_queue_last(ctx->queue); + + lz = ngx_queue_data(q, ngx_http_limit_req_node_t, queue); + + if (n++ != 0) { + + ms = (ngx_msec_int_t) (now - lz->last); + ms = ngx_abs(ms); + + if (ms < 60000) { + return; + } + + rate = lz->rate - ctx->rate * ms / 1000; + + if (rate > 0.0) { + return; + } + } + + ngx_queue_remove(q); + + node = (ngx_rbtree_node_t *) + ((u_char *) lz - offsetof(ngx_rbtree_node_t, color)); + + ngx_rbtree_delete(ctx->rbtree, node); + + ngx_slab_free_locked(ctx->shpool, node); + } +} + + +static ngx_int_t +ngx_http_limit_req_init_zone(ngx_shm_zone_t *shm_zone, void *data) +{ + ngx_http_limit_req_ctx_t *octx = data; + + ngx_rbtree_node_t *sentinel; + ngx_http_limit_req_ctx_t *ctx; + + ctx = shm_zone->data; + + if (octx) { + if (ngx_strcmp(ctx->var.data, octx->var.data) != 0) { + ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0, + "limit_req \"%V\" uses the \"%V\" variable " + "while previously it used the \"%V\" variable", + &shm_zone->name, &ctx->var, &octx->var); + return NGX_ERROR; + } + + ctx->rbtree = octx->rbtree; + ctx->queue = octx->queue; + ctx->shpool = octx->shpool; + + return NGX_OK; + } + + ctx->shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; + + ctx->rbtree = ngx_slab_alloc(ctx->shpool, sizeof(ngx_rbtree_t)); + if (ctx->rbtree == NULL) { + return NGX_ERROR; + } + + sentinel = ngx_slab_alloc(ctx->shpool, sizeof(ngx_rbtree_node_t)); + if (sentinel == NULL) { + return NGX_ERROR; + } + + ngx_rbtree_init(ctx->rbtree, sentinel, + ngx_http_limit_req_rbtree_insert_value); + + ctx->queue = ngx_slab_alloc(ctx->shpool, sizeof(ngx_queue_t)); + if (ctx->queue == NULL) { + return NGX_ERROR; + } + + ngx_queue_init(ctx->queue); + + return NGX_OK; +} + + +static void * +ngx_http_limit_req_create_conf(ngx_conf_t *cf) +{ + ngx_http_limit_req_conf_t *conf; + + conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_req_conf_t)); + if (conf == NULL) { + return NGX_CONF_ERROR; + } + + /* + * set by ngx_pcalloc(): + * + * conf->shm_zone = NULL; + * conf->burst = 0.0; + * conf->delay = 0; + */ + + return conf; +} + + +static char * +ngx_http_limit_req_merge_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_http_limit_req_conf_t *prev = parent; + ngx_http_limit_req_conf_t *conf = child; + + if (conf->shm_zone == NULL) { + *conf = *prev; + } + + return NGX_CONF_OK; +} + + +static char * +ngx_http_limit_req_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + u_char *p; + size_t size, len; + ngx_str_t *value, name, s; + ngx_int_t rate, scale; + ngx_uint_t i; + ngx_shm_zone_t *shm_zone; + ngx_http_limit_req_ctx_t *ctx; + + value = cf->args->elts; + + ctx = NULL; + size = 0; + rate = 1; + scale = 1; + name.len = 0; + + for (i = 1; i < cf->args->nelts; i++) { + + if (ngx_strncmp(value[i].data, "zone=", 5) == 0) { + + name.data = value[i].data + 5; + + p = (u_char *) ngx_strchr(name.data, ':'); + + if (p) { + name.len = p - name.data; + + p++; + + s.len = value[i].data + value[i].len - p; + s.data = p; + + size = ngx_parse_size(&s); + if (size > 8191) { + continue; + } + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid zone size \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + if (ngx_strncmp(value[i].data, "rate=", 5) == 0) { + + len = value[i].len; + p = value[i].data + len - 3; + + if (ngx_strncmp(p, "r/s", 3) == 0) { + scale = 1; + len -= 3; + + } else if (ngx_strncmp(p, "r/m", 3) == 0) { + scale = 60; + len -= 3; + } + + rate = ngx_atoi(value[i].data + 5, len - 5); + if (rate <= NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid rate \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + continue; + } + + if (value[i].data[0] == '$') { + + value[i].len--; + value[i].data++; + + ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_req_ctx_t)); + if (ctx == NULL) { + return NGX_CONF_ERROR; + } + + ctx->index = ngx_http_get_variable_index(cf, &value[i]); + if (ctx->index == NGX_ERROR) { + return NGX_CONF_ERROR; + } + + ctx->var = value[i]; + + continue; + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + if (name.len == 0 || size == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"%V\" must have \"zone\" parameter", + &cmd->name); + return NGX_CONF_ERROR; + } + + if (ctx == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "no variable is defined for limit_req_zone \"%V\"", + &cmd->name); + return NGX_CONF_ERROR; + } + + ctx->rate = (float) rate / scale; + + shm_zone = ngx_shared_memory_add(cf, &name, size, + &ngx_http_limit_req_module); + if (shm_zone == NULL) { + return NGX_CONF_ERROR; + } + + if (shm_zone->data) { + ctx = shm_zone->data; + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "limit_req_zone \"%V\" is already bound to variable \"%V\"", + &value[1], &ctx->var); + return NGX_CONF_ERROR; + } + + shm_zone->init = ngx_http_limit_req_init_zone; + shm_zone->data = ctx; + + return NGX_CONF_OK; +} + + +static char * +ngx_http_limit_req(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_limit_req_conf_t *lzcf = conf; + + u_char *p; + size_t len; + ngx_int_t burst, scale, delay; + ngx_str_t *value, s; + ngx_uint_t i; + ngx_http_limit_req_ctx_t *ctx; + + if (lzcf->shm_zone) { + return "is duplicate"; + } + + value = cf->args->elts; + + burst = 0; + scale = 1; + delay = 0; + + for (i = 1; i < cf->args->nelts; i++) { + + if (ngx_strncmp(value[i].data, "zone=", 5) == 0) { + + s.len = value[i].len - 5; + s.data = value[i].data + 5; + + lzcf->shm_zone = ngx_shared_memory_add(cf, &s, 0, + &ngx_http_limit_req_module); + if (lzcf->shm_zone == NULL) { + return NGX_CONF_ERROR; + } + + continue; + } + + if (ngx_strncmp(value[i].data, "burst=", 6) == 0) { + + len = value[i].len; + p = value[i].data + len - 3; + + if (ngx_strncmp(p, "r/s", 3) == 0) { + scale = 1; + len -= 3; + + } else if (ngx_strncmp(p, "r/m", 3) == 0) { + scale = 60; + len -= 3; + } + + burst = ngx_atoi(value[i].data + 6, len - 6); + if (burst <= 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid burst rate \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + continue; + } + + if (ngx_strncmp(value[i].data, "delay=", 6) == 0) { + + s.len = value[i].len - 6; + s.data = value[i].data + 6; + + delay = ngx_parse_time(&s, 0); + if (delay < 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid clean_time value \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + continue; + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + if (lzcf->shm_zone == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"%V\" must have \"zone\" parameter", + &cmd->name); + return NGX_CONF_ERROR; + } + + if (lzcf->shm_zone->data == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "unknown limit_req_zone \"%V\"", + &lzcf->shm_zone->name); + return NGX_CONF_ERROR; + } + + if (burst) { + lzcf->burst = (float) burst / scale; + + } else { + ctx = lzcf->shm_zone->data; + lzcf->burst = ctx->rate; + } + + lzcf->delay = (ngx_msec_t) delay; + + return NGX_CONF_OK; +} + + +static ngx_int_t +ngx_http_limit_req_init(ngx_conf_t *cf) +{ + ngx_http_handler_pt *h; + ngx_http_core_main_conf_t *cmcf; + + cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); + + h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers); + if (h == NULL) { + return NGX_ERROR; + } + + *h = ngx_http_limit_req_handler; + + return NGX_OK; +} 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 @@ -433,7 +433,7 @@ ngx_http_log_script_write(ngx_http_reque of.log = 1; of.valid = llcf->open_file_cache_valid; of.min_uses = llcf->open_file_cache_min_uses; - of.directio = NGX_MAX_OFF_T_VALUE; + of.directio = NGX_OPEN_FILE_DIRECTIO_OFF; if (ngx_open_cached_file(llcf->open_file_cache, &log, &of, r->pool) != NGX_OK) 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 @@ -536,10 +536,11 @@ static ngx_int_t ngx_http_proxy_eval(ngx_http_request_t *r, ngx_http_proxy_ctx_t *ctx, ngx_http_proxy_loc_conf_t *plcf) { - size_t add; - u_short port; - ngx_str_t proxy; - ngx_url_t u; + u_char *p; + size_t add; + u_short port; + ngx_str_t proxy; + ngx_url_t u; if (ngx_http_script_run(r, &proxy, plcf->proxy_lengths->elts, 0, plcf->proxy_values->elts) @@ -589,6 +590,19 @@ ngx_http_proxy_eval(ngx_http_request_t * return NGX_ERROR; } + if (u.uri.len && u.uri.data[0] == '?') { + p = ngx_pnalloc(r->pool, u.uri.len + 1); + if (p == NULL) { + return NGX_ERROR; + } + + *p++ = '/'; + ngx_memcpy(p, u.uri.data, u.uri.len); + + u.uri.len++; + u.uri.data = p - 1; + } + if (ngx_http_proxy_set_vars(r->pool, &u, &ctx->vars) != NGX_OK) { return NGX_ERROR; } @@ -602,7 +616,7 @@ ngx_http_proxy_eval(ngx_http_request_t * r->upstream->resolved->host = u.host; r->upstream->resolved->port = (in_port_t) (u.no_port ? u.default_port: u.port); - r->upstream->resolved->default_port = u.default_port; + r->upstream->resolved->no_port = u.no_port; return NGX_OK; } diff --git a/src/http/modules/ngx_http_secure_link_module.c b/src/http/modules/ngx_http_secure_link_module.c --- a/src/http/modules/ngx_http_secure_link_module.c +++ b/src/http/modules/ngx_http_secure_link_module.c @@ -113,7 +113,7 @@ url_start: len = last - p; - if (end - start != 32 || len < 3) { + if (end - start != 32 || len == 0) { goto not_found; } 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 @@ -47,7 +47,7 @@ our @EXPORT = qw( HTTP_INSUFFICIENT_STORAGE ); -our $VERSION = '0.7.19'; +our $VERSION = '0.7.20'; require XSLoader; XSLoader::load('nginx', $VERSION); diff --git a/src/http/ngx_http.h b/src/http/ngx_http.h --- a/src/http/ngx_http.h +++ b/src/http/ngx_http.h @@ -106,6 +106,7 @@ size_t ngx_http_get_time(char *buf, time ngx_int_t ngx_http_discard_request_body(ngx_http_request_t *r); void ngx_http_block_reading(ngx_http_request_t *r); +void ngx_http_test_reading(ngx_http_request_t *r); char *ngx_http_types_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); 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 @@ -1631,6 +1631,10 @@ ngx_http_server_addr(ngx_http_request_t } r->in_addr = sin.sin_addr.s_addr; + + } else { + sin.sin_family = c->sockaddr->sa_family; + sin.sin_addr.s_addr = r->in_addr; } if (s == NULL) { @@ -3470,7 +3474,7 @@ ngx_http_core_directio(ngx_conf_t *cf, n value = cf->args->elts; if (ngx_strcmp(value[1].data, "off") == 0) { - clcf->directio = NGX_MAX_OFF_T_VALUE; + clcf->directio = NGX_OPEN_FILE_DIRECTIO_OFF; return NGX_CONF_OK; } diff --git a/src/http/ngx_http_postpone_filter_module.c b/src/http/ngx_http_postpone_filter_module.c --- a/src/http/ngx_http_postpone_filter_module.c +++ b/src/http/ngx_http_postpone_filter_module.c @@ -216,7 +216,7 @@ ngx_http_postpone_filter_output_postpone r->postponed = r->postponed->next; } - if (r->out) { + if (r != r->main && r->out) { ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http postpone filter out again \"%V?%V\"", &r->uri, &r->args); diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -39,7 +39,6 @@ static void ngx_http_request_handler(ngx static ngx_int_t ngx_http_set_write_handler(ngx_http_request_t *r); static void ngx_http_writer(ngx_http_request_t *r); -static void ngx_http_test_reading(ngx_http_request_t *r); static void ngx_http_set_keepalive(ngx_http_request_t *r); static void ngx_http_keepalive_handler(ngx_event_t *ev); static void ngx_http_set_lingering_close(ngx_http_request_t *r); @@ -2024,7 +2023,7 @@ ngx_http_block_reading(ngx_http_request_ } -static void +void ngx_http_test_reading(ngx_http_request_t *r) { int n; diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h --- a/src/http/ngx_http_request.h +++ b/src/http/ngx_http_request.h @@ -438,10 +438,12 @@ struct ngx_http_request_s { unsigned no_cache:1; /* - * instead of using the request context data in ngx_http_limit_zone_module - * we use the single bit in the request structure + * instead of using the request context data in + * ngx_http_limit_zone_module and ngx_http_limit_req_module + * we use the single bits in the request structure */ unsigned limit_zone_set:1; + unsigned limit_req_set:1; #if 0 unsigned cacheable:1; 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 @@ -405,7 +405,7 @@ ngx_http_upstream_init(ngx_http_request_ uscf = uscfp[i]; if (uscf->host.len == host->len - && ((uscf->port == 0 && u->resolved->default_port) + && ((uscf->port == 0 && u->resolved->no_port) || uscf->port == u->resolved->port) && ngx_memcmp(uscf->host.data, host->data, host->len) == 0) { diff --git a/src/http/ngx_http_upstream.h b/src/http/ngx_http_upstream.h --- a/src/http/ngx_http_upstream.h +++ b/src/http/ngx_http_upstream.h @@ -209,7 +209,7 @@ typedef struct { typedef struct { ngx_str_t host; in_port_t port; - ngx_uint_t default_port; /* unsigned default_port:1 */ + ngx_uint_t no_port; /* unsigned no_port:1 */ ngx_uint_t naddrs; in_addr_t *addrs; ngx_resolver_ctx_t *ctx;