summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES22
-rw-r--r--CHANGES.ru22
-rw-r--r--auto/modules5
-rw-r--r--auto/options3
-rw-r--r--auto/sources4
-rw-r--r--conf/nginx.conf2
-rw-r--r--src/core/nginx.h2
-rw-r--r--src/core/ngx_inet.c19
-rw-r--r--src/core/ngx_open_file_cache.h3
-rw-r--r--src/core/ngx_palloc.c1
-rw-r--r--src/core/ngx_resolver.c44
-rw-r--r--src/core/ngx_string.c200
-rw-r--r--src/event/ngx_event_openssl.c16
-rw-r--r--src/http/modules/ngx_http_gzip_filter_module.c794
-rw-r--r--src/http/modules/ngx_http_limit_req_module.c794
-rw-r--r--src/http/modules/ngx_http_log_module.c2
-rw-r--r--src/http/modules/ngx_http_proxy_module.c24
-rw-r--r--src/http/modules/ngx_http_secure_link_module.c2
-rw-r--r--src/http/modules/perl/nginx.pm2
-rw-r--r--src/http/ngx_http.h1
-rw-r--r--src/http/ngx_http_core_module.c6
-rw-r--r--src/http/ngx_http_postpone_filter_module.c2
-rw-r--r--src/http/ngx_http_request.c3
-rw-r--r--src/http/ngx_http_request.h6
-rw-r--r--src/http/ngx_http_upstream.c2
-rw-r--r--src/http/ngx_http_upstream.h2
26 files changed, 1529 insertions, 454 deletions
diff --git a/CHANGES b/CHANGES
index 3b93d47bd..525cf442c 100644
--- 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
index eb3d1277d..ea00b3651 100644
--- 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
index 70b2e6055..456d6e7a5 100644
--- 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
index aba80c4ec..9e6e1a215 100644
--- 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
index 68ec98fd3..1c063d12c 100644
--- a/auto/sources
+++ b/auto/sources
@@ -417,6 +417,10 @@ HTTP_LIMIT_ZONE_MODULE=ngx_http_limit_zone_module
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
index 315245d14..7fac1e6b2 100644
--- 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
index 319b39d36..c40968628 100644
--- 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
index 6568cecb7..35638e53b 100644
--- a/src/core/ngx_inet.c
+++ b/src/core/ngx_inet.c
@@ -251,7 +251,7 @@ ngx_parse_unix_domain_url(ngx_pool_t *pool, ngx_url_t *u)
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_url_t *u)
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_url_t *u)
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
index af39094a3..a8d221b55 100644
--- 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
index 0cadd4aed..ce826bab6 100644
--- a/src/core/ngx_palloc.c
+++ b/src/core/ngx_palloc.c
@@ -171,6 +171,7 @@ ngx_palloc_block(ngx_pool_t *pool, size_t 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
index a7fe056e6..f4bbe19d4 100644
--- a/src/core/ngx_resolver.c
+++ b/src/core/ngx_resolver.c
@@ -956,11 +956,14 @@ ngx_resolver_process_response(ngx_resolver_t *r, u_char *buf, size_t n)
{
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_resolver_t *r, u_char *buf, size_t n)
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
index 203cd3e37..0ad594f82 100644
--- a/src/core/ngx_string.c
+++ b/src/core/ngx_string.c
@@ -8,6 +8,10 @@
#include <ngx_core.h>
+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 *src)
* %[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, const char *fmt, ...)
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, const char *fmt, va_list args)
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, const char *fmt, va_list args)
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, const char *fmt, va_list args)
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, const char *fmt, va_list args)
case 'p':
ui64 = (uintptr_t) va_arg(args, void *);
- hexadecimal = 2;
+ hex = 2;
sign = 0;
zero = '0';
width = NGX_PTR_SIZE * 2;
@@ -398,72 +442,102 @@ ngx_vsnprintf(u_char *buf, size_t max, const char *fmt, va_list args)
}
}
- if (hexadecimal == 1) {
- do {
+ buf = ngx_sprintf_num(buf, last, ui64, zero, hex, width);
+
+ fmt++;
+
+ } else {
+ *buf++ = *fmt++;
+ }
+ }
- /* the "(uint32_t)" cast disables the BCC's warning */
- *--p = hex[(uint32_t) (ui64 & 0xf)];
+ return buf;
+}
- } while (ui64 >>= 4);
- } else if (hexadecimal == 2) {
- do {
+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";
- /* the "(uint32_t)" cast disables the BCC's warning */
- *--p = HEX[(uint32_t) (ui64 & 0xf)];
+ p = temp + NGX_INT64_LEN;
- } while (ui64 >>= 4);
+ if (hexadecimal == 0) {
- } else if (ui64 <= NGX_MAX_UINT32_VALUE) {
+ 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
- */
+ /*
+ * 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;
+ ui32 = (uint32_t) ui64;
- do {
- *--p = (u_char) (ui32 % 10 + '0');
- } while (ui32 /= 10);
+ do {
+ *--p = (u_char) (ui32 % 10 + '0');
+ } while (ui32 /= 10);
- } else {
- do {
- *--p = (u_char) (ui64 % 10 + '0');
- } while (ui64 /= 10);
- }
+ } else {
+ do {
+ *--p = (u_char) (ui64 % 10 + '0');
+ } while (ui64 /= 10);
+ }
- len = (temp + NGX_INT64_LEN) - p;
+ } else if (hexadecimal == 1) {
- while (len++ < width && buf < last) {
- *buf++ = zero;
- }
+ do {
- len = (temp + NGX_INT64_LEN) - p;
- if (buf + len > last) {
- len = last - buf;
- }
+ /* the "(uint32_t)" cast disables the BCC's warning */
+ *--p = hex[(uint32_t) (ui64 & 0xf)];
- buf = ngx_cpymem(buf, p, len);
+ } while (ui64 >>= 4);
- fmt++;
+ } else { /* hexadecimal == 2 */
- } else {
- *buf++ = *fmt++;
- }
+ do {
+
+ /* the "(uint32_t)" cast disables the BCC's warning */
+ *--p = HEX[(uint32_t) (ui64 & 0xf)];
+
+ } while (ui64 >>= 4);
}
- return buf;
+ /* 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);
}
diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c
index af54b5050..2b3f0711a 100644
--- 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_t protocols, void *data)
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, ngx_chain_t *in, off_t limit)
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
index 8c307aec1..15a2c4d33 100644
--- 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_module = {
};
-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_request_t *r)
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_request_t *r, ngx_chain_t *in)
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 (ngx_http_gzip_filter_deflate_start(r, ctx) != NGX_OK) {
+ goto failed;
+ }
+ }
- if (ctx->length > 0) {
+ if (in) {
+ if (ngx_chain_add_copy(r->pool, &ctx->in, in) == NGX_ERROR) {
+ goto failed;
+ }
+ }
- /* the actual zlib window size is smaller by 262 bytes */
+ if (ctx->nomem) {
- while (ctx->length < ((1 << (wbits - 1)) - 262)) {
- wbits--;
- memlevel--;
- }
+ /* flush busy buffers */
+
+ if (ngx_http_next_body_filter(r, NULL) == NGX_ERROR) {
+ goto failed;
}
- /*
- * 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
- */
+ cl = NULL;
- ctx->allocated = 8192 + (1 << (wbits + 2)) + (1 << (memlevel + 9));
+ ngx_chain_update_chains(&ctx->free, &ctx->busy, &cl,
+ (ngx_buf_tag_t) &ngx_http_gzip_filter_module);
+ ctx->nomem = 0;
+ }
- ctx->preallocated = ngx_palloc(r->pool, ctx->allocated);
- if (ctx->preallocated == NULL) {
- return NGX_ERROR;
- }
+ for ( ;; ) {
- ctx->free_mem = ctx->preallocated;
+ /* cycle while we can write to a client */
- ctx->zstream.zalloc = ngx_http_gzip_filter_alloc;
- ctx->zstream.zfree = ngx_http_gzip_filter_free;
- ctx->zstream.opaque = ctx;
+ for ( ;; ) {
- rc = deflateInit2(&ctx->zstream, (int) conf->level, Z_DEFLATED,
- -wbits, memlevel, Z_DEFAULT_STRATEGY);
+ /* cycle while there is data to feed zlib and ... */
- 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;
+ rc = ngx_http_gzip_filter_add_data(r, ctx);
+
+ if (rc == NGX_DECLINED) {
+ break;
+ }
+
+ if (rc == NGX_AGAIN) {
+ continue;
+ }
+
+
+ /* ... there are buffers to write zlib output */
+
+ rc = ngx_http_gzip_filter_get_buf(r, ctx);
+
+ if (rc == NGX_DECLINED) {
+ break;
+ }
+
+ if (rc == NGX_ERROR) {
+ goto failed;
+ }
+
+
+ rc = ngx_http_gzip_filter_deflate(r, ctx);
+
+ if (rc == NGX_OK) {
+ break;
+ }
+
+ if (rc == NGX_ERROR) {
+ goto failed;
+ }
+
+ /* rc == NGX_AGAIN */
}
- b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
- if (b == NULL) {
- ngx_http_gzip_error(ctx);
- return NGX_ERROR;
+ if (ctx->out == NULL) {
+ return ctx->busy ? NGX_AGAIN : NGX_OK;
}
- b->memory = 1;
- b->pos = gzheader;
- b->last = b->pos + 10;
+ if (!ctx->gzheader) {
+ if (ngx_http_gzip_filter_gzheader(r, ctx) != NGX_OK) {
+ goto failed;
+ }
+ }
- out.buf = b;
- out.next = NULL;
+ rc = ngx_http_next_body_filter(r, ctx->out);
- /*
- * 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 (rc == NGX_ERROR) {
+ goto failed;
+ }
- if (ngx_http_next_body_filter(r, &out) == NGX_ERROR) {
- ngx_http_gzip_error(ctx);
- return NGX_ERROR;
+ 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 rc;
}
+ }
- r->connection->buffered |= NGX_HTTP_GZIP_BUFFERED;
+ /* unreachable */
- ctx->last_out = &ctx->out;
+failed:
- ctx->crc32 = crc32(0L, Z_NULL, 0);
- ctx->flush = Z_NO_FLUSH;
+ ctx->done = 1;
+
+ if (ctx->preallocated) {
+ deflateEnd(&ctx->zstream);
+
+ ngx_pfree(r->pool, ctx->preallocated);
}
- if (in) {
- if (ngx_chain_add_copy(r->pool, &ctx->in, in) == NGX_ERROR) {
- ngx_http_gzip_error(ctx);
- return NGX_ERROR;
+ 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--;
}
}
- last = NGX_NONE;
+ /*
+ * 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
+ */
- for ( ;; ) {
+ ctx->allocated = 8192 + (1 << (wbits + 2)) + (1 << (memlevel + 9));
- for ( ;; ) {
+ ctx->preallocated = ngx_palloc(r->pool, ctx->allocated);
+ if (ctx->preallocated == NULL) {
+ return NGX_ERROR;
+ }
- /* 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);
-
- 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);
-
- /* 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;
-
- } 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);
- }
- }
+ 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;
- /* is there a space for the gzipped data ? */
+ rc = deflateInit2(&ctx->zstream, (int) conf->level, Z_DEFLATED,
+ -wbits, memlevel, Z_DEFAULT_STRATEGY);
- if (ctx->zstream.avail_out == 0) {
+ if (rc != Z_OK) {
+ ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+ "deflateInit2() failed: %d", rc);
+ return NGX_ERROR;
+ }
- if (ctx->free) {
- ctx->out_buf = ctx->free->buf;
- ctx->free = ctx->free->next;
+ r->connection->buffered |= NGX_HTTP_GZIP_BUFFERED;
- } 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->last_out = &ctx->out;
+ ctx->crc32 = crc32(0L, Z_NULL, 0);
+ ctx->flush = Z_NO_FLUSH;
- ctx->out_buf->tag = (ngx_buf_tag_t)
- &ngx_http_gzip_filter_module;
- ctx->out_buf->recycled = 1;
- ctx->bufs++;
+ return NGX_OK;
+}
- } else {
- break;
- }
- ctx->zstream.next_out = ctx->out_buf->pos;
- ctx->zstream.avail_out = conf->bufs.size;
- }
+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 };
- 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);
+ b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
+ if (b == NULL) {
+ return NGX_ERROR;
+ }
- rc = deflate(&ctx->zstream, ctx->flush);
+ b->memory = 1;
+ b->pos = gzheader;
+ b->last = b->pos + 10;
- 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;
- }
+ cl = ngx_alloc_chain_link(r->pool);
+ if (cl == NULL) {
+ 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);
+ cl->buf = b;
+ cl->next = ctx->out;
+ ctx->out = cl;
- ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
- "gzip in_buf:%p pos:%p",
- ctx->in_buf, ctx->in_buf->pos);
+ ctx->gzheader = 1;
+ return NGX_OK;
+}
- if (ctx->zstream.next_in) {
- ctx->in_buf->pos = ctx->zstream.next_in;
- if (ctx->zstream.avail_in == 0) {
- ctx->zstream.next_in = NULL;
- }
- }
+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;
+ }
- ctx->out_buf->last = ctx->zstream.next_out;
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "gzip in: %p", ctx->in);
- if (ctx->zstream.avail_out == 0) {
+ if (ctx->in == NULL) {
+ return NGX_DECLINED;
+ }
- /* zlib wants to output some more gzipped data */
+ ctx->in_buf = ctx->in->buf;
+ ctx->in = ctx->in->next;
- cl = ngx_alloc_chain_link(r->pool);
- if (cl == NULL) {
- ngx_http_gzip_error(ctx);
- return NGX_ERROR;
- }
+ ctx->zstream.next_in = ctx->in_buf->pos;
+ ctx->zstream.avail_in = ctx->in_buf->last - ctx->in_buf->pos;
- cl->buf = ctx->out_buf;
- cl->next = NULL;
- *ctx->last_out = cl;
- ctx->last_out = &cl->next;
+ 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);
- ctx->redo = 1;
+ if (ctx->in_buf->last_buf) {
+ ctx->flush = Z_FINISH;
- continue;
- }
+ } else if (ctx->in_buf->flush) {
+ ctx->flush = Z_SYNC_FLUSH;
+ }
- ctx->redo = 0;
+ if (ctx->zstream.avail_in) {
- if (ctx->flush == Z_SYNC_FLUSH) {
+ ctx->crc32 = crc32(ctx->crc32, ctx->zstream.next_in,
+ ctx->zstream.avail_in);
- ctx->zstream.avail_out = 0;
- ctx->out_buf->flush = 1;
- ctx->flush = Z_NO_FLUSH;
+ } else if (ctx->flush == Z_NO_FLUSH) {
+ return NGX_AGAIN;
+ }
- cl = ngx_alloc_chain_link(r->pool);
- if (cl == NULL) {
- ngx_http_gzip_error(ctx);
- return NGX_ERROR;
- }
+ return NGX_OK;
+}
- cl->buf = ctx->out_buf;
- cl->next = NULL;
- *ctx->last_out = cl;
- ctx->last_out = &cl->next;
- break;
- }
+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 (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 (ctx->zstream.avail_out) {
+ return NGX_OK;
+ }
-#if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)
+ conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);
- trailer->crc32 = ctx->crc32;
- trailer->zlen = ctx->zin;
+ if (ctx->free) {
+ ctx->out_buf = ctx->free->buf;
+ ctx->free = ctx->free->next;
-#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
+ } else if (ctx->bufs < conf->bufs.num) {
- ctx->zstream.avail_in = 0;
- ctx->zstream.avail_out = 0;
+ ctx->out_buf = ngx_create_temp_buf(r->pool, conf->bufs.size);
+ if (ctx->out_buf == NULL) {
+ return NGX_ERROR;
+ }
- ctx->done = 1;
+ ctx->out_buf->tag = (ngx_buf_tag_t) &ngx_http_gzip_filter_module;
+ ctx->out_buf->recycled = 1;
+ ctx->bufs++;
- r->connection->buffered &= ~NGX_HTTP_GZIP_BUFFERED;
+ } 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;
+}
- break;
- }
- if (conf->no_buffer && ctx->in == NULL) {
+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;
- cl = ngx_alloc_chain_link(r->pool);
- if (cl == NULL) {
- ngx_http_gzip_error(ctx);
- return NGX_ERROR;
- }
+ 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);
- cl->buf = ctx->out_buf;
- cl->next = NULL;
- *ctx->last_out = cl;
- ctx->last_out = &cl->next;
+ rc = deflate(&ctx->zstream, ctx->flush);
- break;
- }
+ 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;
}
+ }
- if (ctx->out == NULL) {
+ ctx->out_buf->last = ctx->zstream.next_out;
- if (last == NGX_AGAIN) {
- return NGX_AGAIN;
- }
+ if (ctx->zstream.avail_out == 0) {
- if (ctx->busy == NULL) {
- return NGX_OK;
- }
+ /* zlib wants to output some more gzipped data */
+
+ cl = ngx_alloc_chain_link(r->pool);
+ if (cl == NULL) {
+ return NGX_ERROR;
}
- last = ngx_http_next_body_filter(r, ctx->out);
+ cl->buf = ctx->out_buf;
+ cl->next = NULL;
+ *ctx->last_out = cl;
+ ctx->last_out = &cl->next;
- /*
- * we do not check NGX_AGAIN here because the downstream filters
- * may free some buffers and zlib may compress some data into them
- */
+ ctx->redo = 1;
+
+ return NGX_AGAIN;
+ }
+
+ ctx->redo = 0;
+
+ if (ctx->flush == Z_SYNC_FLUSH) {
- if (last == NGX_ERROR) {
- ngx_http_gzip_error(ctx);
+ 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;
}
- ngx_chain_update_chains(&ctx->free, &ctx->busy, &ctx->out,
- (ngx_buf_tag_t) &ngx_http_gzip_filter_module);
- ctx->last_out = &ctx->out;
+ cl->buf = ctx->out_buf;
+ cl->next = NULL;
+ *ctx->last_out = cl;
+ ctx->last_out = &cl->next;
- if (ctx->done) {
- return last;
+ 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, void *address)
}
-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, void *post, void *data)
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
index 000000000..1376685ba
--- /dev/null
+++ b/src/http/modules/ngx_http_limit_req_module.c
@@ -0,0 +1,794 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+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
index 80bf22180..7a85d1b75 100644
--- 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_request_t *r, ngx_http_log_script_t *script,
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
index f3d747f0f..5e98ad9d4 100644
--- 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 *r, ngx_http_proxy_ctx_t *ctx,
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, ngx_http_proxy_ctx_t *ctx,
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
index 0b2a4451a..049a45c3e 100644
--- 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
index 4b93284e0..ed620a6c8 100644
--- 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
index 79b218716..77d80a895 100644
--- 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_t t);
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
index 1d8a55222..f220368da 100644
--- 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, ngx_str_t *s)
}
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, ngx_command_t *cmd, void *conf)
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
index ca5cb9e63..1088704c9 100644
--- 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_postponed_request(ngx_http_request_t *r)
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
index e1f0ae793..ab4c0e08f 100644
--- 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_event_t *ev);
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_t *r)
}
-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
index 2486ca5c0..2a3216158 100644
--- 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
index 9144913be..c3fc8612c 100644
--- 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_t *r)
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
index dd72ba86d..c87bcdc57 100644
--- 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;