summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIgor Sysoev <igor@sysoev.ru>2011-09-20 11:21:32 +0000
committerJonathan Kolb <jon@b0g.us>2011-09-20 11:21:32 +0000
commit73e4a20a129d2afb1c5a1e1bb0a5d6966d9f5e5e (patch)
tree2b3effcc3781c00b66e25b5fc5cdeb97f4af962a
parentf4ca46af8aa347d4fd13c1db55a9a4e79bfacc92 (diff)
downloadnginx-73e4a20a129d2afb1c5a1e1bb0a5d6966d9f5e5e.tar.gz
Changes with nginx 1.1.4 20 Sep 2011v1.1.4
*) Feature: the ngx_http_upstream_keepalive module. *) Feature: the "proxy_http_version" directive. *) Feature: the "fastcgi_keep_conn" directive. *) Feature: the "worker_aio_requests" directive. *) Bugfix: if nginx was built --with-file-aio it could not be run on Linux kernel which did not support AIO. *) Bugfix: in Linux AIO error processing. Thanks to Hagai Avrahami. *) Bugfix: reduced memory consumption for long-lived requests. *) Bugfix: the module ngx_http_mp4_module did not support 64-bit MP4 "co64" atom.
-rw-r--r--CHANGES22
-rw-r--r--CHANGES.ru22
-rw-r--r--auto/modules5
-rw-r--r--auto/options2
-rw-r--r--auto/sources5
-rw-r--r--html/ngx_core_module.html13
-rw-r--r--src/core/nginx.h4
-rw-r--r--src/core/ngx_buf.c16
-rw-r--r--src/core/ngx_buf.h4
-rw-r--r--src/core/ngx_cycle.c2
-rw-r--r--src/core/ngx_output_chain.c3
-rw-r--r--src/core/ngx_palloc.c2
-rw-r--r--src/core/ngx_string.c2
-rw-r--r--src/event/modules/ngx_epoll_module.c152
-rw-r--r--src/event/ngx_event.c2
-rw-r--r--src/event/ngx_event_pipe.c34
-rw-r--r--src/event/ngx_event_pipe.h1
-rw-r--r--src/event/ngx_event_timer.c10
-rw-r--r--src/http/modules/ngx_http_chunked_filter_module.c99
-rw-r--r--src/http/modules/ngx_http_dav_module.c2
-rw-r--r--src/http/modules/ngx_http_fastcgi_module.c82
-rw-r--r--src/http/modules/ngx_http_gzip_filter_module.c4
-rw-r--r--src/http/modules/ngx_http_headers_filter_module.c2
-rw-r--r--src/http/modules/ngx_http_log_module.c2
-rw-r--r--src/http/modules/ngx_http_memcached_module.c22
-rw-r--r--src/http/modules/ngx_http_mp4_module.c184
-rw-r--r--src/http/modules/ngx_http_proxy_module.c783
-rw-r--r--src/http/modules/ngx_http_upstream_keepalive_module.c569
-rw-r--r--src/http/modules/perl/nginx.pm2
-rw-r--r--src/http/ngx_http.c2
-rw-r--r--src/http/ngx_http.h1
-rw-r--r--src/http/ngx_http_core_module.c48
-rw-r--r--src/http/ngx_http_parse.c5
-rw-r--r--src/http/ngx_http_upstream.c153
-rw-r--r--src/http/ngx_http_upstream.h7
-rw-r--r--src/os/unix/ngx_errno.c2
-rw-r--r--src/os/unix/ngx_file_aio_read.c2
-rw-r--r--src/os/unix/ngx_freebsd_sendfile_chain.c2
-rw-r--r--src/os/unix/ngx_linux_aio_read.c16
-rw-r--r--src/os/unix/ngx_process.c2
-rw-r--r--src/os/unix/ngx_process_cycle.c3
41 files changed, 2064 insertions, 231 deletions
diff --git a/CHANGES b/CHANGES
index bce98b7ea..16856a62f 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,26 @@
+Changes with nginx 1.1.4 20 Sep 2011
+
+ *) Feature: the ngx_http_upstream_keepalive module.
+
+ *) Feature: the "proxy_http_version" directive.
+
+ *) Feature: the "fastcgi_keep_conn" directive.
+
+ *) Feature: the "worker_aio_requests" directive.
+
+ *) Bugfix: if nginx was built --with-file-aio it could not be run on
+ Linux kernel which did not support AIO.
+
+ *) Bugfix: in Linux AIO error processing.
+ Thanks to Hagai Avrahami.
+
+ *) Bugfix: reduced memory consumption for long-lived requests.
+
+ *) Bugfix: the module ngx_http_mp4_module did not support 64-bit MP4
+ "co64" atom.
+
+
Changes with nginx 1.1.3 14 Sep 2011
*) Feature: the module ngx_http_mp4_module.
diff --git a/CHANGES.ru b/CHANGES.ru
index 24fd20b01..02fd37a59 100644
--- a/CHANGES.ru
+++ b/CHANGES.ru
@@ -1,4 +1,26 @@
+Изменения в nginx 1.1.4 20.09.2011
+
+ *) Добавление: модуль ngx_http_upstream_keepalive.
+
+ *) Добавление: директива proxy_http_version.
+
+ *) Добавление: директива fastcgi_keep_conn.
+
+ *) Добавление: директива worker_aio_requests.
+
+ *) Исправление: если nginx был собран с файловым AIO, он не мог
+ запускаться на Linux без поддержки AIO.
+
+ *) Исправление: в обработке ошибок при работе с Linux AIO.
+ Спасибо Hagai Avrahami.
+
+ *) Исправление: уменьшено потребление памяти для долгоживущих запросов.
+
+ *) Исправление: модуль ngx_http_mp4_module не поддерживал 64-битный
+ MP4-атом co64.
+
+
Изменения в nginx 1.1.3 14.09.2011
*) Добавление: модуль ngx_http_mp4_module.
diff --git a/auto/modules b/auto/modules
index b13bcb25b..57f0f89e7 100644
--- a/auto/modules
+++ b/auto/modules
@@ -344,6 +344,11 @@ if [ $HTTP_UPSTREAM_IP_HASH = YES ]; then
HTTP_SRCS="$HTTP_SRCS $HTTP_UPSTREAM_IP_HASH_SRCS"
fi
+if [ $HTTP_UPSTREAM_KEEPALIVE = YES ]; then
+ HTTP_MODULES="$HTTP_MODULES $HTTP_UPSTREAM_KEEPALIVE_MODULE"
+ HTTP_SRCS="$HTTP_SRCS $HTTP_UPSTREAM_KEEPALIVE_SRCS"
+fi
+
if [ $HTTP_STUB_STATUS = YES ]; then
have=NGX_STAT_STUB . auto/have
HTTP_MODULES="$HTTP_MODULES ngx_http_stub_status_module"
diff --git a/auto/options b/auto/options
index f30917c89..2019498c3 100644
--- a/auto/options
+++ b/auto/options
@@ -95,6 +95,7 @@ HTTP_FLV=NO
HTTP_MP4=NO
HTTP_GZIP_STATIC=NO
HTTP_UPSTREAM_IP_HASH=YES
+HTTP_UPSTREAM_KEEPALIVE=YES
# STUB
HTTP_STUB_STATUS=NO
@@ -231,6 +232,7 @@ do
--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 ;;
+ --without-http_upstream_keepalive_module) HTTP_UPSTREAM_KEEPALIVE=NO ;;
--with-http_perl_module) HTTP_PERL=YES ;;
--with-perl_modules_path=*) NGX_PERL_MODULES="$value" ;;
diff --git a/auto/sources b/auto/sources
index 00ec7b3d4..6c973f66b 100644
--- a/auto/sources
+++ b/auto/sources
@@ -475,6 +475,11 @@ HTTP_UPSTREAM_IP_HASH_MODULE=ngx_http_upstream_ip_hash_module
HTTP_UPSTREAM_IP_HASH_SRCS=src/http/modules/ngx_http_upstream_ip_hash_module.c
+HTTP_UPSTREAM_KEEPALIVE_MODULE=ngx_http_upstream_keepalive_module
+HTTP_UPSTREAM_KEEPALIVE_SRCS=" \
+ src/http/modules/ngx_http_upstream_keepalive_module.c"
+
+
MAIL_INCS="src/mail"
MAIL_DEPS="src/mail/ngx_mail.h"
diff --git a/html/ngx_core_module.html b/html/ngx_core_module.html
index 705cd7bed..4c5e8d8ac 100644
--- a/html/ngx_core_module.html
+++ b/html/ngx_core_module.html
@@ -1,4 +1,4 @@
-<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><title>Core Module</title></head><body><a name="example"></a><center><h4>Example Configuration</h4></center><p><blockquote><pre>
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><title>Core Module</title></head><body><center><h3>Core Module</h3></center><a name="example"></a><center><h4>Example Configuration</h4></center><p><blockquote><pre>
user www www;
worker_processes 2;
@@ -27,9 +27,8 @@ variable inheritance during a
<a href="control.html#upgrade">live upgrade</a>
of an executable file;
</li><li>
-use of variables by the
-<a href="http/ngx_http_perl_module.html">http_perl</a>
-module;
+use of variables by the module
+<a href="http/ngx_http_perl_module.html">ngx_http_perl_module</a>;
</li><li>
use of variables by worker processes.
Please bear in mind that controlling system libraries in this way
@@ -40,9 +39,9 @@ An exception from this is an above mentioned
<a href="control.html#upgrade">live upgrade</a>
of an executable file.
</li></ul></p><p>
-The TZ variable is always inherited and made available to the
-<a href="http/ngx_http_perl_module.html">http_perl</a>
-module, unless configured explicitly.
+The TZ variable is always inherited and made available to the module
+<a href="http/ngx_http_perl_module.html">ngx_http_perl_module</a>,
+unless configured explicitly.
</p><p>
Usage example:
<blockquote><pre>
diff --git a/src/core/nginx.h b/src/core/nginx.h
index a47054bcb..ca6cca092 100644
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -8,8 +8,8 @@
#define _NGINX_H_INCLUDED_
-#define nginx_version 1001003
-#define NGINX_VERSION "1.1.3"
+#define nginx_version 1001004
+#define NGINX_VERSION "1.1.4"
#define NGINX_VER "nginx/" NGINX_VERSION
#define NGINX_VAR "NGINX"
diff --git a/src/core/ngx_buf.c b/src/core/ngx_buf.c
index 2f2c43721..53cd50cea 100644
--- a/src/core/ngx_buf.c
+++ b/src/core/ngx_buf.c
@@ -180,7 +180,7 @@ ngx_chain_get_free_buf(ngx_pool_t *p, ngx_chain_t **free)
void
-ngx_chain_update_chains(ngx_chain_t **free, ngx_chain_t **busy,
+ngx_chain_update_chains(ngx_pool_t *p, ngx_chain_t **free, ngx_chain_t **busy,
ngx_chain_t **out, ngx_buf_tag_t tag)
{
ngx_chain_t *cl;
@@ -197,19 +197,21 @@ ngx_chain_update_chains(ngx_chain_t **free, ngx_chain_t **busy,
*out = NULL;
while (*busy) {
- if (ngx_buf_size((*busy)->buf) != 0) {
+ cl = *busy;
+
+ if (ngx_buf_size(cl->buf) != 0) {
break;
}
- if ((*busy)->buf->tag != tag) {
- *busy = (*busy)->next;
+ if (cl->buf->tag != tag) {
+ *busy = cl->next;
+ ngx_free_chain(p, cl);
continue;
}
- (*busy)->buf->pos = (*busy)->buf->start;
- (*busy)->buf->last = (*busy)->buf->start;
+ cl->buf->pos = cl->buf->start;
+ cl->buf->last = cl->buf->start;
- cl = *busy;
*busy = cl->next;
cl->next = *free;
*free = cl;
diff --git a/src/core/ngx_buf.h b/src/core/ngx_buf.h
index 847eaad05..a6bf39ff3 100644
--- a/src/core/ngx_buf.h
+++ b/src/core/ngx_buf.h
@@ -154,8 +154,8 @@ ngx_int_t ngx_chain_writer(void *ctx, ngx_chain_t *in);
ngx_int_t ngx_chain_add_copy(ngx_pool_t *pool, ngx_chain_t **chain,
ngx_chain_t *in);
ngx_chain_t *ngx_chain_get_free_buf(ngx_pool_t *p, ngx_chain_t **free);
-void ngx_chain_update_chains(ngx_chain_t **free, ngx_chain_t **busy,
- ngx_chain_t **out, ngx_buf_tag_t tag);
+void ngx_chain_update_chains(ngx_pool_t *p, ngx_chain_t **free,
+ ngx_chain_t **busy, ngx_chain_t **out, ngx_buf_tag_t tag);
#endif /* _NGX_BUF_H_INCLUDED_ */
diff --git a/src/core/ngx_cycle.c b/src/core/ngx_cycle.c
index 968056c42..db473571f 100644
--- a/src/core/ngx_cycle.c
+++ b/src/core/ngx_cycle.c
@@ -739,7 +739,7 @@ old_shm_zone_done:
ngx_temp_pool = ngx_create_pool(128, cycle->log);
if (ngx_temp_pool == NULL) {
ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
- "can not create ngx_temp_pool");
+ "could not create ngx_temp_pool");
exit(1);
}
diff --git a/src/core/ngx_output_chain.c b/src/core/ngx_output_chain.c
index 4f100a818..e45f0342d 100644
--- a/src/core/ngx_output_chain.c
+++ b/src/core/ngx_output_chain.c
@@ -208,7 +208,8 @@ ngx_output_chain(ngx_output_chain_ctx_t *ctx, ngx_chain_t *in)
return last;
}
- ngx_chain_update_chains(&ctx->free, &ctx->busy, &out, ctx->tag);
+ ngx_chain_update_chains(ctx->pool, &ctx->free, &ctx->busy, &out,
+ ctx->tag);
last_out = &out;
}
}
diff --git a/src/core/ngx_palloc.c b/src/core/ngx_palloc.c
index 35f13119c..9eac114b7 100644
--- a/src/core/ngx_palloc.c
+++ b/src/core/ngx_palloc.c
@@ -68,7 +68,7 @@ ngx_destroy_pool(ngx_pool_t *pool)
/*
* we could allocate the pool->log from this pool
- * so we can not use this log while the free()ing the pool
+ * so we cannot use this log while free()ing the pool
*/
for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {
diff --git a/src/core/ngx_string.c b/src/core/ngx_string.c
index 4dcfe01eb..75bc6578d 100644
--- a/src/core/ngx_string.c
+++ b/src/core/ngx_string.c
@@ -381,7 +381,7 @@ ngx_vslprintf(u_char *buf, u_char *last, const char *fmt, va_list args)
/*
* (int64_t) cast is required for msvc6:
- * it can not convert uint64_t to double
+ * it cannot convert uint64_t to double
*/
ui64 = (uint64_t) ((f - (int64_t) ui64) * scale + 0.5);
diff --git a/src/event/modules/ngx_epoll_module.c b/src/event/modules/ngx_epoll_module.c
index e30501b00..31c10c6dc 100644
--- a/src/event/modules/ngx_epoll_module.c
+++ b/src/event/modules/ngx_epoll_module.c
@@ -86,6 +86,7 @@ int eventfd(u_int initval)
typedef struct {
ngx_uint_t events;
+ ngx_uint_t aio_requests;
} ngx_epoll_conf_t;
@@ -133,6 +134,13 @@ static ngx_command_t ngx_epoll_commands[] = {
offsetof(ngx_epoll_conf_t, events),
NULL },
+ { ngx_string("worker_aio_requests"),
+ NGX_EVENT_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ 0,
+ offsetof(ngx_epoll_conf_t, aio_requests),
+ NULL },
+
ngx_null_command
};
@@ -184,7 +192,7 @@ ngx_module_t ngx_epoll_module = {
* into single eventfd() function with different number of parameters.
*/
-static long
+static int
io_setup(u_int nr_reqs, aio_context_t *ctx)
{
return syscall(SYS_io_setup, nr_reqs, ctx);
@@ -198,13 +206,81 @@ io_destroy(aio_context_t ctx)
}
-static long
+static int
io_getevents(aio_context_t ctx, long min_nr, long nr, struct io_event *events,
struct timespec *tmo)
{
return syscall(SYS_io_getevents, ctx, min_nr, nr, events, tmo);
}
+
+static void
+ngx_epoll_aio_init(ngx_cycle_t *cycle, ngx_epoll_conf_t *epcf)
+{
+ int n;
+ struct epoll_event ee;
+
+ ngx_eventfd = syscall(SYS_eventfd, 0);
+
+ if (ngx_eventfd == -1) {
+ ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
+ "eventfd() failed");
+ ngx_file_aio = 0;
+ return;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "eventfd: %d", ngx_eventfd);
+
+ n = 1;
+
+ if (ioctl(ngx_eventfd, FIONBIO, &n) == -1) {
+ ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
+ "ioctl(eventfd, FIONBIO) failed");
+ goto failed;
+ }
+
+ if (io_setup(epcf->aio_requests, &ngx_aio_ctx) == -1) {
+ ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
+ "io_setup() failed");
+ goto failed;
+ }
+
+ ngx_eventfd_event.data = &ngx_eventfd_conn;
+ ngx_eventfd_event.handler = ngx_epoll_eventfd_handler;
+ ngx_eventfd_event.log = cycle->log;
+ ngx_eventfd_event.active = 1;
+ ngx_eventfd_conn.fd = ngx_eventfd;
+ ngx_eventfd_conn.read = &ngx_eventfd_event;
+ ngx_eventfd_conn.log = cycle->log;
+
+ ee.events = EPOLLIN|EPOLLET;
+ ee.data.ptr = &ngx_eventfd_conn;
+
+ if (epoll_ctl(ep, EPOLL_CTL_ADD, ngx_eventfd, &ee) != -1) {
+ return;
+ }
+
+ ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
+ "epoll_ctl(EPOLL_CTL_ADD, eventfd) failed");
+
+ if (io_destroy(ngx_aio_ctx) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "io_destroy() failed");
+ }
+
+failed:
+
+ if (close(ngx_eventfd) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "eventfd close() failed");
+ }
+
+ ngx_eventfd = -1;
+ ngx_aio_ctx = 0;
+ ngx_file_aio = 0;
+}
+
#endif
@@ -225,52 +301,9 @@ ngx_epoll_init(ngx_cycle_t *cycle, ngx_msec_t timer)
}
#if (NGX_HAVE_FILE_AIO)
- {
- int n;
- struct epoll_event ee;
-
- ngx_eventfd = syscall(SYS_eventfd, 0);
-
- if (ngx_eventfd == -1) {
- ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
- "eventfd() failed");
- return NGX_ERROR;
- }
-
- n = 1;
-
- if (ioctl(ngx_eventfd, FIONBIO, &n) == -1) {
- ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
- "ioctl(eventfd, FIONBIO) failed");
- }
-
- ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
- "eventfd: %d", ngx_eventfd);
- n = io_setup(1024, &ngx_aio_ctx);
+ ngx_epoll_aio_init(cycle, epcf);
- if (n != 0) {
- ngx_log_error(NGX_LOG_EMERG, cycle->log, -n, "io_setup() failed");
- return NGX_ERROR;
- }
-
- ngx_eventfd_event.data = &ngx_eventfd_conn;
- ngx_eventfd_event.handler = ngx_epoll_eventfd_handler;
- ngx_eventfd_event.log = cycle->log;
- ngx_eventfd_event.active = 1;
- ngx_eventfd_conn.fd = ngx_eventfd;
- ngx_eventfd_conn.read = &ngx_eventfd_event;
- ngx_eventfd_conn.log = cycle->log;
-
- ee.events = EPOLLIN|EPOLLET;
- ee.data.ptr = &ngx_eventfd_conn;
-
- if (epoll_ctl(ep, EPOLL_CTL_ADD, ngx_eventfd, &ee) == -1) {
- ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
- "epoll_ctl(EPOLL_CTL_ADD, eventfd) failed");
- return NGX_ERROR;
- }
- }
#endif
}
@@ -316,9 +349,19 @@ ngx_epoll_done(ngx_cycle_t *cycle)
#if (NGX_HAVE_FILE_AIO)
- if (io_destroy(ngx_aio_ctx) != 0) {
- ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
- "io_destroy() failed");
+ if (ngx_eventfd != -1) {
+
+ if (io_destroy(ngx_aio_ctx) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "io_destroy() failed");
+ }
+
+ if (close(ngx_eventfd) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "eventfd close() failed");
+ }
+
+ ngx_eventfd = -1;
}
ngx_aio_ctx = 0;
@@ -667,8 +710,8 @@ ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags)
static void
ngx_epoll_eventfd_handler(ngx_event_t *ev)
{
- int n;
- long i, events;
+ int n, events;
+ long i;
uint64_t ready;
ngx_err_t err;
ngx_event_t *e;
@@ -738,8 +781,9 @@ ngx_epoll_eventfd_handler(ngx_event_t *ev)
return;
}
- /* events < 0 */
- ngx_log_error(NGX_LOG_ALERT, ev->log, -events, "io_getevents() failed");
+ /* events == -1 */
+ ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,
+ "io_getevents() failed");
return;
}
}
@@ -758,6 +802,7 @@ ngx_epoll_create_conf(ngx_cycle_t *cycle)
}
epcf->events = NGX_CONF_UNSET;
+ epcf->aio_requests = NGX_CONF_UNSET;
return epcf;
}
@@ -769,6 +814,7 @@ ngx_epoll_init_conf(ngx_cycle_t *cycle, void *conf)
ngx_epoll_conf_t *epcf = conf;
ngx_conf_init_uint_value(epcf->events, 512);
+ ngx_conf_init_uint_value(epcf->aio_requests, 32);
return NGX_CONF_OK;
}
diff --git a/src/event/ngx_event.c b/src/event/ngx_event.c
index c57d37ec4..8a472ffe8 100644
--- a/src/event/ngx_event.c
+++ b/src/event/ngx_event.c
@@ -1027,7 +1027,7 @@ ngx_event_use(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
"when the server runs without a master process "
"the \"%V\" event type must be the same as "
"in previous configuration - \"%s\" "
- "and it can not be changed on the fly, "
+ "and it cannot be changed on the fly, "
"to change it you need to stop server "
"and start it again",
&value[1], old_ecf->name);
diff --git a/src/event/ngx_event_pipe.c b/src/event/ngx_event_pipe.c
index 53d738f0a..4f45bb872 100644
--- a/src/event/ngx_event_pipe.c
+++ b/src/event/ngx_event_pipe.c
@@ -392,8 +392,32 @@ ngx_event_pipe_read_upstream(ngx_event_pipe_t *p)
cl->buf->file_last - cl->buf->file_pos);
}
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0,
+ "pipe length: %O", p->length);
+
#endif
+ if (p->free_raw_bufs && p->length != -1) {
+ cl = p->free_raw_bufs;
+
+ if (cl->buf->last - cl->buf->pos >= p->length) {
+
+ /* STUB */ cl->buf->num = p->num++;
+
+ if (p->input_filter(p, cl->buf) == NGX_ERROR) {
+ return NGX_ABORT;
+ }
+
+ p->free_raw_bufs = cl->next;
+ ngx_free_chain(p->pool, cl);
+ }
+ }
+
+ if (p->length == 0) {
+ p->upstream_done = 1;
+ p->read = 1;
+ }
+
if ((p->upstream_eof || p->upstream_error) && p->free_raw_bufs) {
/* STUB */ p->free_raw_bufs->buf->num = p->num++;
@@ -633,13 +657,13 @@ ngx_event_pipe_write_to_downstream(ngx_event_pipe_t *p)
rc = p->output_filter(p->output_ctx, out);
+ ngx_chain_update_chains(p->pool, &p->free, &p->busy, &out, p->tag);
+
if (rc == NGX_ERROR) {
p->downstream_error = 1;
return ngx_event_pipe_drain_chains(p);
}
- ngx_chain_update_chains(&p->free, &p->busy, &out, p->tag);
-
for (cl = p->free; cl; cl = cl->next) {
if (cl->buf->temp_file) {
@@ -848,6 +872,12 @@ ngx_event_pipe_copy_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
}
p->last_in = &cl->next;
+ if (p->length == -1) {
+ return NGX_OK;
+ }
+
+ p->length -= b->last - b->pos;
+
return NGX_OK;
}
diff --git a/src/event/ngx_event_pipe.h b/src/event/ngx_event_pipe.h
index 00b8acf66..26334673c 100644
--- a/src/event/ngx_event_pipe.h
+++ b/src/event/ngx_event_pipe.h
@@ -65,6 +65,7 @@ struct ngx_event_pipe_s {
ssize_t busy_size;
off_t read_length;
+ off_t length;
off_t max_temp_file_size;
ssize_t temp_file_write_size;
diff --git a/src/event/ngx_event_timer.c b/src/event/ngx_event_timer.c
index 2931a9f4e..76a1a1f41 100644
--- a/src/event/ngx_event_timer.c
+++ b/src/event/ngx_event_timer.c
@@ -103,11 +103,11 @@ ngx_event_expire_timers(void)
if (ngx_threaded && ngx_trylock(ev->lock) == 0) {
/*
- * We can not change the timer of the event that is been
- * handling by another thread. And we can not easy walk
- * the rbtree to find a next expired timer so we exit the loop.
- * However it should be rare case when the event that is
- * been handling has expired timer.
+ * We cannot change the timer of the event that is being
+ * handled by another thread. And we cannot easy walk
+ * the rbtree to find next expired timer so we exit the loop.
+ * However, it should be a rare case when the event that is
+ * being handled has an expired timer.
*/
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0,
diff --git a/src/http/modules/ngx_http_chunked_filter_module.c b/src/http/modules/ngx_http_chunked_filter_module.c
index 9c4c5de14..e6ae1c99d 100644
--- a/src/http/modules/ngx_http_chunked_filter_module.c
+++ b/src/http/modules/ngx_http_chunked_filter_module.c
@@ -9,6 +9,12 @@
#include <ngx_http.h>
+typedef struct {
+ ngx_chain_t *free;
+ ngx_chain_t *busy;
+} ngx_http_chunked_filter_ctx_t;
+
+
static ngx_int_t ngx_http_chunked_filter_init(ngx_conf_t *cf);
@@ -50,7 +56,8 @@ static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
static ngx_int_t
ngx_http_chunked_header_filter(ngx_http_request_t *r)
{
- ngx_http_core_loc_conf_t *clcf;
+ ngx_http_core_loc_conf_t *clcf;
+ ngx_http_chunked_filter_ctx_t *ctx;
if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED
|| r->headers_out.status == NGX_HTTP_NO_CONTENT
@@ -70,6 +77,14 @@ ngx_http_chunked_header_filter(ngx_http_request_t *r)
if (clcf->chunked_transfer_encoding) {
r->chunked = 1;
+ ctx = ngx_pcalloc(r->pool,
+ sizeof(ngx_http_chunked_filter_ctx_t));
+ if (ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_http_set_ctx(r, ctx, ngx_http_chunked_filter_module);
+
} else {
r->keepalive = 0;
}
@@ -83,17 +98,21 @@ ngx_http_chunked_header_filter(ngx_http_request_t *r)
static ngx_int_t
ngx_http_chunked_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
- u_char *chunk;
- off_t size;
- ngx_buf_t *b;
- ngx_chain_t out, tail, *cl, *tl, **ll;
+ u_char *chunk;
+ off_t size;
+ ngx_int_t rc;
+ ngx_buf_t *b;
+ ngx_chain_t *out, *cl, *tl, **ll;
+ ngx_http_chunked_filter_ctx_t *ctx;
if (in == NULL || !r->chunked || r->header_only) {
return ngx_http_next_body_filter(r, in);
}
- out.buf = NULL;
- ll = &out.next;
+ ctx = ngx_http_get_module_ctx(r, ngx_http_chunked_filter_module);
+
+ out = NULL;
+ ll = &out;
size = 0;
cl = in;
@@ -127,31 +146,46 @@ ngx_http_chunked_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
}
if (size) {
- b = ngx_calloc_buf(r->pool);
- if (b == NULL) {
+ tl = ngx_chain_get_free_buf(r->pool, &ctx->free);
+ if (tl == NULL) {
return NGX_ERROR;
}
- /* the "0000000000000000" is 64-bit hexadimal string */
+ b = tl->buf;
+ chunk = b->start;
- chunk = ngx_palloc(r->pool, sizeof("0000000000000000" CRLF) - 1);
if (chunk == NULL) {
- return NGX_ERROR;
+ /* the "0000000000000000" is 64-bit hexadecimal string */
+
+ chunk = ngx_palloc(r->pool, sizeof("0000000000000000" CRLF) - 1);
+ if (chunk == NULL) {
+ return NGX_ERROR;
+ }
+
+ b->start = chunk;
+ b->end = chunk + sizeof("0000000000000000" CRLF) - 1;
}
+ b->tag = (ngx_buf_tag_t) &ngx_http_chunked_filter_module;
+ b->memory = 0;
b->temporary = 1;
b->pos = chunk;
b->last = ngx_sprintf(chunk, "%xO" CRLF, size);
- out.buf = b;
+ tl->next = out;
+ out = tl;
}
if (cl->buf->last_buf) {
- b = ngx_calloc_buf(r->pool);
- if (b == NULL) {
+ tl = ngx_chain_get_free_buf(r->pool, &ctx->free);
+ if (tl == NULL) {
return NGX_ERROR;
}
+ b = tl->buf;
+
+ b->tag = (ngx_buf_tag_t) &ngx_http_chunked_filter_module;
+ b->temporary = 0;
b->memory = 1;
b->last_buf = 1;
b->pos = (u_char *) CRLF "0" CRLF CRLF;
@@ -159,35 +193,38 @@ ngx_http_chunked_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
cl->buf->last_buf = 0;
- if (size == 0) {
- b->pos += 2;
- out.buf = b;
- out.next = NULL;
-
- return ngx_http_next_body_filter(r, &out);
- }
+ *ll = tl;
- } else {
if (size == 0) {
- *ll = NULL;
- return ngx_http_next_body_filter(r, out.next);
+ b->pos += 2;
}
- b = ngx_calloc_buf(r->pool);
- if (b == NULL) {
+ } else if (size > 0) {
+ tl = ngx_chain_get_free_buf(r->pool, &ctx->free);
+ if (tl == NULL) {
return NGX_ERROR;
}
+ b = tl->buf;
+
+ b->tag = (ngx_buf_tag_t) &ngx_http_chunked_filter_module;
+ b->temporary = 0;
b->memory = 1;
b->pos = (u_char *) CRLF;
b->last = b->pos + 2;
+
+ *ll = tl;
+
+ } else {
+ *ll = NULL;
}
- tail.buf = b;
- tail.next = NULL;
- *ll = &tail;
+ rc = ngx_http_next_body_filter(r, out);
+
+ ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &out,
+ (ngx_buf_tag_t) &ngx_http_chunked_filter_module);
- return ngx_http_next_body_filter(r, &out);
+ return rc;
}
diff --git a/src/http/modules/ngx_http_dav_module.c b/src/http/modules/ngx_http_dav_module.c
index 0761c1654..3bd9d3e94 100644
--- a/src/http/modules/ngx_http_dav_module.c
+++ b/src/http/modules/ngx_http_dav_module.c
@@ -158,7 +158,7 @@ ngx_http_dav_handler(ngx_http_request_t *r)
if (r->uri.data[r->uri.len - 1] == '/') {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
- "can not PUT to a collection");
+ "cannot PUT to a collection");
return NGX_HTTP_CONFLICT;
}
diff --git a/src/http/modules/ngx_http_fastcgi_module.c b/src/http/modules/ngx_http_fastcgi_module.c
index 0bc95f8da..6f639acc2 100644
--- a/src/http/modules/ngx_http_fastcgi_module.c
+++ b/src/http/modules/ngx_http_fastcgi_module.c
@@ -26,6 +26,8 @@ typedef struct {
ngx_hash_t headers_hash;
ngx_uint_t header_params;
+ ngx_flag_t keep_conn;
+
#if (NGX_HTTP_CACHE)
ngx_http_complex_value_t cache_key;
#endif
@@ -77,6 +79,8 @@ typedef struct {
#define NGX_HTTP_FASTCGI_RESPONDER 1
+#define NGX_HTTP_FASTCGI_KEEP_CONN 1
+
#define NGX_HTTP_FASTCGI_BEGIN_REQUEST 1
#define NGX_HTTP_FASTCGI_ABORT_REQUEST 2
#define NGX_HTTP_FASTCGI_END_REQUEST 3
@@ -130,6 +134,7 @@ static ngx_int_t ngx_http_fastcgi_create_key(ngx_http_request_t *r);
static ngx_int_t ngx_http_fastcgi_create_request(ngx_http_request_t *r);
static ngx_int_t ngx_http_fastcgi_reinit_request(ngx_http_request_t *r);
static ngx_int_t ngx_http_fastcgi_process_header(ngx_http_request_t *r);
+static ngx_int_t ngx_http_fastcgi_input_filter_init(void *data);
static ngx_int_t ngx_http_fastcgi_input_filter(ngx_event_pipe_t *p,
ngx_buf_t *buf);
static ngx_int_t ngx_http_fastcgi_process_record(ngx_http_request_t *r,
@@ -437,6 +442,13 @@ static ngx_command_t ngx_http_fastcgi_commands[] = {
offsetof(ngx_http_fastcgi_loc_conf_t, catch_stderr),
NULL },
+ { ngx_string("fastcgi_keep_conn"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_fastcgi_loc_conf_t, keep_conn),
+ NULL },
+
ngx_null_command
};
@@ -600,6 +612,8 @@ ngx_http_fastcgi_handler(ngx_http_request_t *r)
u->pipe->input_filter = ngx_http_fastcgi_input_filter;
u->pipe->input_ctx = r;
+ u->input_filter_init = ngx_http_fastcgi_input_filter_init;
+
rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);
if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
@@ -841,6 +855,9 @@ ngx_http_fastcgi_create_request(ngx_http_request_t *r)
cl->buf = b;
+ ngx_http_fastcgi_request_start.br.flags =
+ flcf->keep_conn ? NGX_HTTP_FASTCGI_KEEP_CONN : 0;
+
ngx_memcpy(b->pos, &ngx_http_fastcgi_request_start,
sizeof(ngx_http_fastcgi_request_start_t));
@@ -1574,14 +1591,30 @@ ngx_http_fastcgi_process_header(ngx_http_request_t *r)
static ngx_int_t
+ngx_http_fastcgi_input_filter_init(void *data)
+{
+ ngx_http_request_t *r = data;
+ ngx_http_fastcgi_loc_conf_t *flcf;
+
+ flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
+
+ r->upstream->pipe->length = flcf->keep_conn ?
+ (off_t) sizeof(ngx_http_fastcgi_header_t) : -1;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
ngx_http_fastcgi_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
{
- u_char *m, *msg;
- ngx_int_t rc;
- ngx_buf_t *b, **prev;
- ngx_chain_t *cl;
- ngx_http_request_t *r;
- ngx_http_fastcgi_ctx_t *f;
+ u_char *m, *msg;
+ ngx_int_t rc;
+ ngx_buf_t *b, **prev;
+ ngx_chain_t *cl;
+ ngx_http_request_t *r;
+ ngx_http_fastcgi_ctx_t *f;
+ ngx_http_fastcgi_loc_conf_t *flcf;
if (buf->pos == buf->last) {
return NGX_OK;
@@ -1589,6 +1622,7 @@ ngx_http_fastcgi_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
r = p->input_ctx;
f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);
+ flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
b = NULL;
prev = &buf->shadow;
@@ -1611,7 +1645,10 @@ ngx_http_fastcgi_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
if (f->type == NGX_HTTP_FASTCGI_STDOUT && f->length == 0) {
f->state = ngx_http_fastcgi_st_version;
- p->upstream_done = 1;
+
+ if (!flcf->keep_conn) {
+ p->upstream_done = 1;
+ }
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, p->log, 0,
"http fastcgi closed stdout");
@@ -1623,6 +1660,10 @@ ngx_http_fastcgi_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
f->state = ngx_http_fastcgi_st_version;
p->upstream_done = 1;
+ if (flcf->keep_conn) {
+ r->upstream->keepalive = 1;
+ }
+
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, p->log, 0,
"http fastcgi sent end request");
@@ -1703,8 +1744,10 @@ ngx_http_fastcgi_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
}
if (p->free) {
- b = p->free->buf;
- p->free = p->free->next;
+ cl = p->free;
+ b = cl->buf;
+ p->free = cl->next;
+ ngx_free_chain(p->pool, cl);
} else {
b = ngx_alloc_buf(p->pool);
@@ -1781,6 +1824,23 @@ ngx_http_fastcgi_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
}
+ if (flcf->keep_conn) {
+
+ /* set p->length, minimal amount of data we want to see */
+
+ if (f->state < ngx_http_fastcgi_st_data) {
+ p->length = 1;
+
+ } else if (f->state == ngx_http_fastcgi_st_padding) {
+ p->length = f->padding;
+
+ } else {
+ /* ngx_http_fastcgi_st_data */
+
+ p->length = f->length;
+ }
+ }
+
if (b) {
b->shadow = buf;
b->last_shadow = 1;
@@ -2011,6 +2071,8 @@ ngx_http_fastcgi_create_loc_conf(ngx_conf_t *cf)
conf->catch_stderr = NGX_CONF_UNSET_PTR;
+ conf->keep_conn = NGX_CONF_UNSET;
+
ngx_str_set(&conf->upstream.module, "fastcgi");
return conf;
@@ -2254,6 +2316,8 @@ ngx_http_fastcgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_conf_merge_ptr_value(conf->catch_stderr, prev->catch_stderr, NULL);
+ ngx_conf_merge_value(conf->keep_conn, prev->keep_conn, 0);
+
ngx_conf_merge_str_value(conf->index, prev->index, "");
diff --git a/src/http/modules/ngx_http_gzip_filter_module.c b/src/http/modules/ngx_http_gzip_filter_module.c
index d624e36ff..18824059a 100644
--- a/src/http/modules/ngx_http_gzip_filter_module.c
+++ b/src/http/modules/ngx_http_gzip_filter_module.c
@@ -378,7 +378,7 @@ ngx_http_gzip_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
cl = NULL;
- ngx_chain_update_chains(&ctx->free, &ctx->busy, &cl,
+ ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &cl,
(ngx_buf_tag_t) &ngx_http_gzip_filter_module);
ctx->nomem = 0;
}
@@ -448,7 +448,7 @@ ngx_http_gzip_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
ngx_http_gzip_filter_free_copy_buf(r, ctx);
- ngx_chain_update_chains(&ctx->free, &ctx->busy, &ctx->out,
+ ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &ctx->out,
(ngx_buf_tag_t) &ngx_http_gzip_filter_module);
ctx->last_out = &ctx->out;
diff --git a/src/http/modules/ngx_http_headers_filter_module.c b/src/http/modules/ngx_http_headers_filter_module.c
index 1d409582c..c3f74ae19 100644
--- a/src/http/modules/ngx_http_headers_filter_module.c
+++ b/src/http/modules/ngx_http_headers_filter_module.c
@@ -507,7 +507,7 @@ ngx_http_headers_expires(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
minus = 0;
if (hcf->expires == NGX_HTTP_EXPIRES_MODIFIED) {
- return "daily time can not be used with \"modified\" parameter";
+ return "daily time cannot be used with \"modified\" parameter";
}
hcf->expires = NGX_HTTP_EXPIRES_DAILY;
diff --git a/src/http/modules/ngx_http_log_module.c b/src/http/modules/ngx_http_log_module.c
index b2bfbf477..9728d5411 100644
--- a/src/http/modules/ngx_http_log_module.c
+++ b/src/http/modules/ngx_http_log_module.c
@@ -960,7 +960,7 @@ buffer:
if (log->script) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
- "buffered logs can not have variables in name");
+ "buffered logs cannot have variables in name");
return NGX_CONF_ERROR;
}
diff --git a/src/http/modules/ngx_http_memcached_module.c b/src/http/modules/ngx_http_memcached_module.c
index 745f73aa5..dbe0fe838 100644
--- a/src/http/modules/ngx_http_memcached_module.c
+++ b/src/http/modules/ngx_http_memcached_module.c
@@ -344,8 +344,8 @@ found:
while (*p && *p++ != CR) { /* void */ }
- r->headers_out.content_length_n = ngx_atoof(len, p - len - 1);
- if (r->headers_out.content_length_n == -1) {
+ u->headers_in.content_length_n = ngx_atoof(len, p - len - 1);
+ if (u->headers_in.content_length_n == -1) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"memcached sent invalid length in response \"%V\" "
"for key \"%V\"",
@@ -366,6 +366,7 @@ found:
u->headers_in.status_n = 404;
u->state->status = 404;
+ u->keepalive = 1;
return NGX_OK;
}
@@ -407,7 +408,7 @@ ngx_http_memcached_filter(void *data, ssize_t bytes)
u = ctx->request->upstream;
b = &u->buffer;
- if (u->length == ctx->rest) {
+ if (u->length == (ssize_t) ctx->rest) {
if (ngx_strncmp(b->last,
ngx_http_memcached_end + NGX_HTTP_MEMCACHED_END - ctx->rest,
@@ -426,6 +427,10 @@ ngx_http_memcached_filter(void *data, ssize_t bytes)
u->length -= bytes;
ctx->rest -= bytes;
+ if (u->length == 0) {
+ u->keepalive = 1;
+ }
+
return NGX_OK;
}
@@ -463,6 +468,13 @@ ngx_http_memcached_filter(void *data, ssize_t bytes)
if (ngx_strncmp(last, ngx_http_memcached_end, b->last - last) != 0) {
ngx_log_error(NGX_LOG_ERR, ctx->request->connection->log, 0,
"memcached sent invalid trailer");
+
+ b->last = last;
+ cl->buf->last = last;
+ u->length = 0;
+ ctx->rest = 0;
+
+ return NGX_OK;
}
ctx->rest -= b->last - last;
@@ -470,6 +482,10 @@ ngx_http_memcached_filter(void *data, ssize_t bytes)
cl->buf->last = last;
u->length = ctx->rest;
+ if (u->length == 0) {
+ u->keepalive = 1;
+ }
+
return NGX_OK;
}
diff --git a/src/http/modules/ngx_http_mp4_module.c b/src/http/modules/ngx_http_mp4_module.c
index fc21ec410..9a3362e8d 100644
--- a/src/http/modules/ngx_http_mp4_module.c
+++ b/src/http/modules/ngx_http_mp4_module.c
@@ -32,8 +32,10 @@
#define NGX_HTTP_MP4_STSZ_DATA 21
#define NGX_HTTP_MP4_STCO_ATOM 22
#define NGX_HTTP_MP4_STCO_DATA 23
+#define NGX_HTTP_MP4_CO64_ATOM 24
+#define NGX_HTTP_MP4_CO64_DATA 25
-#define NGX_HTTP_MP4_LAST_ATOM NGX_HTTP_MP4_STCO_DATA
+#define NGX_HTTP_MP4_LAST_ATOM NGX_HTTP_MP4_CO64_DATA
typedef struct {
@@ -61,7 +63,7 @@ typedef struct {
ngx_uint_t start_sample;
ngx_uint_t start_chunk;
ngx_uint_t chunk_samples;
- ngx_uint_t chunk_samples_size;
+ uint64_t chunk_samples_size;
off_t start_offset;
size_t tkhd_size;
@@ -96,8 +98,10 @@ typedef struct {
ngx_buf_t stsc_data_buf;
ngx_buf_t stsz_atom_buf;
ngx_buf_t stsz_data_buf;
- ngx_buf_t tsco_atom_buf;
- ngx_buf_t tsco_data_buf;
+ ngx_buf_t stco_atom_buf;
+ ngx_buf_t stco_data_buf;
+ ngx_buf_t co64_atom_buf;
+ ngx_buf_t co64_data_buf;
ngx_mp4_stsc_entry_t stsc_chunk_entry;
} ngx_http_mp4_trak_t;
@@ -268,6 +272,12 @@ static ngx_int_t ngx_http_mp4_update_stco_atom(ngx_http_mp4_file_t *mp4,
ngx_http_mp4_trak_t *trak);
static void ngx_http_mp4_adjust_stco_atom(ngx_http_mp4_file_t *mp4,
ngx_http_mp4_trak_t *trak, int32_t adjustment);
+static ngx_int_t ngx_http_mp4_read_co64_atom(ngx_http_mp4_file_t *mp4,
+ uint64_t atom_data_size);
+static ngx_int_t ngx_http_mp4_update_co64_atom(ngx_http_mp4_file_t *mp4,
+ ngx_http_mp4_trak_t *trak);
+static void ngx_http_mp4_adjust_co64_atom(ngx_http_mp4_file_t *mp4,
+ ngx_http_mp4_trak_t *trak, off_t adjustment);
static char *ngx_http_mp4(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static void *ngx_http_mp4_create_conf(ngx_conf_t *cf);
static char *ngx_http_mp4_merge_conf(ngx_conf_t *cf, void *parent, void *child);
@@ -373,6 +383,7 @@ static ngx_http_mp4_atom_handler_t ngx_http_mp4_stbl_atoms[] = {
{ "stsc", ngx_http_mp4_read_stsc_atom },
{ "stsz", ngx_http_mp4_read_stsz_atom },
{ "stco", ngx_http_mp4_read_stco_atom },
+ { "co64", ngx_http_mp4_read_co64_atom },
{ NULL, NULL }
};
@@ -676,8 +687,15 @@ ngx_http_mp4_process(ngx_http_mp4_file_t *mp4)
ngx_http_mp4_update_stsz_atom(mp4, &trak[i]);
- if (ngx_http_mp4_update_stco_atom(mp4, &trak[i]) != NGX_OK) {
- return NGX_ERROR;
+ if (trak[i].out[NGX_HTTP_MP4_CO64_DATA].buf) {
+ if (ngx_http_mp4_update_co64_atom(mp4, &trak[i]) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ } else {
+ if (ngx_http_mp4_update_stco_atom(mp4, &trak[i]) != NGX_OK) {
+ return NGX_ERROR;
+ }
}
ngx_http_mp4_update_stbl_atom(mp4, &trak[i]);
@@ -721,7 +739,11 @@ ngx_http_mp4_process(ngx_http_mp4_file_t *mp4)
"mp4 adjustment:%D", adjustment);
for (i = 0; i < mp4->trak.nelts; i++) {
- ngx_http_mp4_adjust_stco_atom(mp4, &trak[i], (int32_t) adjustment);
+ if (trak[i].out[NGX_HTTP_MP4_CO64_DATA].buf) {
+ ngx_http_mp4_adjust_co64_atom(mp4, &trak[i], adjustment);
+ } else {
+ ngx_http_mp4_adjust_stco_atom(mp4, &trak[i], (int32_t) adjustment);
+ }
}
return NGX_OK;
@@ -1702,7 +1724,6 @@ ngx_http_mp4_read_stsd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
{
u_char *atom_header, *atom_table;
size_t atom_size;
- uint32_t entries;
ngx_buf_t *atom;
ngx_mp4_stsd_atom_t *stsd_atom;
ngx_http_mp4_trak_t *trak;
@@ -1718,10 +1739,9 @@ ngx_http_mp4_read_stsd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
ngx_mp4_set_32value(stsd_atom->size, atom_size);
ngx_mp4_set_atom_name(stsd_atom, 's', 't', 's', 'd');
- entries = ngx_mp4_get_32value(stsd_atom->entries);
-
if ((uint64_t) (sizeof(ngx_mp4_stsd_atom_t) - sizeof(ngx_mp4_atom_header_t))
- > atom_data_size) {
+ > atom_data_size)
+ {
ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
"\"%s\" mp4 stsd atom too large",
mp4->file.name.data);
@@ -2497,7 +2517,7 @@ ngx_http_mp4_update_stsz_atom(ngx_http_mp4_file_t *mp4,
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
- "chunk samples sizes:%uD", trak->chunk_samples_size);
+ "chunk samples sizes:%uL", trak->chunk_samples_size);
atom_size = sizeof(ngx_mp4_stsz_atom_t) + (data->last - data->pos);
trak->size += atom_size;
@@ -2554,12 +2574,12 @@ ngx_http_mp4_read_stco_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
trak = ngx_mp4_last_trak(mp4);
trak->chunks = entries;
- atom = &trak->tsco_atom_buf;
+ atom = &trak->stco_atom_buf;
atom->temporary = 1;
atom->pos = atom_header;
atom->last = atom_table;
- data = &trak->tsco_data_buf;
+ data = &trak->stco_data_buf;
data->temporary = 1;
data->pos = atom_table;
data->last = atom_end;
@@ -2648,6 +2668,142 @@ ngx_http_mp4_adjust_stco_atom(ngx_http_mp4_file_t *mp4,
}
+typedef struct {
+ u_char size[4];
+ u_char name[4];
+ u_char version[1];
+ u_char flags[3];
+ u_char entries[4];
+} ngx_mp4_co64_atom_t;
+
+
+static ngx_int_t
+ngx_http_mp4_read_co64_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
+{
+ u_char *atom_header, *atom_table, *atom_end;
+ uint32_t entries;
+ ngx_buf_t *atom, *data;
+ ngx_mp4_co64_atom_t *co64_atom;
+ ngx_http_mp4_trak_t *trak;
+
+ /* chunk offsets atom */
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 co64 atom");
+
+ atom_header = ngx_mp4_atom_header(mp4);
+ co64_atom = (ngx_mp4_co64_atom_t *) atom_header;
+ ngx_mp4_set_atom_name(co64_atom, 'c', 'o', '6', '4');
+
+ entries = ngx_mp4_get_32value(co64_atom->entries);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "chunks:%uD", entries);
+
+ atom_table = atom_header + sizeof(ngx_mp4_co64_atom_t);
+ atom_end = atom_table + entries * sizeof(uint64_t);
+
+ if ((uint64_t) (atom_end - co64_atom->version) > atom_data_size) {
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
+ "\"%s\" mp4 co64 atom too large", mp4->file.name.data);
+ return NGX_ERROR;
+ }
+
+ trak = ngx_mp4_last_trak(mp4);
+ trak->chunks = entries;
+
+ atom = &trak->co64_atom_buf;
+ atom->temporary = 1;
+ atom->pos = atom_header;
+ atom->last = atom_table;
+
+ data = &trak->co64_data_buf;
+ data->temporary = 1;
+ data->pos = atom_table;
+ data->last = atom_end;
+
+ trak->out[NGX_HTTP_MP4_CO64_ATOM].buf = atom;
+ trak->out[NGX_HTTP_MP4_CO64_DATA].buf = data;
+
+ ngx_mp4_atom_next(mp4, atom_data_size);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_mp4_update_co64_atom(ngx_http_mp4_file_t *mp4,
+ ngx_http_mp4_trak_t *trak)
+{
+ size_t atom_size;
+ ngx_buf_t *atom, *data;
+ ngx_mp4_co64_atom_t *co64_atom;
+
+ /*
+ * mdia.minf.stbl.co64 updating requires trak->start_chunk
+ * from mdia.minf.stbl.stsc which depends on value from mdia.mdhd
+ * atom which may reside after mdia.minf
+ */
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "mp4 co64 atom update");
+
+ data = trak->out[NGX_HTTP_MP4_CO64_DATA].buf;
+
+ if (data == NULL) {
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
+ "no mp4 co64 atoms were found in \"%s\"",
+ mp4->file.name.data);
+ return NGX_ERROR;
+ }
+
+ data->pos += trak->start_chunk * sizeof(uint64_t);
+ atom_size = sizeof(ngx_mp4_co64_atom_t) + (data->last - data->pos);
+ trak->size += atom_size;
+
+ trak->start_offset = ngx_mp4_get_64value(data->pos);
+ trak->start_offset += trak->chunk_samples_size;
+ ngx_mp4_set_64value(data->pos, trak->start_offset);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "start chunk offset:%uL", trak->start_offset);
+
+ atom = trak->out[NGX_HTTP_MP4_CO64_ATOM].buf;
+ co64_atom = (ngx_mp4_co64_atom_t *) atom->pos;
+
+ ngx_mp4_set_32value(co64_atom->size, atom_size);
+ ngx_mp4_set_32value(co64_atom->entries, trak->chunks - trak->start_chunk);
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_http_mp4_adjust_co64_atom(ngx_http_mp4_file_t *mp4,
+ ngx_http_mp4_trak_t *trak, off_t adjustment)
+{
+ uint64_t offset, *entry, *end;
+ ngx_buf_t *data;
+
+ /*
+ * moov.trak.mdia.minf.stbl.co64 adjustment requires
+ * minimal start offset of all traks and new moov atom size
+ */
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "mp4 co64 atom adjustment");
+
+ data = trak->out[NGX_HTTP_MP4_CO64_DATA].buf;
+ entry = (uint64_t *) data->pos;
+ end = (uint64_t *) data->last;
+
+ while (entry < end) {
+ offset = ngx_mp4_get_64value(entry);
+ offset += adjustment;
+ ngx_mp4_set_64value(entry, offset);
+ entry++;
+ }
+}
+
+
static char *
ngx_http_mp4(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
diff --git a/src/http/modules/ngx_http_proxy_module.c b/src/http/modules/ngx_http_proxy_module.c
index 902cfb8f2..88ee42472 100644
--- a/src/http/modules/ngx_http_proxy_module.c
+++ b/src/http/modules/ngx_http_proxy_module.c
@@ -71,6 +71,8 @@ typedef struct {
ngx_flag_t redirect;
+ ngx_uint_t http_version;
+
ngx_uint_t headers_hash_max_size;
ngx_uint_t headers_hash_bucket_size;
} ngx_http_proxy_loc_conf_t;
@@ -80,6 +82,12 @@ typedef struct {
ngx_http_status_t status;
ngx_http_proxy_vars_t vars;
size_t internal_body_length;
+
+ ngx_uint_t state;
+ off_t size;
+ off_t length;
+
+ ngx_uint_t head; /* unsigned head:1 */
} ngx_http_proxy_ctx_t;
@@ -92,6 +100,15 @@ static ngx_int_t ngx_http_proxy_create_request(ngx_http_request_t *r);
static ngx_int_t ngx_http_proxy_reinit_request(ngx_http_request_t *r);
static ngx_int_t ngx_http_proxy_process_status_line(ngx_http_request_t *r);
static ngx_int_t ngx_http_proxy_process_header(ngx_http_request_t *r);
+static ngx_int_t ngx_http_proxy_input_filter_init(void *data);
+static ngx_int_t ngx_http_proxy_copy_filter(ngx_event_pipe_t *p,
+ ngx_buf_t *buf);
+static ngx_int_t ngx_http_proxy_chunked_filter(ngx_event_pipe_t *p,
+ ngx_buf_t *buf);
+static ngx_int_t ngx_http_proxy_non_buffered_copy_filter(void *data,
+ ssize_t bytes);
+static ngx_int_t ngx_http_proxy_non_buffered_chunked_filter(void *data,
+ ssize_t bytes);
static void ngx_http_proxy_abort_request(ngx_http_request_t *r);
static void ngx_http_proxy_finalize_request(ngx_http_request_t *r,
ngx_int_t rc);
@@ -157,6 +174,13 @@ static ngx_conf_bitmask_t ngx_http_proxy_next_upstream_masks[] = {
};
+static ngx_conf_enum_t ngx_http_proxy_http_version[] = {
+ { ngx_string("1.0"), NGX_HTTP_VERSION_10 },
+ { ngx_string("1.1"), NGX_HTTP_VERSION_11 },
+ { ngx_null_string, 0 }
+};
+
+
ngx_module_t ngx_http_proxy_module;
@@ -432,6 +456,13 @@ static ngx_command_t ngx_http_proxy_commands[] = {
offsetof(ngx_http_proxy_loc_conf_t, upstream.ignore_headers),
&ngx_http_upstream_ignore_headers_masks },
+ { ngx_string("proxy_http_version"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_enum_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_proxy_loc_conf_t, http_version),
+ &ngx_http_proxy_http_version },
+
#if (NGX_HTTP_SSL)
{ ngx_string("proxy_ssl_session_reuse"),
@@ -479,6 +510,7 @@ ngx_module_t ngx_http_proxy_module = {
static char ngx_http_proxy_version[] = " HTTP/1.0" CRLF;
+static char ngx_http_proxy_version_11[] = " HTTP/1.1" CRLF;
static ngx_keyval_t ngx_http_proxy_headers[] = {
@@ -486,6 +518,7 @@ static ngx_keyval_t ngx_http_proxy_headers[] = {
{ ngx_string("Connection"), ngx_string("close") },
{ ngx_string("Keep-Alive"), ngx_string("") },
{ ngx_string("Expect"), ngx_string("") },
+ { ngx_string("Upgrade"), ngx_string("") },
{ ngx_null_string, ngx_null_string }
};
@@ -610,7 +643,12 @@ ngx_http_proxy_handler(ngx_http_request_t *r)
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
- u->pipe->input_filter = ngx_event_pipe_copy_input_filter;
+ u->pipe->input_filter = ngx_http_proxy_copy_filter;
+ u->pipe->input_ctx = r;
+
+ u->input_filter_init = ngx_http_proxy_input_filter_init;
+ u->input_filter = ngx_http_proxy_non_buffered_copy_filter;
+ u->input_filter_ctx = r;
u->accel = 1;
@@ -866,14 +904,20 @@ ngx_http_proxy_create_request(ngx_http_request_t *r)
method.len++;
}
+ ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
+
+ if (method.len == 5
+ && ngx_strncasecmp(method.data, (u_char *) "HEAD ", 5) == 0)
+ {
+ ctx->head = 1;
+ }
+
len = method.len + sizeof(ngx_http_proxy_version) - 1 + sizeof(CRLF) - 1;
escape = 0;
loc_len = 0;
unparsed_uri = 0;
- ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
-
if (plcf->proxy_lengths) {
uri_len = ctx->vars.uri.len;
@@ -1009,8 +1053,14 @@ ngx_http_proxy_create_request(ngx_http_request_t *r)
u->uri.len = b->last - u->uri.data;
- b->last = ngx_cpymem(b->last, ngx_http_proxy_version,
- sizeof(ngx_http_proxy_version) - 1);
+ if (plcf->http_version == NGX_HTTP_VERSION_11) {
+ b->last = ngx_cpymem(b->last, ngx_http_proxy_version_11,
+ sizeof(ngx_http_proxy_version_11) - 1);
+
+ } else {
+ b->last = ngx_cpymem(b->last, ngx_http_proxy_version,
+ sizeof(ngx_http_proxy_version) - 1);
+ }
ngx_memzero(&e, sizeof(ngx_http_script_engine_t));
@@ -1158,8 +1208,11 @@ ngx_http_proxy_reinit_request(ngx_http_request_t *r)
ctx->status.count = 0;
ctx->status.start = NULL;
ctx->status.end = NULL;
+ ctx->state = 0;
r->upstream->process_header = ngx_http_proxy_process_status_line;
+ r->upstream->pipe->input_filter = ngx_http_proxy_copy_filter;
+ r->upstream->input_filter = ngx_http_proxy_non_buffered_copy_filter;
r->state = 0;
return NGX_OK;
@@ -1210,6 +1263,7 @@ ngx_http_proxy_process_status_line(ngx_http_request_t *r)
r->http_version = NGX_HTTP_VERSION_9;
u->state->status = NGX_HTTP_OK;
+ u->headers_in.connection_close = 1;
return NGX_OK;
}
@@ -1234,6 +1288,10 @@ ngx_http_proxy_process_status_line(ngx_http_request_t *r)
"http proxy status %ui \"%V\"",
u->headers_in.status_n, &u->headers_in.status_line);
+ if (ctx->status.http_version < NGX_HTTP_VERSION_11) {
+ u->headers_in.connection_close = 1;
+ }
+
u->process_header = ngx_http_proxy_process_header;
return ngx_http_proxy_process_header(r);
@@ -1245,6 +1303,8 @@ ngx_http_proxy_process_header(ngx_http_request_t *r)
{
ngx_int_t rc;
ngx_table_elt_t *h;
+ ngx_http_upstream_t *u;
+ ngx_http_proxy_ctx_t *ctx;
ngx_http_upstream_header_t *hh;
ngx_http_upstream_main_conf_t *umcf;
@@ -1340,6 +1400,30 @@ ngx_http_proxy_process_header(ngx_http_request_t *r)
h->lowcase_key = (u_char *) "date";
}
+ /* clear content length if response is chunked */
+
+ u = r->upstream;
+
+ if (u->headers_in.chunked) {
+ u->headers_in.content_length_n = -1;
+ }
+
+ /*
+ * set u->keepalive if response has no body; this allows to keep
+ * connections alive in case of r->header_only or X-Accel-Redirect
+ */
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
+
+ if (u->headers_in.status_n == NGX_HTTP_NO_CONTENT
+ || u->headers_in.status_n == NGX_HTTP_NOT_MODIFIED
+ || ctx->head
+ || (!u->headers_in.chunked
+ && u->headers_in.content_length_n == 0))
+ {
+ u->keepalive = !u->headers_in.connection_close;
+ }
+
return NGX_OK;
}
@@ -1357,6 +1441,690 @@ ngx_http_proxy_process_header(ngx_http_request_t *r)
}
+static ngx_int_t
+ngx_http_proxy_input_filter_init(void *data)
+{
+ ngx_http_request_t *r = data;
+ ngx_http_upstream_t *u;
+ ngx_http_proxy_ctx_t *ctx;
+
+ u = r->upstream;
+ ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
+
+ ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http proxy filter init s:%d h:%d c:%d l:%O",
+ u->headers_in.status_n, ctx->head, u->headers_in.chunked,
+ u->headers_in.content_length_n);
+
+ /* as per RFC2616, 4.4 Message Length */
+
+ if (u->headers_in.status_n == NGX_HTTP_NO_CONTENT
+ || u->headers_in.status_n == NGX_HTTP_NOT_MODIFIED
+ || ctx->head)
+ {
+ /* 1xx, 204, and 304 and replies to HEAD requests */
+ /* no 1xx since we don't send Expect and Upgrade */
+
+ u->pipe->length = 0;
+ u->length = 0;
+ u->keepalive = !u->headers_in.connection_close;
+
+ } else if (u->headers_in.chunked) {
+ /* chunked */
+
+ u->pipe->input_filter = ngx_http_proxy_chunked_filter;
+ u->pipe->length = 3; /* "0" LF LF */
+
+ u->input_filter = ngx_http_proxy_non_buffered_chunked_filter;
+ u->length = -1;
+
+ } else if (u->headers_in.content_length_n == 0) {
+ /* empty body: special case as filter won't be called */
+
+ u->pipe->length = 0;
+ u->length = 0;
+ u->keepalive = !u->headers_in.connection_close;
+
+ } else {
+ /* content length or connection close */
+
+ u->pipe->length = u->headers_in.content_length_n;
+ u->length = u->headers_in.content_length_n;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_proxy_copy_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
+{
+ ngx_buf_t *b;
+ ngx_chain_t *cl;
+ ngx_http_request_t *r;
+
+ if (buf->pos == buf->last) {
+ return NGX_OK;
+ }
+
+ if (p->free) {
+ cl = p->free;
+ b = cl->buf;
+ p->free = cl->next;
+ ngx_free_chain(p->pool, cl);
+
+ } else {
+ b = ngx_alloc_buf(p->pool);
+ if (b == NULL) {
+ return NGX_ERROR;
+ }
+ }
+
+ ngx_memcpy(b, buf, sizeof(ngx_buf_t));
+ b->shadow = buf;
+ b->tag = p->tag;
+ b->last_shadow = 1;
+ b->recycled = 1;
+ buf->shadow = b;
+
+ cl = ngx_alloc_chain_link(p->pool);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl->buf = b;
+ cl->next = NULL;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0, "input buf #%d", b->num);
+
+ if (p->in) {
+ *p->last_in = cl;
+ } else {
+ p->in = cl;
+ }
+ p->last_in = &cl->next;
+
+ if (p->length == -1) {
+ return NGX_OK;
+ }
+
+ p->length -= b->last - b->pos;
+
+ if (p->length == 0) {
+ r = p->input_ctx;
+ p->upstream_done = 1;
+ r->upstream->keepalive = !r->upstream->headers_in.connection_close;
+
+ } else if (p->length < 0) {
+ r = p->input_ctx;
+ p->upstream_done = 1;
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "upstream sent too many data");
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_inline ngx_int_t
+ngx_http_proxy_parse_chunked(ngx_http_request_t *r, ngx_buf_t *buf)
+{
+ u_char *pos, ch, c;
+ ngx_int_t rc;
+ ngx_http_proxy_ctx_t *ctx;
+ enum {
+ sw_chunk_start = 0,
+ sw_chunk_size,
+ sw_chunk_extension,
+ sw_chunk_extension_almost_done,
+ sw_chunk_data,
+ sw_after_data,
+ sw_after_data_almost_done,
+ sw_last_chunk_extension,
+ sw_last_chunk_extension_almost_done,
+ sw_trailer,
+ sw_trailer_almost_done,
+ sw_trailer_header,
+ sw_trailer_header_almost_done
+ } state;
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
+ state = ctx->state;
+
+ if (state == sw_chunk_data && ctx->size == 0) {
+ state = sw_after_data;
+ }
+
+ rc = NGX_AGAIN;
+
+ for (pos = buf->pos; pos < buf->last; pos++) {
+
+ ch = *pos;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http proxy chunked byte: %02Xd s:%d", ch, state);
+
+ switch (state) {
+
+ case sw_chunk_start:
+ if (ch >= '0' && ch <= '9') {
+ state = sw_chunk_size;
+ ctx->size = ch - '0';
+ break;
+ }
+
+ c = (u_char) (ch | 0x20);
+
+ if (c >= 'a' && c <= 'f') {
+ state = sw_chunk_size;
+ ctx->size = c - 'a' + 10;
+ break;
+ }
+
+ goto invalid;
+
+ case sw_chunk_size:
+ if (ch >= '0' && ch <= '9') {
+ ctx->size = ctx->size * 16 + (ch - '0');
+ break;
+ }
+
+ c = (u_char) (ch | 0x20);
+
+ if (c >= 'a' && c <= 'f') {
+ ctx->size = ctx->size * 16 + (c - 'a' + 10);
+ break;
+ }
+
+ if (ctx->size == 0) {
+
+ switch (ch) {
+ case CR:
+ state = sw_last_chunk_extension_almost_done;
+ break;
+ case LF:
+ state = sw_trailer;
+ break;
+ case ';':
+ state = sw_last_chunk_extension;
+ break;
+ default:
+ goto invalid;
+ }
+
+ break;
+ }
+
+ switch (ch) {
+ case CR:
+ state = sw_chunk_extension_almost_done;
+ break;
+ case LF:
+ state = sw_chunk_data;
+ break;
+ case ';':
+ state = sw_chunk_extension;
+ break;
+ default:
+ goto invalid;
+ }
+
+ break;
+
+ case sw_chunk_extension:
+ switch (ch) {
+ case CR:
+ state = sw_chunk_extension_almost_done;
+ break;
+ case LF:
+ state = sw_chunk_data;
+ }
+ break;
+
+ case sw_chunk_extension_almost_done:
+ if (ch == LF) {
+ state = sw_chunk_data;
+ break;
+ }
+ goto invalid;
+
+ case sw_chunk_data:
+ rc = NGX_OK;
+ goto data;
+
+ case sw_after_data:
+ switch (ch) {
+ case CR:
+ state = sw_after_data_almost_done;
+ break;
+ case LF:
+ state = sw_chunk_start;
+ }
+ break;
+
+ case sw_after_data_almost_done:
+ if (ch == LF) {
+ state = sw_chunk_start;
+ break;
+ }
+ goto invalid;
+
+ case sw_last_chunk_extension:
+ switch (ch) {
+ case CR:
+ state = sw_last_chunk_extension_almost_done;
+ break;
+ case LF:
+ state = sw_trailer;
+ }
+ break;
+
+ case sw_last_chunk_extension_almost_done:
+ if (ch == LF) {
+ state = sw_trailer;
+ break;
+ }
+ goto invalid;
+
+ case sw_trailer:
+ switch (ch) {
+ case CR:
+ state = sw_trailer_almost_done;
+ break;
+ case LF:
+ goto done;
+ default:
+ state = sw_trailer_header;
+ }
+ break;
+
+ case sw_trailer_almost_done:
+ if (ch == LF) {
+ goto done;
+ }
+ goto invalid;
+
+ case sw_trailer_header:
+ switch (ch) {
+ case CR:
+ state = sw_trailer_header_almost_done;
+ break;
+ case LF:
+ state = sw_trailer;
+ }
+ break;
+
+ case sw_trailer_header_almost_done:
+ if (ch == LF) {
+ state = sw_trailer;
+ break;
+ }
+ goto invalid;
+
+ }
+ }
+
+data:
+
+ ctx->state = state;
+ buf->pos = pos;
+
+ switch (state) {
+
+ case sw_chunk_start:
+ ctx->length = 3 /* "0" LF LF */;
+ break;
+ case sw_chunk_size:
+ ctx->length = 2 /* LF LF */
+ + (ctx->size ? ctx->size + 4 /* LF "0" LF LF */ : 0);
+ break;
+ case sw_chunk_extension:
+ case sw_chunk_extension_almost_done:
+ ctx->length = 1 /* LF */ + ctx->size + 4 /* LF "0" LF LF */;
+ break;
+ case sw_chunk_data:
+ ctx->length = ctx->size + 4 /* LF "0" LF LF */;
+ break;
+ case sw_after_data:
+ case sw_after_data_almost_done:
+ ctx->length = 4 /* LF "0" LF LF */;
+ break;
+ case sw_last_chunk_extension:
+ case sw_last_chunk_extension_almost_done:
+ ctx->length = 2 /* LF LF */;
+ break;
+ case sw_trailer:
+ case sw_trailer_almost_done:
+ ctx->length = 1 /* LF */;
+ break;
+ case sw_trailer_header:
+ case sw_trailer_header_almost_done:
+ ctx->length = 2 /* LF LF */;
+ break;
+
+ }
+
+ return rc;
+
+done:
+
+ return NGX_DONE;
+
+invalid:
+
+ ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+ "upstream sent invalid chunked response");
+
+ return NGX_ERROR;
+}
+
+
+static ngx_int_t
+ngx_http_proxy_chunked_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
+{
+ ngx_int_t rc;
+ ngx_buf_t *b, **prev;
+ ngx_chain_t *cl;
+ ngx_http_request_t *r;
+ ngx_http_proxy_ctx_t *ctx;
+
+ if (buf->pos == buf->last) {
+ return NGX_OK;
+ }
+
+ r = p->input_ctx;
+ ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
+
+ b = NULL;
+ prev = &buf->shadow;
+
+ for ( ;; ) {
+
+ rc = ngx_http_proxy_parse_chunked(r, buf);
+
+ if (rc == NGX_OK) {
+
+ /* a chunk has been parsed successfully */
+
+ if (p->free) {
+ cl = p->free;
+ b = cl->buf;
+ p->free = cl->next;
+ ngx_free_chain(p->pool, cl);
+
+ } else {
+ b = ngx_alloc_buf(p->pool);
+ if (b == NULL) {
+ return NGX_ERROR;
+ }
+ }
+
+ ngx_memzero(b, sizeof(ngx_buf_t));
+
+ b->pos = buf->pos;
+ b->start = buf->start;
+ b->end = buf->end;
+ b->tag = p->tag;
+ b->temporary = 1;
+ b->recycled = 1;
+
+ *prev = b;
+ prev = &b->shadow;
+
+ cl = ngx_alloc_chain_link(p->pool);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl->buf = b;
+ cl->next = NULL;
+
+ if (p->in) {
+ *p->last_in = cl;
+ } else {
+ p->in = cl;
+ }
+ p->last_in = &cl->next;
+
+ /* STUB */ b->num = buf->num;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, p->log, 0,
+ "input buf #%d %p", b->num, b->pos);
+
+ if (buf->last - buf->pos >= ctx->size) {
+
+ buf->pos += ctx->size;
+ b->last = buf->pos;
+ ctx->size = 0;
+
+ continue;
+ }
+
+ ctx->size -= buf->last - buf->pos;
+ buf->pos = buf->last;
+ b->last = buf->last;
+
+ continue;
+ }
+
+ if (rc == NGX_DONE) {
+
+ /* a whole response has been parsed successfully */
+
+ p->upstream_done = 1;
+ r->upstream->keepalive = !r->upstream->headers_in.connection_close;
+
+ break;
+ }
+
+ if (rc == NGX_AGAIN) {
+
+ /* set p->length, minimal amount of data we want to see */
+
+ p->length = ctx->length;
+
+ break;
+ }
+
+ /* invalid response */
+
+ ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+ "upstream sent invalid chunked response");
+
+ return NGX_ERROR;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http proxy chunked state %d, length %d",
+ ctx->state, p->length);
+
+ if (b) {
+ b->shadow = buf;
+ b->last_shadow = 1;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, p->log, 0,
+ "input buf %p %z", b->pos, b->last - b->pos);
+
+ return NGX_OK;
+ }
+
+ /* there is no data record in the buf, add it to free chain */
+
+ if (ngx_event_pipe_add_free_buf(p, buf) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_proxy_non_buffered_copy_filter(void *data, ssize_t bytes)
+{
+ ngx_http_request_t *r = data;
+
+ ngx_buf_t *b;
+ ngx_chain_t *cl, **ll;
+ ngx_http_upstream_t *u;
+
+ u = r->upstream;
+
+ for (cl = u->out_bufs, ll = &u->out_bufs; cl; cl = cl->next) {
+ ll = &cl->next;
+ }
+
+ cl = ngx_chain_get_free_buf(r->pool, &u->free_bufs);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ *ll = cl;
+
+ cl->buf->flush = 1;
+ cl->buf->memory = 1;
+
+ b = &u->buffer;
+
+ cl->buf->pos = b->last;
+ b->last += bytes;
+ cl->buf->last = b->last;
+ cl->buf->tag = u->output.tag;
+
+ if (u->length == -1) {
+ return NGX_OK;
+ }
+
+ u->length -= bytes;
+
+ if (u->length == 0) {
+ u->keepalive = !u->headers_in.connection_close;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_proxy_non_buffered_chunked_filter(void *data, ssize_t bytes)
+{
+ ngx_http_request_t *r = data;
+
+ ngx_int_t rc;
+ ngx_buf_t *b, *buf;
+ ngx_chain_t *cl, **ll;
+ ngx_http_upstream_t *u;
+ ngx_http_proxy_ctx_t *ctx;
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
+ u = r->upstream;
+ buf = &u->buffer;
+
+ buf->pos = buf->last;
+ buf->last += bytes;
+
+ for (cl = u->out_bufs, ll = &u->out_bufs; cl; cl = cl->next) {
+ ll = &cl->next;
+ }
+
+ for ( ;; ) {
+
+ rc = ngx_http_proxy_parse_chunked(r, buf);
+
+ if (rc == NGX_OK) {
+
+ /* a chunk has been parsed successfully */
+
+ cl = ngx_chain_get_free_buf(r->pool, &u->free_bufs);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ *ll = cl;
+ ll = &cl->next;
+
+ b = cl->buf;
+
+ b->flush = 1;
+ b->memory = 1;
+
+ b->pos = buf->pos;
+ b->tag = u->output.tag;
+
+ if (buf->last - buf->pos >= ctx->size) {
+ buf->pos += ctx->size;
+ b->last = buf->pos;
+ ctx->size = 0;
+
+ } else {
+ ctx->size -= buf->last - buf->pos;
+ buf->pos = buf->last;
+ b->last = buf->last;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http proxy out buf %p %z",
+ b->pos, b->last - b->pos);
+
+ continue;
+ }
+
+ if (rc == NGX_DONE) {
+
+ /* a whole response has been parsed successfully */
+
+ u->keepalive = !u->headers_in.connection_close;
+ u->length = 0;
+
+ break;
+ }
+
+ if (rc == NGX_AGAIN) {
+ break;
+ }
+
+ /* invalid response */
+
+ ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+ "upstream sent invalid chunked response");
+
+ return NGX_ERROR;
+ }
+
+ /* provide continuous buffer for subrequests in memory */
+
+ if (r->subrequest_in_memory) {
+
+ cl = u->out_bufs;
+
+ if (cl) {
+ buf->pos = cl->buf->pos;
+ }
+
+ buf->last = buf->pos;
+
+ for (cl = u->out_bufs; cl; cl = cl->next) {
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http proxy in memory %p-%p %uz",
+ cl->buf->pos, cl->buf->last, ngx_buf_size(cl->buf));
+
+ if (buf->last == cl->buf->pos) {
+ buf->last = cl->buf->last;
+ continue;
+ }
+
+ buf->last = ngx_movemem(buf->last, cl->buf->pos,
+ cl->buf->last - cl->buf->pos);
+
+ cl->buf->pos = buf->last - (cl->buf->last - cl->buf->pos);
+ cl->buf->last = buf->last;
+ }
+ }
+
+ return NGX_OK;
+}
+
+
static void
ngx_http_proxy_abort_request(ngx_http_request_t *r)
{
@@ -1705,6 +2473,8 @@ ngx_http_proxy_create_loc_conf(ngx_conf_t *cf)
conf->redirect = NGX_CONF_UNSET;
conf->upstream.change_buffering = 1;
+ conf->http_version = NGX_CONF_UNSET_UINT;
+
conf->headers_hash_max_size = NGX_CONF_UNSET_UINT;
conf->headers_hash_bucket_size = NGX_CONF_UNSET_UINT;
@@ -2008,6 +2778,9 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
}
#endif
+ ngx_conf_merge_uint_value(conf->http_version, prev->http_version,
+ NGX_HTTP_VERSION_10);
+
ngx_conf_merge_uint_value(conf->headers_hash_max_size,
prev->headers_hash_max_size, 512);
diff --git a/src/http/modules/ngx_http_upstream_keepalive_module.c b/src/http/modules/ngx_http_upstream_keepalive_module.c
new file mode 100644
index 000000000..945f78b15
--- /dev/null
+++ b/src/http/modules/ngx_http_upstream_keepalive_module.c
@@ -0,0 +1,569 @@
+
+/*
+ * Copyright (C) Maxim Dounin
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct {
+ ngx_uint_t max_cached;
+ ngx_uint_t single; /* unsigned:1 */
+
+ ngx_queue_t cache;
+ ngx_queue_t free;
+
+ ngx_http_upstream_init_pt original_init_upstream;
+ ngx_http_upstream_init_peer_pt original_init_peer;
+
+} ngx_http_upstream_keepalive_srv_conf_t;
+
+
+typedef struct {
+ ngx_http_upstream_keepalive_srv_conf_t *conf;
+
+ ngx_http_upstream_t *upstream;
+
+ void *data;
+
+ ngx_event_get_peer_pt original_get_peer;
+ ngx_event_free_peer_pt original_free_peer;
+
+#if (NGX_HTTP_SSL)
+ ngx_event_set_peer_session_pt original_set_session;
+ ngx_event_save_peer_session_pt original_save_session;
+#endif
+
+ ngx_uint_t failed; /* unsigned:1 */
+
+} ngx_http_upstream_keepalive_peer_data_t;
+
+
+typedef struct {
+ ngx_http_upstream_keepalive_srv_conf_t *conf;
+
+ ngx_queue_t queue;
+ ngx_connection_t *connection;
+
+ socklen_t socklen;
+ u_char sockaddr[NGX_SOCKADDRLEN];
+
+} ngx_http_upstream_keepalive_cache_t;
+
+
+static ngx_int_t ngx_http_upstream_init_keepalive_peer(ngx_http_request_t *r,
+ ngx_http_upstream_srv_conf_t *us);
+static ngx_int_t ngx_http_upstream_get_keepalive_peer(ngx_peer_connection_t *pc,
+ void *data);
+static void ngx_http_upstream_free_keepalive_peer(ngx_peer_connection_t *pc,
+ void *data, ngx_uint_t state);
+
+static void ngx_http_upstream_keepalive_dummy_handler(ngx_event_t *ev);
+static void ngx_http_upstream_keepalive_close_handler(ngx_event_t *ev);
+static void ngx_http_upstream_keepalive_close(ngx_connection_t *c);
+
+
+#if (NGX_HTTP_SSL)
+static ngx_int_t ngx_http_upstream_keepalive_set_session(
+ ngx_peer_connection_t *pc, void *data);
+static void ngx_http_upstream_keepalive_save_session(ngx_peer_connection_t *pc,
+ void *data);
+#endif
+
+static void *ngx_http_upstream_keepalive_create_conf(ngx_conf_t *cf);
+static char *ngx_http_upstream_keepalive(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
+
+static ngx_command_t ngx_http_upstream_keepalive_commands[] = {
+
+ { ngx_string("keepalive"),
+ NGX_HTTP_UPS_CONF|NGX_CONF_TAKE12,
+ ngx_http_upstream_keepalive,
+ 0,
+ 0,
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_upstream_keepalive_module_ctx = {
+ NULL, /* preconfiguration */
+ NULL, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ ngx_http_upstream_keepalive_create_conf, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ NULL, /* create location configuration */
+ NULL /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_upstream_keepalive_module = {
+ NGX_MODULE_V1,
+ &ngx_http_upstream_keepalive_module_ctx, /* module context */
+ ngx_http_upstream_keepalive_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_upstream_init_keepalive(ngx_conf_t *cf,
+ ngx_http_upstream_srv_conf_t *us)
+{
+ ngx_uint_t i;
+ ngx_http_upstream_keepalive_srv_conf_t *kcf;
+ ngx_http_upstream_keepalive_cache_t *cached;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cf->log, 0,
+ "init keepalive");
+
+ kcf = ngx_http_conf_upstream_srv_conf(us,
+ ngx_http_upstream_keepalive_module);
+
+ if (kcf->original_init_upstream(cf, us) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ kcf->original_init_peer = us->peer.init;
+
+ us->peer.init = ngx_http_upstream_init_keepalive_peer;
+
+ /* allocate cache items and add to free queue */
+
+ cached = ngx_pcalloc(cf->pool,
+ sizeof(ngx_http_upstream_keepalive_cache_t) * kcf->max_cached);
+ if (cached == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_queue_init(&kcf->cache);
+ ngx_queue_init(&kcf->free);
+
+ for (i = 0; i < kcf->max_cached; i++) {
+ ngx_queue_insert_head(&kcf->free, &cached[i].queue);
+ cached[i].conf = kcf;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_init_keepalive_peer(ngx_http_request_t *r,
+ ngx_http_upstream_srv_conf_t *us)
+{
+ ngx_http_upstream_keepalive_peer_data_t *kp;
+ ngx_http_upstream_keepalive_srv_conf_t *kcf;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "init keepalive peer");
+
+ kcf = ngx_http_conf_upstream_srv_conf(us,
+ ngx_http_upstream_keepalive_module);
+
+ kp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_keepalive_peer_data_t));
+ if (kp == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (kcf->original_init_peer(r, us) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ kp->conf = kcf;
+ kp->upstream = r->upstream;
+ kp->data = r->upstream->peer.data;
+ kp->original_get_peer = r->upstream->peer.get;
+ kp->original_free_peer = r->upstream->peer.free;
+
+ r->upstream->peer.data = kp;
+ r->upstream->peer.get = ngx_http_upstream_get_keepalive_peer;
+ r->upstream->peer.free = ngx_http_upstream_free_keepalive_peer;
+
+#if (NGX_HTTP_SSL)
+ kp->original_set_session = r->upstream->peer.set_session;
+ kp->original_save_session = r->upstream->peer.save_session;
+ r->upstream->peer.set_session = ngx_http_upstream_keepalive_set_session;
+ r->upstream->peer.save_session = ngx_http_upstream_keepalive_save_session;
+#endif
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_get_keepalive_peer(ngx_peer_connection_t *pc, void *data)
+{
+ ngx_http_upstream_keepalive_peer_data_t *kp = data;
+ ngx_http_upstream_keepalive_cache_t *item;
+
+ ngx_int_t rc;
+ ngx_queue_t *q, *cache;
+ ngx_connection_t *c;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,
+ "get keepalive peer");
+
+ kp->failed = 0;
+
+ /* single pool of cached connections */
+
+ if (kp->conf->single && !ngx_queue_empty(&kp->conf->cache)) {
+
+ q = ngx_queue_head(&kp->conf->cache);
+
+ item = ngx_queue_data(q, ngx_http_upstream_keepalive_cache_t, queue);
+ c = item->connection;
+
+ ngx_queue_remove(q);
+ ngx_queue_insert_head(&kp->conf->free, q);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
+ "get keepalive peer: using connection %p", c);
+
+ c->idle = 0;
+ c->log = pc->log;
+ c->read->log = pc->log;
+ c->write->log = pc->log;
+ c->pool->log = pc->log;
+
+ pc->connection = c;
+ pc->cached = 1;
+
+ return NGX_DONE;
+ }
+
+ rc = kp->original_get_peer(pc, kp->data);
+
+ if (kp->conf->single || rc != NGX_OK) {
+ return rc;
+ }
+
+ /* search cache for suitable connection */
+
+ cache = &kp->conf->cache;
+
+ for (q = ngx_queue_head(cache);
+ q != ngx_queue_sentinel(cache);
+ q = ngx_queue_next(q))
+ {
+ item = ngx_queue_data(q, ngx_http_upstream_keepalive_cache_t, queue);
+ c = item->connection;
+
+ if (ngx_memn2cmp((u_char *) &item->sockaddr, (u_char *) pc->sockaddr,
+ item->socklen, pc->socklen)
+ == 0)
+ {
+ ngx_queue_remove(q);
+ ngx_queue_insert_head(&kp->conf->free, q);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
+ "get keepalive peer: using connection %p", c);
+
+ c->idle = 0;
+ c->log = pc->log;
+ c->read->log = pc->log;
+ c->write->log = pc->log;
+ c->pool->log = pc->log;
+
+ pc->connection = c;
+ pc->cached = 1;
+
+ return NGX_DONE;
+ }
+ }
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_http_upstream_free_keepalive_peer(ngx_peer_connection_t *pc, void *data,
+ ngx_uint_t state)
+{
+ ngx_http_upstream_keepalive_peer_data_t *kp = data;
+ ngx_http_upstream_keepalive_cache_t *item;
+
+ ngx_queue_t *q;
+ ngx_connection_t *c;
+ ngx_http_upstream_t *u;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,
+ "free keepalive peer");
+
+ /* remember failed state - peer.free() may be called more than once */
+
+ if (state & NGX_PEER_FAILED) {
+ kp->failed = 1;
+ }
+
+ /* cache valid connections */
+
+ u = kp->upstream;
+ c = pc->connection;
+
+ if (kp->failed
+ || c == NULL
+ || c->read->eof
+ || c->read->error
+ || c->read->timedout
+ || c->write->error
+ || c->write->timedout)
+ {
+ goto invalid;
+ }
+
+ if (!u->keepalive) {
+ goto invalid;
+ }
+
+ if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+ goto invalid;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
+ "free keepalive peer: saving connection %p", c);
+
+ if (ngx_queue_empty(&kp->conf->free)) {
+
+ q = ngx_queue_last(&kp->conf->cache);
+ ngx_queue_remove(q);
+
+ item = ngx_queue_data(q, ngx_http_upstream_keepalive_cache_t, queue);
+
+ ngx_http_upstream_keepalive_close(item->connection);
+
+ } else {
+ q = ngx_queue_head(&kp->conf->free);
+ ngx_queue_remove(q);
+
+ item = ngx_queue_data(q, ngx_http_upstream_keepalive_cache_t, queue);
+ }
+
+ item->connection = c;
+ ngx_queue_insert_head(&kp->conf->cache, q);
+
+ pc->connection = NULL;
+
+ if (c->read->timer_set) {
+ ngx_del_timer(c->read);
+ }
+ if (c->write->timer_set) {
+ ngx_del_timer(c->write);
+ }
+
+ c->write->handler = ngx_http_upstream_keepalive_dummy_handler;
+ c->read->handler = ngx_http_upstream_keepalive_close_handler;
+
+ c->data = item;
+ c->idle = 1;
+ c->log = ngx_cycle->log;
+ c->read->log = ngx_cycle->log;
+ c->write->log = ngx_cycle->log;
+ c->pool->log = ngx_cycle->log;
+
+ item->socklen = pc->socklen;
+ ngx_memcpy(&item->sockaddr, pc->sockaddr, pc->socklen);
+
+ if (c->read->ready) {
+ ngx_http_upstream_keepalive_close_handler(c->read);
+ }
+
+invalid:
+
+ kp->original_free_peer(pc, kp->data, state);
+}
+
+
+static void
+ngx_http_upstream_keepalive_dummy_handler(ngx_event_t *ev)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0,
+ "keepalive dummy handler");
+}
+
+
+static void
+ngx_http_upstream_keepalive_close_handler(ngx_event_t *ev)
+{
+ ngx_http_upstream_keepalive_srv_conf_t *conf;
+ ngx_http_upstream_keepalive_cache_t *item;
+
+ int n;
+ char buf[1];
+ ngx_connection_t *c;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0,
+ "keepalive close handler");
+
+ c = ev->data;
+
+ if (c->close) {
+ goto close;
+ }
+
+ n = recv(c->fd, buf, 1, MSG_PEEK);
+
+ if (n == -1 && ngx_socket_errno == NGX_EAGAIN) {
+ /* stale event */
+
+ if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+ goto close;
+ }
+
+ return;
+ }
+
+close:
+
+ item = c->data;
+ conf = item->conf;
+
+ ngx_http_upstream_keepalive_close(c);
+
+ ngx_queue_remove(&item->queue);
+ ngx_queue_insert_head(&conf->free, &item->queue);
+}
+
+
+static void
+ngx_http_upstream_keepalive_close(ngx_connection_t *c)
+{
+
+#if (NGX_HTTP_SSL)
+
+ if (c->ssl) {
+ c->ssl->no_wait_shutdown = 1;
+ c->ssl->no_send_shutdown = 1;
+
+ if (ngx_ssl_shutdown(c) == NGX_AGAIN) {
+ c->ssl->handler = ngx_http_upstream_keepalive_close;
+ return;
+ }
+ }
+
+#endif
+
+ ngx_destroy_pool(c->pool);
+ ngx_close_connection(c);
+}
+
+
+#if (NGX_HTTP_SSL)
+
+static ngx_int_t
+ngx_http_upstream_keepalive_set_session(ngx_peer_connection_t *pc, void *data)
+{
+ ngx_http_upstream_keepalive_peer_data_t *kp = data;
+
+ return kp->original_set_session(pc, kp->data);
+}
+
+
+static void
+ngx_http_upstream_keepalive_save_session(ngx_peer_connection_t *pc, void *data)
+{
+ ngx_http_upstream_keepalive_peer_data_t *kp = data;
+
+ kp->original_save_session(pc, kp->data);
+ return;
+}
+
+#endif
+
+
+static void *
+ngx_http_upstream_keepalive_create_conf(ngx_conf_t *cf)
+{
+ ngx_http_upstream_keepalive_srv_conf_t *conf;
+
+ conf = ngx_pcalloc(cf->pool,
+ sizeof(ngx_http_upstream_keepalive_srv_conf_t));
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ /*
+ * set by ngx_pcalloc():
+ *
+ * conf->original_init_upstream = NULL;
+ * conf->original_init_peer = NULL;
+ */
+
+ conf->max_cached = 1;
+
+ return conf;
+}
+
+
+static char *
+ngx_http_upstream_keepalive(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_upstream_srv_conf_t *uscf;
+ ngx_http_upstream_keepalive_srv_conf_t *kcf;
+
+ ngx_int_t n;
+ ngx_str_t *value;
+ ngx_uint_t i;
+
+ uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);
+
+ kcf = ngx_http_conf_upstream_srv_conf(uscf,
+ ngx_http_upstream_keepalive_module);
+
+ kcf->original_init_upstream = uscf->peer.init_upstream
+ ? uscf->peer.init_upstream
+ : ngx_http_upstream_init_round_robin;
+
+ uscf->peer.init_upstream = ngx_http_upstream_init_keepalive;
+
+ /* read options */
+
+ value = cf->args->elts;
+
+ n = ngx_atoi(value[1].data, value[1].len);
+
+ if (n == NGX_ERROR || n == 0) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid value \"%V\" in \"%V\" directive",
+ &value[1], &cmd->name);
+ return NGX_CONF_ERROR;
+ }
+
+ kcf->max_cached = n;
+
+ for (i = 2; i < cf->args->nelts; i++) {
+
+ if (ngx_strcmp(value[i].data, "single") == 0) {
+ kcf->single = 1;
+ continue;
+ }
+
+ goto invalid;
+ }
+
+ return NGX_CONF_OK;
+
+invalid:
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid parameter \"%V\"", &value[i]);
+
+ return NGX_CONF_ERROR;
+}
diff --git a/src/http/modules/perl/nginx.pm b/src/http/modules/perl/nginx.pm
index 6dedc6d7d..429af0d1c 100644
--- a/src/http/modules/perl/nginx.pm
+++ b/src/http/modules/perl/nginx.pm
@@ -48,7 +48,7 @@ our @EXPORT = qw(
HTTP_INSUFFICIENT_STORAGE
);
-our $VERSION = '1.1.3';
+our $VERSION = '1.1.4';
require XSLoader;
XSLoader::load('nginx', $VERSION);
diff --git a/src/http/ngx_http.c b/src/http/ngx_http.c
index 5ca9fed3c..e8090453a 100644
--- a/src/http/ngx_http.c
+++ b/src/http/ngx_http.c
@@ -1226,7 +1226,7 @@ ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
#endif
/*
- * we can not compare whole sockaddr struct's as kernel
+ * we cannot compare whole sockaddr struct's as kernel
* may fill some fields in inherited sockaddr struct's
*/
diff --git a/src/http/ngx_http.h b/src/http/ngx_http.h
index e3619e148..3c1a9d275 100644
--- a/src/http/ngx_http.h
+++ b/src/http/ngx_http.h
@@ -52,6 +52,7 @@ struct ngx_http_log_ctx_s {
typedef struct {
+ ngx_uint_t http_version;
ngx_uint_t code;
ngx_uint_t count;
u_char *start;
diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c
index ebe22ceaf..d51dc23d1 100644
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -402,7 +402,7 @@ static ngx_command_t ngx_http_core_commands[] = {
{ ngx_string("sendfile"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
- |NGX_CONF_TAKE1,
+ |NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_core_loc_conf_t, sendfile),
@@ -639,7 +639,7 @@ static ngx_command_t ngx_http_core_commands[] = {
NULL },
{ ngx_string("chunked_transfer_encoding"),
- NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_core_loc_conf_t, chunked_transfer_encoding),
@@ -1259,7 +1259,7 @@ ngx_http_core_try_files_phase(ngx_http_request_t *r,
tf++;
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
- "try to use %s: \"%s\" \"%s\"",
+ "trying to use %s: \"%s\" \"%s\"",
test_dir ? "dir" : "file", name, path.data);
if (tf->lengths == NULL && tf->name.len == 0) {
@@ -1897,7 +1897,7 @@ ngx_http_map_uri_to_path(ngx_http_request_t *r, ngx_str_t *path,
if (alias && !r->valid_location) {
ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
- "\"alias\" could not be used in location \"%V\" "
+ "\"alias\" cannot be used in location \"%V\" "
"where URI was rewritten", &clcf->name);
return NULL;
}
@@ -2468,7 +2468,7 @@ ngx_http_internal_redirect(ngx_http_request_t *r,
if (r->uri_changes == 0) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"rewrite or internal redirection cycle "
- "while internal redirect to \"%V\"", uri);
+ "while internally redirecting to \"%V\"", uri);
r->main->count++;
ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
@@ -2862,7 +2862,7 @@ ngx_http_core_location(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy)
if (pclcf->exact_match) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
- "location \"%V\" could not be inside "
+ "location \"%V\" cannot be inside "
"the exact location \"%V\"",
&clcf->name, &pclcf->name);
return NGX_CONF_ERROR;
@@ -2870,7 +2870,7 @@ ngx_http_core_location(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy)
if (pclcf->named) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
- "location \"%V\" could not be inside "
+ "location \"%V\" cannot be inside "
"the named location \"%V\"",
&clcf->name, &pclcf->name);
return NGX_CONF_ERROR;
@@ -2878,8 +2878,8 @@ ngx_http_core_location(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy)
if (clcf->named) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
- "named location \"%V\" must be "
- "on server level only",
+ "named location \"%V\" can be "
+ "on the server level only",
&clcf->name);
return NGX_CONF_ERROR;
}
@@ -2948,7 +2948,7 @@ ngx_http_core_regex_location(ngx_conf_t *cf, ngx_http_core_loc_conf_t *clcf,
#else
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
- "the using of the regex \"%V\" requires PCRE library",
+ "using regex \"%V\" requires PCRE library",
regex);
return NGX_ERROR;
@@ -3024,9 +3024,9 @@ ngx_http_core_type(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
type[n].value = content_type;
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
- "duplicate extention \"%V\", "
+ "duplicate extension \"%V\", "
"content type: \"%V\", "
- "old content type: \"%V\"",
+ "previous content type: \"%V\"",
&value[i], content_type, old);
continue;
}
@@ -3178,7 +3178,7 @@ ngx_http_core_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
if (conf->large_client_header_buffers.size < conf->connection_pool_size) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"the \"large_client_header_buffers\" size must be "
- "equal to or bigger than \"connection_pool_size\"");
+ "equal to or greater than \"connection_pool_size\"");
return NGX_CONF_ERROR;
}
@@ -3191,7 +3191,7 @@ ngx_http_core_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
prev->underscores_in_headers, 0);
if (conf->server_names.nelts == 0) {
- /* the array has 4 empty preallocated elements, so push can not fail */
+ /* the array has 4 empty preallocated elements, so push cannot fail */
sn = ngx_array_push(&conf->server_names);
#if (NGX_PCRE)
sn->regex = NULL;
@@ -3779,7 +3779,7 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
continue;
#else
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
- "bind ipv6only is not supported "
+ "ipv6only is not supported "
"on this platform");
return NGX_CONF_ERROR;
#endif
@@ -3798,7 +3798,7 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
}
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
- "the invalid \"%V\" parameter", &value[n]);
+ "invalid parameter \"%V\"", &value[n]);
return NGX_CONF_ERROR;
}
@@ -3836,7 +3836,7 @@ ngx_http_core_server_name(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
if (ngx_strchr(value[i].data, '/')) {
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
- "server name \"%V\" has strange symbols",
+ "server name \"%V\" has suspicious symbols",
&value[i]);
}
@@ -3907,7 +3907,7 @@ ngx_http_core_server_name(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
}
#else
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
- "the using of the regex \"%V\" "
+ "using regex \"%V\" "
"requires PCRE library", &value[i]);
return NGX_CONF_ERROR;
@@ -3939,7 +3939,7 @@ ngx_http_core_root(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
} else {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"\"%V\" directive is duplicate, "
- "\"%s\" directive is specified before",
+ "\"%s\" directive was specified earlier",
&cmd->name, clcf->alias ? "alias" : "root");
}
@@ -3948,8 +3948,8 @@ ngx_http_core_root(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
if (clcf->named && alias) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
- "the \"alias\" directive may not be used "
- "inside named location");
+ "the \"alias\" directive cannot be used "
+ "inside the named location");
return NGX_CONF_ERROR;
}
@@ -3960,7 +3960,7 @@ ngx_http_core_root(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|| ngx_strstr(value[1].data, "${document_root}"))
{
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
- "the $document_root variable may not be used "
+ "the $document_root variable cannot be used "
"in the \"%V\" directive",
&cmd->name);
@@ -3971,7 +3971,7 @@ ngx_http_core_root(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|| ngx_strstr(value[1].data, "${realpath_root}"))
{
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
- "the $realpath_root variable may not be used "
+ "the $realpath_root variable cannot be used "
"in the \"%V\" directive",
&cmd->name);
@@ -4430,7 +4430,7 @@ ngx_http_core_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
if (max == 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
- "\"open_file_cache\" must have \"max\" parameter");
+ "\"open_file_cache\" must have the \"max\" parameter");
return NGX_CONF_ERROR;
}
diff --git a/src/http/ngx_http_parse.c b/src/http/ngx_http_parse.c
index 949006b47..a7a4c6ee6 100644
--- a/src/http/ngx_http_parse.c
+++ b/src/http/ngx_http_parse.c
@@ -1403,6 +1403,7 @@ ngx_http_parse_status_line(ngx_http_request_t *r, ngx_buf_t *b,
return NGX_ERROR;
}
+ r->http_major = ch - '0';
state = sw_major_digit;
break;
@@ -1417,6 +1418,7 @@ ngx_http_parse_status_line(ngx_http_request_t *r, ngx_buf_t *b,
return NGX_ERROR;
}
+ r->http_major = r->http_major * 10 + ch - '0';
break;
/* the first digit of minor HTTP version */
@@ -1425,6 +1427,7 @@ ngx_http_parse_status_line(ngx_http_request_t *r, ngx_buf_t *b,
return NGX_ERROR;
}
+ r->http_minor = ch - '0';
state = sw_minor_digit;
break;
@@ -1439,6 +1442,7 @@ ngx_http_parse_status_line(ngx_http_request_t *r, ngx_buf_t *b,
return NGX_ERROR;
}
+ r->http_minor = r->http_minor * 10 + ch - '0';
break;
/* HTTP status code */
@@ -1516,6 +1520,7 @@ done:
status->end = p;
}
+ status->http_version = r->http_major * 1000 + r->http_minor;
r->state = sw_start;
return NGX_OK;
diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c
index 29432dc14..ef16a662c 100644
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -72,6 +72,8 @@ static void ngx_http_upstream_finalize_request(ngx_http_request_t *r,
static ngx_int_t ngx_http_upstream_process_header_line(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_upstream_process_content_length(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t ngx_http_upstream_process_set_cookie(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t
@@ -89,6 +91,11 @@ static ngx_int_t ngx_http_upstream_process_buffering(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t ngx_http_upstream_process_charset(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_upstream_process_connection(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t
+ ngx_http_upstream_process_transfer_encoding(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t ngx_http_upstream_copy_header_line(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t
@@ -96,8 +103,6 @@ static ngx_int_t
ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t ngx_http_upstream_copy_content_type(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
-static ngx_int_t ngx_http_upstream_copy_content_length(ngx_http_request_t *r,
- ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t ngx_http_upstream_copy_last_modified(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t ngx_http_upstream_rewrite_location(ngx_http_request_t *r,
@@ -149,9 +154,9 @@ ngx_http_upstream_header_t ngx_http_upstream_headers_in[] = {
ngx_http_upstream_copy_content_type, 0, 1 },
{ ngx_string("Content-Length"),
- ngx_http_upstream_process_header_line,
+ ngx_http_upstream_process_content_length,
offsetof(ngx_http_upstream_headers_in_t, content_length),
- ngx_http_upstream_copy_content_length, 0, 0 },
+ ngx_http_upstream_ignore_header_line, 0, 0 },
{ ngx_string("Date"),
ngx_http_upstream_process_header_line,
@@ -215,7 +220,7 @@ ngx_http_upstream_header_t ngx_http_upstream_headers_in[] = {
offsetof(ngx_http_headers_out_t, accept_ranges), 1 },
{ ngx_string("Connection"),
- ngx_http_upstream_ignore_header_line, 0,
+ ngx_http_upstream_process_connection, 0,
ngx_http_upstream_ignore_header_line, 0, 0 },
{ ngx_string("Keep-Alive"),
@@ -247,6 +252,10 @@ ngx_http_upstream_header_t ngx_http_upstream_headers_in[] = {
ngx_http_upstream_process_charset, 0,
ngx_http_upstream_copy_header_line, 0, 0 },
+ { ngx_string("Transfer-Encoding"),
+ ngx_http_upstream_process_transfer_encoding, 0,
+ ngx_http_upstream_ignore_header_line, 0, 0 },
+
#if (NGX_HTTP_GZIP)
{ ngx_string("Content-Encoding"),
ngx_http_upstream_process_header_line,
@@ -396,6 +405,8 @@ ngx_http_upstream_create(ngx_http_request_t *r)
r->cache = NULL;
#endif
+ u->headers_in.content_length_n = -1;
+
return NGX_OK;
}
@@ -800,6 +811,7 @@ ngx_http_upstream_cache_send(ngx_http_request_t *r, ngx_http_upstream_t *u)
u->buffer.pos += c->header_start;
ngx_memzero(&u->headers_in, sizeof(ngx_http_upstream_headers_in_t));
+ u->headers_in.content_length_n = -1;
if (ngx_list_init(&u->headers_in.headers, r->pool, 8,
sizeof(ngx_table_elt_t))
@@ -1136,8 +1148,20 @@ ngx_http_upstream_connect(ngx_http_request_t *r, ngx_http_upstream_t *u)
c->sendfile &= r->connection->sendfile;
u->output.sendfile = c->sendfile;
- c->pool = r->pool;
+ if (c->pool == NULL) {
+
+ /* we need separate pool here to be able to cache SSL connections */
+
+ c->pool = ngx_create_pool(128, r->connection->log);
+ if (c->pool == NULL) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+ }
+
c->log = r->connection->log;
+ c->pool->log = c->log;
c->read->log = c->log;
c->write->log = c->log;
@@ -1282,7 +1306,10 @@ ngx_http_upstream_reinit(ngx_http_request_t *r, ngx_http_upstream_t *u)
return NGX_ERROR;
}
+ u->keepalive = 0;
+
ngx_memzero(&u->headers_in, sizeof(ngx_http_upstream_headers_in_t));
+ u->headers_in.content_length_n = -1;
if (ngx_list_init(&u->headers_in.headers, r->pool, 8,
sizeof(ngx_table_elt_t))
@@ -1924,14 +1951,9 @@ ngx_http_upstream_process_headers(ngx_http_request_t *r, ngx_http_upstream_t *u)
r->headers_out.status = u->headers_in.status_n;
r->headers_out.status_line = u->headers_in.status_line;
- u->headers_in.content_length_n = r->headers_out.content_length_n;
-
- if (r->headers_out.content_length_n != -1) {
- u->length = (size_t) r->headers_out.content_length_n;
+ r->headers_out.content_length_n = u->headers_in.content_length_n;
- } else {
- u->length = NGX_MAX_SIZE_T_VALUE;
- }
+ u->length = u->headers_in.content_length_n;
return NGX_OK;
}
@@ -1995,6 +2017,11 @@ ngx_http_upstream_process_body_in_memory(ngx_http_request_t *r,
}
}
+ if (u->length == 0) {
+ ngx_http_upstream_finalize_request(r, u, 0);
+ return;
+ }
+
if (ngx_handle_read_event(rev, 0) != NGX_OK) {
ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
return;
@@ -2115,7 +2142,7 @@ ngx_http_upstream_send_response(ngx_http_request_t *r, ngx_http_upstream_t *u)
return;
}
- if (u->peer.connection->read->ready) {
+ if (u->peer.connection->read->ready || u->length == 0) {
ngx_http_upstream_process_non_buffered_upstream(r, u);
}
}
@@ -2293,6 +2320,15 @@ ngx_http_upstream_send_response(ngx_http_request_t *r, ngx_http_upstream_t *u)
p->send_timeout = clcf->send_timeout;
p->send_lowat = clcf->send_lowat;
+ p->length = -1;
+
+ if (u->input_filter_init
+ && u->input_filter_init(p->input_ctx) != NGX_OK)
+ {
+ ngx_http_upstream_finalize_request(r, u, 0);
+ return;
+ }
+
u->read_event_handler = ngx_http_upstream_process_upstream;
r->write_event_handler = ngx_http_upstream_process_downstream;
@@ -2382,7 +2418,7 @@ ngx_http_upstream_process_non_buffered_request(ngx_http_request_t *r,
return;
}
- ngx_chain_update_chains(&u->free_bufs, &u->busy_bufs,
+ ngx_chain_update_chains(r->pool, &u->free_bufs, &u->busy_bufs,
&u->out_bufs, u->output.tag);
}
@@ -2403,10 +2439,6 @@ ngx_http_upstream_process_non_buffered_request(ngx_http_request_t *r,
size = b->end - b->last;
- if (size > u->length) {
- size = u->length;
- }
-
if (size && upstream->read->ready) {
n = upstream->recv(upstream, b->last, size);
@@ -2503,7 +2535,7 @@ ngx_http_upstream_non_buffered_filter(void *data, ssize_t bytes)
cl->buf->last = b->last;
cl->buf->tag = u->output.tag;
- if (u->length == NGX_MAX_SIZE_T_VALUE) {
+ if (u->length == -1) {
return NGX_OK;
}
@@ -2812,6 +2844,10 @@ ngx_http_upstream_next(ngx_http_request_t *r, ngx_http_upstream_t *u,
if (u->peer.cached && ft_type == NGX_HTTP_UPSTREAM_FT_ERROR) {
status = 0;
+ /* TODO: inform balancer instead */
+
+ u->peer.tries++;
+
} else {
switch(ft_type) {
@@ -2886,6 +2922,10 @@ ngx_http_upstream_next(ngx_http_request_t *r, ngx_http_upstream_t *u,
}
#endif
+ if (u->peer.connection->pool) {
+ ngx_destroy_pool(u->peer.connection->pool);
+ }
+
ngx_close_connection(u->peer.connection);
}
@@ -2980,6 +3020,10 @@ ngx_http_upstream_finalize_request(ngx_http_request_t *r,
"close http upstream connection: %d",
u->peer.connection->fd);
+ if (u->peer.connection->pool) {
+ ngx_destroy_pool(u->peer.connection->pool);
+ }
+
ngx_close_connection(u->peer.connection);
}
@@ -3060,6 +3104,21 @@ ngx_http_upstream_ignore_header_line(ngx_http_request_t *r, ngx_table_elt_t *h,
static ngx_int_t
+ngx_http_upstream_process_content_length(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset)
+{
+ ngx_http_upstream_t *u;
+
+ u = r->upstream;
+
+ u->headers_in.content_length = h;
+ u->headers_in.content_length_n = ngx_atoof(h->value.data, h->value.len);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
ngx_http_upstream_process_set_cookie(ngx_http_request_t *r, ngx_table_elt_t *h,
ngx_uint_t offset)
{
@@ -3321,6 +3380,40 @@ ngx_http_upstream_process_charset(ngx_http_request_t *r, ngx_table_elt_t *h,
static ngx_int_t
+ngx_http_upstream_process_connection(ngx_http_request_t *r, ngx_table_elt_t *h,
+ ngx_uint_t offset)
+{
+ r->upstream->headers_in.connection = h;
+
+ if (ngx_strlcasestrn(h->value.data, h->value.data + h->value.len,
+ (u_char *) "close", 5 - 1)
+ != NULL)
+ {
+ r->upstream->headers_in.connection_close = 1;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_process_transfer_encoding(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset)
+{
+ r->upstream->headers_in.transfer_encoding = h;
+
+ if (ngx_strlcasestrn(h->value.data, h->value.data + h->value.len,
+ (u_char *) "chunked", 7 - 1)
+ != NULL)
+ {
+ r->upstream->headers_in.chunked = 1;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
ngx_http_upstream_copy_header_line(ngx_http_request_t *r, ngx_table_elt_t *h,
ngx_uint_t offset)
{
@@ -3428,26 +3521,6 @@ ngx_http_upstream_copy_content_type(ngx_http_request_t *r, ngx_table_elt_t *h,
static ngx_int_t
-ngx_http_upstream_copy_content_length(ngx_http_request_t *r, ngx_table_elt_t *h,
- ngx_uint_t offset)
-{
- ngx_table_elt_t *ho;
-
- ho = ngx_list_push(&r->headers_out.headers);
- if (ho == NULL) {
- return NGX_ERROR;
- }
-
- *ho = *h;
-
- r->headers_out.content_length = ho;
- r->headers_out.content_length_n = ngx_atoof(h->value.data, h->value.len);
-
- return NGX_OK;
-}
-
-
-static ngx_int_t
ngx_http_upstream_copy_last_modified(ngx_http_request_t *r, ngx_table_elt_t *h,
ngx_uint_t offset)
{
diff --git a/src/http/ngx_http_upstream.h b/src/http/ngx_http_upstream.h
index fa848c0d3..bcdd64b16 100644
--- a/src/http/ngx_http_upstream.h
+++ b/src/http/ngx_http_upstream.h
@@ -217,6 +217,7 @@ typedef struct {
ngx_table_elt_t *location;
ngx_table_elt_t *accept_ranges;
ngx_table_elt_t *www_authenticate;
+ ngx_table_elt_t *transfer_encoding;
#if (NGX_HTTP_GZIP)
ngx_table_elt_t *content_encoding;
@@ -225,6 +226,9 @@ typedef struct {
off_t content_length_n;
ngx_array_t cache_control;
+
+ unsigned connection_close:1;
+ unsigned chunked:1;
} ngx_http_upstream_headers_in_t;
@@ -267,7 +271,7 @@ struct ngx_http_upstream_s {
ngx_http_upstream_resolved_t *resolved;
ngx_buf_t buffer;
- size_t length;
+ off_t length;
ngx_chain_t *out_bufs;
ngx_chain_t *busy_bufs;
@@ -308,6 +312,7 @@ struct ngx_http_upstream_s {
#endif
unsigned buffering:1;
+ unsigned keepalive:1;
unsigned request_sent:1;
unsigned header_sent:1;
diff --git a/src/os/unix/ngx_errno.c b/src/os/unix/ngx_errno.c
index 02994b808..faae47829 100644
--- a/src/os/unix/ngx_errno.c
+++ b/src/os/unix/ngx_errno.c
@@ -12,7 +12,7 @@
* The strerror() messages are copied because:
*
* 1) strerror() and strerror_r() functions are not Async-Signal-Safe,
- * therefore, they can not be used in signal handlers;
+ * therefore, they cannot be used in signal handlers;
*
* 2) a direct sys_errlist[] array may be used instead of these functions,
* but Linux linker warns about its usage:
diff --git a/src/os/unix/ngx_file_aio_read.c b/src/os/unix/ngx_file_aio_read.c
index 557f9de6a..49662365f 100644
--- a/src/os/unix/ngx_file_aio_read.c
+++ b/src/os/unix/ngx_file_aio_read.c
@@ -23,7 +23,7 @@
* kqueue EVFILT_AIO filter is level triggered only: an event repeats
* until aio_return() will be called;
*
- * aio_cancel() can not cancel file AIO: it returns AIO_NOTCANCELED always.
+ * aio_cancel() cannot cancel file AIO: it returns AIO_NOTCANCELED always.
*/
diff --git a/src/os/unix/ngx_freebsd_sendfile_chain.c b/src/os/unix/ngx_freebsd_sendfile_chain.c
index 70cdb7495..039243e96 100644
--- a/src/os/unix/ngx_freebsd_sendfile_chain.c
+++ b/src/os/unix/ngx_freebsd_sendfile_chain.c
@@ -11,7 +11,7 @@
/*
* Although FreeBSD sendfile() allows to pass a header and a trailer,
- * it can not send a header with a part of the file in one packet until
+ * it cannot send a header with a part of the file in one packet until
* FreeBSD 5.3. Besides, over the fast ethernet connection sendfile()
* may send the partially filled packets, i.e. the 8 file pages may be sent
* as the 11 full 1460-bytes packets, then one incomplete 324-bytes packet,
diff --git a/src/os/unix/ngx_linux_aio_read.c b/src/os/unix/ngx_linux_aio_read.c
index b9d1d01cd..d31c4a064 100644
--- a/src/os/unix/ngx_linux_aio_read.c
+++ b/src/os/unix/ngx_linux_aio_read.c
@@ -16,7 +16,7 @@ extern aio_context_t ngx_aio_ctx;
static void ngx_file_aio_event_handler(ngx_event_t *ev);
-static long
+static int
io_submit(aio_context_t ctx, long n, struct iocb **paiocb)
{
return syscall(SYS_io_submit, ctx, n, paiocb);
@@ -27,7 +27,7 @@ ssize_t
ngx_file_aio_read(ngx_file_t *file, u_char *buf, size_t size, off_t offset,
ngx_pool_t *pool)
{
- long n;
+ ngx_err_t err;
struct iocb *piocb[1];
ngx_event_t *ev;
ngx_event_aio_t *aio;
@@ -96,9 +96,7 @@ ngx_file_aio_read(ngx_file_t *file, u_char *buf, size_t size, off_t offset,
piocb[0] = &aio->aiocb;
- n = io_submit(ngx_aio_ctx, 1, piocb);
-
- if (n == 1) {
+ if (io_submit(ngx_aio_ctx, 1, piocb) == 1) {
ev->active = 1;
ev->ready = 0;
ev->complete = 0;
@@ -106,16 +104,16 @@ ngx_file_aio_read(ngx_file_t *file, u_char *buf, size_t size, off_t offset,
return NGX_AGAIN;
}
- n = -n;
+ err = ngx_errno;
- if (n == NGX_EAGAIN) {
+ if (err == NGX_EAGAIN) {
return ngx_read_file(file, buf, size, offset);
}
- ngx_log_error(NGX_LOG_CRIT, file->log, n,
+ ngx_log_error(NGX_LOG_CRIT, file->log, err,
"io_submit(\"%V\") failed", &file->name);
- if (n == NGX_ENOSYS) {
+ if (err == NGX_ENOSYS) {
ngx_file_aio = 0;
return ngx_read_file(file, buf, size, offset);
}
diff --git a/src/os/unix/ngx_process.c b/src/os/unix/ngx_process.c
index ad2fc19de..08069c4b6 100644
--- a/src/os/unix/ngx_process.c
+++ b/src/os/unix/ngx_process.c
@@ -541,7 +541,7 @@ ngx_process_get_status(void)
if (WEXITSTATUS(status) == 2 && ngx_processes[i].respawn) {
ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
"%s %P exited with fatal code %d "
- "and can not be respawn",
+ "and cannot be respawned",
process, pid, WEXITSTATUS(status));
ngx_processes[i].respawn = 0;
}
diff --git a/src/os/unix/ngx_process_cycle.c b/src/os/unix/ngx_process_cycle.c
index 3ff0f75c6..863176fac 100644
--- a/src/os/unix/ngx_process_cycle.c
+++ b/src/os/unix/ngx_process_cycle.c
@@ -620,7 +620,8 @@ ngx_reap_children(ngx_cycle_t *cycle)
== NGX_INVALID_PID)
{
ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
- "can not respawn %s", ngx_processes[i].name);
+ "could not respawn %s",
+ ngx_processes[i].name);
continue;
}