summaryrefslogtreecommitdiff
path: root/usr.sbin/nginx
diff options
context:
space:
mode:
authorRobert Nagy <robert@cvs.openbsd.org>2013-06-01 16:12:55 +0000
committerRobert Nagy <robert@cvs.openbsd.org>2013-06-01 16:12:55 +0000
commit6c767d473d124564aa10591e9b7250fa0f9b7ee7 (patch)
tree54f4eaa9eb12722e610bfdc36ad814345f21942e /usr.sbin/nginx
parent279e7c0ae74ae79649ceb00ad0b56a345408b41a (diff)
update to nginx-1.4.1 and enable the SPDY module by default
Diffstat (limited to 'usr.sbin/nginx')
-rw-r--r--usr.sbin/nginx/CHANGES275
-rw-r--r--usr.sbin/nginx/CHANGES.ru284
-rw-r--r--usr.sbin/nginx/Makefile.bsd-wrapper3
-rw-r--r--usr.sbin/nginx/auto/lib/md5/conf6
-rw-r--r--usr.sbin/nginx/auto/lib/perl/conf4
-rw-r--r--usr.sbin/nginx/auto/lib/perl/make37
-rw-r--r--usr.sbin/nginx/auto/lib/sha1/conf2
-rw-r--r--usr.sbin/nginx/auto/modules29
-rw-r--r--usr.sbin/nginx/auto/options8
-rw-r--r--usr.sbin/nginx/auto/sources16
-rwxr-xr-xusr.sbin/nginx/auto/unix14
-rw-r--r--usr.sbin/nginx/src/core/nginx.h4
-rw-r--r--usr.sbin/nginx/src/core/ngx_array.c8
-rw-r--r--usr.sbin/nginx/src/core/ngx_array.h4
-rw-r--r--usr.sbin/nginx/src/core/ngx_connection.c20
-rw-r--r--usr.sbin/nginx/src/core/ngx_connection.h3
-rw-r--r--usr.sbin/nginx/src/core/ngx_core.h3
-rw-r--r--usr.sbin/nginx/src/core/ngx_crypt.c37
-rw-r--r--usr.sbin/nginx/src/core/ngx_cycle.h2
-rw-r--r--usr.sbin/nginx/src/core/ngx_inet.c267
-rw-r--r--usr.sbin/nginx/src/core/ngx_inet.h2
-rw-r--r--usr.sbin/nginx/src/event/modules/ngx_devpoll_module.c8
-rw-r--r--usr.sbin/nginx/src/event/modules/ngx_eventport_module.c2
-rw-r--r--usr.sbin/nginx/src/event/ngx_event.c6
-rw-r--r--usr.sbin/nginx/src/event/ngx_event.h1
-rw-r--r--usr.sbin/nginx/src/event/ngx_event_connect.c2
-rw-r--r--usr.sbin/nginx/src/event/ngx_event_openssl.c144
-rw-r--r--usr.sbin/nginx/src/event/ngx_event_openssl.h9
-rw-r--r--usr.sbin/nginx/src/event/ngx_event_openssl_stapling.c1749
-rw-r--r--usr.sbin/nginx/src/http/modules/ngx_http_addition_filter_module.c1
-rw-r--r--usr.sbin/nginx/src/http/modules/ngx_http_chunked_filter_module.c1
-rw-r--r--usr.sbin/nginx/src/http/modules/ngx_http_fastcgi_module.c5
-rw-r--r--usr.sbin/nginx/src/http/modules/ngx_http_flv_module.c4
-rw-r--r--usr.sbin/nginx/src/http/modules/ngx_http_geo_module.c15
-rw-r--r--usr.sbin/nginx/src/http/modules/ngx_http_geoip_module.c22
-rw-r--r--usr.sbin/nginx/src/http/modules/ngx_http_gunzip_filter_module.c677
-rw-r--r--usr.sbin/nginx/src/http/modules/ngx_http_gzip_filter_module.c3
-rw-r--r--usr.sbin/nginx/src/http/modules/ngx_http_gzip_static_module.c46
-rw-r--r--usr.sbin/nginx/src/http/modules/ngx_http_headers_filter_module.c44
-rw-r--r--usr.sbin/nginx/src/http/modules/ngx_http_image_filter_module.c13
-rw-r--r--usr.sbin/nginx/src/http/modules/ngx_http_limit_conn_module.c20
-rw-r--r--usr.sbin/nginx/src/http/modules/ngx_http_limit_req_module.c21
-rw-r--r--usr.sbin/nginx/src/http/modules/ngx_http_map_module.c2
-rw-r--r--usr.sbin/nginx/src/http/modules/ngx_http_memcached_module.c68
-rw-r--r--usr.sbin/nginx/src/http/modules/ngx_http_mp4_module.c12
-rw-r--r--usr.sbin/nginx/src/http/modules/ngx_http_not_modified_filter_module.c155
-rw-r--r--usr.sbin/nginx/src/http/modules/ngx_http_proxy_module.c364
-rw-r--r--usr.sbin/nginx/src/http/modules/ngx_http_range_filter_module.c40
-rw-r--r--usr.sbin/nginx/src/http/modules/ngx_http_realip_module.c25
-rw-r--r--usr.sbin/nginx/src/http/modules/ngx_http_scgi_module.c41
-rw-r--r--usr.sbin/nginx/src/http/modules/ngx_http_ssi_filter_module.c1
-rw-r--r--usr.sbin/nginx/src/http/modules/ngx_http_ssl_module.c159
-rw-r--r--usr.sbin/nginx/src/http/modules/ngx_http_ssl_module.h6
-rw-r--r--usr.sbin/nginx/src/http/modules/ngx_http_static_module.c4
-rw-r--r--usr.sbin/nginx/src/http/modules/ngx_http_stub_status_module.c93
-rw-r--r--usr.sbin/nginx/src/http/modules/ngx_http_sub_filter_module.c1
-rw-r--r--usr.sbin/nginx/src/http/modules/ngx_http_upstream_ip_hash_module.c5
-rw-r--r--usr.sbin/nginx/src/http/modules/ngx_http_upstream_least_conn_module.c5
-rw-r--r--usr.sbin/nginx/src/http/modules/ngx_http_uwsgi_module.c15
-rw-r--r--usr.sbin/nginx/src/http/modules/ngx_http_xslt_filter_module.c1
-rw-r--r--usr.sbin/nginx/src/http/modules/perl/Makefile.PL6
-rw-r--r--usr.sbin/nginx/src/http/modules/perl/nginx.pm2
-rw-r--r--usr.sbin/nginx/src/http/modules/perl/nginx.xs39
-rw-r--r--usr.sbin/nginx/src/http/ngx_http.c25
-rw-r--r--usr.sbin/nginx/src/http/ngx_http.h28
-rw-r--r--usr.sbin/nginx/src/http/ngx_http_core_module.c146
-rw-r--r--usr.sbin/nginx/src/http/ngx_http_core_module.h50
-rw-r--r--usr.sbin/nginx/src/http/ngx_http_header_filter_module.c11
-rw-r--r--usr.sbin/nginx/src/http/ngx_http_parse.c408
-rw-r--r--usr.sbin/nginx/src/http/ngx_http_request.c1324
-rw-r--r--usr.sbin/nginx/src/http/ngx_http_request.h62
-rw-r--r--usr.sbin/nginx/src/http/ngx_http_request_body.c741
-rw-r--r--usr.sbin/nginx/src/http/ngx_http_spdy.c2882
-rw-r--r--usr.sbin/nginx/src/http/ngx_http_spdy.h235
-rw-r--r--usr.sbin/nginx/src/http/ngx_http_spdy_filter_module.c999
-rw-r--r--usr.sbin/nginx/src/http/ngx_http_spdy_module.c351
-rw-r--r--usr.sbin/nginx/src/http/ngx_http_spdy_module.h36
-rw-r--r--usr.sbin/nginx/src/http/ngx_http_special_response.c4
-rw-r--r--usr.sbin/nginx/src/http/ngx_http_upstream.c441
-rw-r--r--usr.sbin/nginx/src/http/ngx_http_upstream.h12
-rw-r--r--usr.sbin/nginx/src/http/ngx_http_upstream_round_robin.c47
-rw-r--r--usr.sbin/nginx/src/http/ngx_http_upstream_round_robin.h2
-rw-r--r--usr.sbin/nginx/src/http/ngx_http_variables.c84
-rw-r--r--usr.sbin/nginx/src/mail/ngx_mail.h4
-rw-r--r--usr.sbin/nginx/src/mail/ngx_mail_auth_http_module.c60
-rw-r--r--usr.sbin/nginx/src/mail/ngx_mail_core_module.c6
86 files changed, 11309 insertions, 1514 deletions
diff --git a/usr.sbin/nginx/CHANGES b/usr.sbin/nginx/CHANGES
index e8422c2dd7e..fd42b20eed4 100644
--- a/usr.sbin/nginx/CHANGES
+++ b/usr.sbin/nginx/CHANGES
@@ -1,29 +1,152 @@
-Changes with nginx 1.2.9 13 May 2013
+Changes with nginx 1.4.1 07 May 2013
- *) Security: contents of worker process memory might be sent to a client
- if HTTP backend returned specially crafted response (CVE-2013-2070);
- the bug had appeared in 1.1.4.
+ *) Security: a stack-based buffer overflow might occur in a worker
+ process while handling a specially crafted request, potentially
+ resulting in arbitrary code execution (CVE-2013-2028); the bug had
+ appeared in 1.3.9.
+ Thanks to Greg MacManus, iSIGHT Partners Labs.
-Changes with nginx 1.2.8 02 Apr 2013
+Changes with nginx 1.4.0 24 Apr 2013
- *) Bugfix: new sessions were not always stored if the "ssl_session_cache
- shared" directive was used and there was no free space in shared
- memory.
+ *) Bugfix: nginx could not be built with the ngx_http_perl_module if the
+ --with-openssl option was used; the bug had appeared in 1.3.16.
+
+ *) Bugfix: in a request body handling in the ngx_http_perl_module; the
+ bug had appeared in 1.3.9.
+
+
+Changes with nginx 1.3.16 16 Apr 2013
+
+ *) Bugfix: a segmentation fault might occur in a worker process if
+ subrequests were used; the bug had appeared in 1.3.9.
+
+ *) Bugfix: the "tcp_nodelay" directive caused an error if a WebSocket
+ connection was proxied into a unix domain socket.
+
+ *) Bugfix: the $upstream_response_length variable has an incorrect value
+ "0" if buffering was not used.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: in the eventport and /dev/poll methods.
+
+
+Changes with nginx 1.3.15 26 Mar 2013
+
+ *) Change: opening and closing a connection without sending any data in
+ it is no longer logged to access_log with error code 400.
+
+ *) Feature: the ngx_http_spdy_module.
+ Thanks to Automattic for sponsoring this work.
+
+ *) Feature: the "limit_req_status" and "limit_conn_status" directives.
+ Thanks to Nick Marden.
+
+ *) Feature: the "image_filter_interlace" directive.
+ Thanks to Ian Babrou.
+
+ *) Feature: $connections_waiting variable in the
+ ngx_http_stub_status_module.
+
+ *) Feature: the mail proxy module now supports IPv6 backends.
+
+ *) Bugfix: request body might be transmitted incorrectly when retrying a
+ request to the next upstream server; the bug had appeared in 1.3.9.
Thanks to Piotr Sikora.
+ *) Bugfix: in the "client_body_in_file_only" directive; the bug had
+ appeared in 1.3.9.
+
*) Bugfix: responses might hang if subrequests were used and a DNS error
happened during subrequest processing.
Thanks to Lanshun Zhou.
+ *) Bugfix: in backend usage accounting.
+
+
+Changes with nginx 1.3.14 05 Mar 2013
+
+ *) Feature: $connections_active, $connections_reading, and
+ $connections_writing variables in the ngx_http_stub_status_module.
+
+ *) Feature: support of WebSocket connections in the
+ ngx_http_uwsgi_module and ngx_http_scgi_module.
+
+ *) Bugfix: in virtual servers handling with SNI.
+
+ *) Bugfix: new sessions were not always stored if the "ssl_session_cache
+ shared" directive was used and there was no free space in shared
+ memory.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: multiple X-Forwarded-For headers were handled incorrectly.
+ Thanks to Neal Poole for sponsoring this work.
+
*) Bugfix: in the ngx_http_mp4_module.
Thanks to Gernot Vormayr.
- *) Bugfix: in backend usage accounting.
+Changes with nginx 1.3.13 19 Feb 2013
+
+ *) Change: a compiler with name "cc" is now used by default.
+
+ *) Feature: support for proxying of WebSocket connections.
+ Thanks to Apcera and CloudBees for sponsoring this work.
+
+ *) Feature: the "auth_basic_user_file" directive supports "{SHA}"
+ password encryption method.
+ Thanks to Louis Opter.
+
+
+Changes with nginx 1.3.12 05 Feb 2013
+
+ *) Feature: variables support in the "proxy_bind", "fastcgi_bind",
+ "memcached_bind", "scgi_bind", and "uwsgi_bind" directives.
-Changes with nginx 1.2.7 12 Feb 2013
+ *) Feature: the $pipe, $request_length, $time_iso8601, and $time_local
+ variables can now be used not only in the "log_format" directive.
+ Thanks to Kiril Kalchev.
+
+ *) Feature: IPv6 support in the ngx_http_geoip_module.
+ Thanks to Gregor Kališnik.
+
+ *) Bugfix: in the "proxy_method" directive.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if
+ resolver was used with the poll method.
+
+ *) Bugfix: nginx might hog CPU during SSL handshake with a backend if
+ the select, poll, or /dev/poll methods were used.
+
+ *) Bugfix: the "[crit] SSL_write() failed (SSL:)" error.
+
+ *) Bugfix: in the "client_body_in_file_only" directive; the bug had
+ appeared in 1.3.9.
+
+ *) Bugfix: in the "fastcgi_keep_conn" directive.
+
+
+Changes with nginx 1.3.11 10 Jan 2013
+
+ *) Bugfix: a segmentation fault might occur if logging was used; the bug
+ had appeared in 1.3.10.
+
+ *) Bugfix: the "proxy_pass" directive did not work with IP addresses
+ without port specified; the bug had appeared in 1.3.10.
+
+ *) Bugfix: a segmentation fault occurred on start or during
+ reconfiguration if the "keepalive" directive was specified more than
+ once in a single upstream block.
+
+ *) Bugfix: parameter "default" of the "geo" directive did not set
+ default value for IPv6 addresses.
+
+
+Changes with nginx 1.3.10 25 Dec 2012
+
+ *) Change: domain names specified in configuration file are now resolved
+ to IPv6 addresses as well as IPv4 ones.
*) Change: now if the "include" directive with mask is used on Unix
systems, included files are sorted in alphabetical order.
@@ -38,13 +161,6 @@ Changes with nginx 1.2.7 12 Feb 2013
*) Feature: variables support in the "auth_basic" directive.
- *) Feature: the $pipe, $request_length, $time_iso8601, and $time_local
- variables can now be used not only in the "log_format" directive.
- Thanks to Kiril Kalchev.
-
- *) Feature: IPv6 support in the ngx_http_geoip_module.
- Thanks to Gregor Kališnik.
-
*) Bugfix: nginx could not be built with the ngx_http_perl_module in
some cases.
@@ -78,24 +194,11 @@ Changes with nginx 1.2.7 12 Feb 2013
*) Bugfix: proxied HEAD requests might return incorrect response if the
"gzip" directive was used.
- *) Bugfix: a segmentation fault occurred on start or during
- reconfiguration if the "keepalive" directive was specified more than
- once in a single upstream block.
- *) Bugfix: in the "proxy_method" directive.
+Changes with nginx 1.3.9 27 Nov 2012
- *) Bugfix: a segmentation fault might occur in a worker process if
- resolver was used with the poll method.
-
- *) Bugfix: nginx might hog CPU during SSL handshake with a backend if
- the select, poll, or /dev/poll methods were used.
-
- *) Bugfix: the "[crit] SSL_write() failed (SSL:)" error.
-
- *) Bugfix: in the "fastcgi_keep_conn" directive.
-
-
-Changes with nginx 1.2.6 11 Dec 2012
+ *) Feature: support for chunked transfer encoding while reading client
+ request body.
*) Feature: the $request_time and $msec variables can now be used not
only in the "log_format" directive.
@@ -106,7 +209,7 @@ Changes with nginx 1.2.6 11 Dec 2012
*) Bugfix: in the ngx_http_dav_module.
-Changes with nginx 1.2.5 13 Nov 2012
+Changes with nginx 1.3.8 30 Oct 2012
*) Feature: the "optional_no_ca" parameter of the "ssl_verify_client"
directive.
@@ -116,17 +219,35 @@ Changes with nginx 1.2.5 13 Nov 2012
variables can now be used not only in the "log_format" directive.
Thanks to Benjamin Grössing.
- *) Feature: resolver now randomly rotates addresses returned from cache.
- Thanks to Anton Jouline.
-
*) Feature: the "auto" parameter of the "worker_processes" directive.
*) Bugfix: "cache file ... has md5 collision" alert.
+ *) Bugfix: in the ngx_http_gunzip_filter_module.
+
+ *) Bugfix: in the "ssl_stapling" directive.
+
+
+Changes with nginx 1.3.7 02 Oct 2012
+
+ *) Feature: OCSP stapling support.
+ Thanks to Comodo, DigiCert and GlobalSign for sponsoring this work.
+
+ *) Feature: the "ssl_trusted_certificate" directive.
+
+ *) Feature: resolver now randomly rotates addresses returned from cache.
+ Thanks to Anton Jouline.
+
*) Bugfix: OpenSSL 0.9.7 compatibility.
-Changes with nginx 1.2.4 25 Sep 2012
+Changes with nginx 1.3.6 12 Sep 2012
+
+ *) Feature: the ngx_http_gunzip_filter_module.
+
+ *) Feature: the "memcached_gzip_flag" directive.
+
+ *) Feature: the "always" parameter of the "gzip_static" directive.
*) Bugfix: in the "limit_req" directive; the bug had appeared in 1.1.14.
Thanks to Charles Chen.
@@ -134,6 +255,12 @@ Changes with nginx 1.2.4 25 Sep 2012
*) Bugfix: nginx could not be built by gcc 4.7 with -O2 optimization if
the --with-ipv6 option was used.
+
+Changes with nginx 1.3.5 21 Aug 2012
+
+ *) Change: the ngx_http_mp4_module module no longer skips tracks in
+ formats other than H.264 and AAC.
+
*) Bugfix: a segmentation fault might occur in a worker process if the
"map" directive was used with variables as values.
@@ -154,7 +281,10 @@ Changes with nginx 1.2.4 25 Sep 2012
Thanks to HAYASHI Kentaro.
-Changes with nginx 1.2.3 07 Aug 2012
+Changes with nginx 1.3.4 31 Jul 2012
+
+ *) Change: the "ipv6only" parameter is now turned on by default for
+ listening IPv6 sockets.
*) Feature: the Clang compiler support.
@@ -170,6 +300,11 @@ Changes with nginx 1.2.3 07 Aug 2012
"fastcgi_hide_header", "scgi_hide_header", and "uwsgi_hide_header"
directives might be inherited incorrectly.
+
+Changes with nginx 1.3.3 10 Jul 2012
+
+ *) Feature: entity tags support and the "etag" directive.
+
*) Bugfix: trailing dot in a source value was not ignored if the "map"
directive was used with the "hostnames" parameter.
@@ -178,7 +313,7 @@ Changes with nginx 1.2.3 07 Aug 2012
to a named location.
-Changes with nginx 1.2.2 03 Jul 2012
+Changes with nginx 1.3.2 26 Jun 2012
*) Change: the "single" parameter of the "keepalive" directive is now
ignored.
@@ -186,6 +321,36 @@ Changes with nginx 1.2.2 03 Jul 2012
*) Change: SSL compression is now disabled when using all versions of
OpenSSL, including ones prior to 1.0.0.
+ *) Feature: it is now possible to use the "ip_hash" directive to balance
+ IPv6 clients.
+
+ *) Feature: the $status variable can now be used not only in the
+ "log_format" directive.
+
+ *) Bugfix: a segmentation fault might occur in a worker process on
+ shutdown if the "resolver" directive was used.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if the
+ ngx_http_mp4_module was used.
+
+ *) Bugfix: in the ngx_http_mp4_module.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if
+ conflicting wildcard server names were used.
+
+ *) Bugfix: nginx might be terminated abnormally on a SIGBUS signal on
+ ARM platform.
+
+ *) Bugfix: an alert "sendmsg() failed (9: Bad file number)" on HP-UX
+ while reconfiguration.
+
+
+Changes with nginx 1.3.1 05 Jun 2012
+
+ *) Security: now nginx/Windows ignores trailing dot in URI path
+ component, and does not allow URIs with ":$" in it.
+ Thanks to Vladimir Kochetkov, Positive Research Center.
+
*) Feature: the "proxy_pass", "fastcgi_pass", "scgi_pass", "uwsgi_pass"
directives, and the "server" directive inside the "upstream" block,
now support IPv6 addresses.
@@ -198,11 +363,8 @@ Changes with nginx 1.2.2 03 Jul 2012
*) Feature: it is now possible to specify a weight for servers while
using the "ip_hash" directive.
- *) Feature: it is now possible to use the "ip_hash" directive to balance
- IPv6 clients.
-
- *) Feature: the $status variable can now be used not only in the
- "log_format" directive.
+ *) Bugfix: a segmentation fault might occur in a worker process if the
+ "image_filter" directive was used; the bug had appeared in 1.3.0.
*) Bugfix: nginx could not be built with ngx_cpp_test_module; the bug
had appeared in 1.1.12.
@@ -220,29 +382,8 @@ Changes with nginx 1.2.2 03 Jul 2012
*) Bugfix: in the "proxy_cookie_domain" and "proxy_cookie_path"
directives.
- *) Bugfix: a segmentation fault might occur in a worker process on
- shutdown if the "resolver" directive was used.
-
- *) Bugfix: a segmentation fault might occur in a worker process if the
- ngx_http_mp4_module was used.
- *) Bugfix: in the ngx_http_mp4_module.
-
- *) Bugfix: a segmentation fault might occur in a worker process if
- conflicting wildcard server names were used.
-
- *) Bugfix: nginx might be terminated abnormally on a SIGBUS signal on
- ARM platform.
-
- *) Bugfix: an alert "sendmsg() failed (9: Bad file number)" on HP-UX
- while reconfiguration.
-
-
-Changes with nginx 1.2.1 05 Jun 2012
-
- *) Security: now nginx/Windows ignores trailing dot in URI path
- component, and does not allow URIs with ":$" in it.
- Thanks to Vladimir Kochetkov, Positive Research Center.
+Changes with nginx 1.3.0 15 May 2012
*) Feature: the "debug_connection" directive now supports IPv6 addresses
and the "unix:" parameter.
diff --git a/usr.sbin/nginx/CHANGES.ru b/usr.sbin/nginx/CHANGES.ru
index 33490f76d85..12856d8eabc 100644
--- a/usr.sbin/nginx/CHANGES.ru
+++ b/usr.sbin/nginx/CHANGES.ru
@@ -1,42 +1,111 @@
-Изменения в nginx 1.2.9 13.05.2013
+Изменения в nginx 1.4.1 07.05.2013
- *) Безопасность: содержимое памяти рабочего процесса могло быть
- отправлено клиенту, если HTTP-бэкенд возвращал специально созданный
- ответ (CVE-2013-2070); ошибка появилась в 1.1.4.
+ *) Безопасность: при обработке специально созданного запроса мог
+ перезаписываться стек рабочего процесса, что могло приводить к
+ выполнению произвольного кода (CVE-2013-2028); ошибка появилась в
+ 1.3.9.
+ Спасибо Greg MacManus, iSIGHT Partners Labs.
-Изменения в nginx 1.2.8 02.04.2013
+Изменения в nginx 1.4.0 24.04.2013
- *) Исправление: при использовании директивы "ssl_session_cache shared"
- новые сессии могли не сохраняться, если заканчивалось место в
- разделяемой памяти.
+ *) Исправление: nginx не собирался с модулем ngx_http_perl_module, если
+ использовался параметр --with-openssl; ошибка появилась в 1.3.16.
+
+ *) Исправление: в работе с телом запроса из модуля ngx_http_perl_module;
+ ошибка появилась в 1.3.9.
+
+
+Изменения в nginx 1.3.16 16.04.2013
+
+ *) Исправление: в рабочем процессе мог произойти segmentation fault,
+ если использовались подзапросы; ошибка появилась в 1.3.9.
+
+ *) Исправление: директива tcp_nodelay вызывала ошибку при проксировании
+ WebSocket-соединений в unix domain сокет.
+
+ *) Исправление: переменная $upstream_response_length возвращала значение
+ "0", если не использовалась буферизация.
+ Спасибо Piotr Sikora.
+
+ *) Исправление: в методах обработки соединений eventport и /dev/poll.
+
+
+Изменения в nginx 1.3.15 26.03.2013
+
+ *) Изменение: открытие и закрытие соединения без отправки в нём
+ каких-либо данных больше не записывается в access_log с кодом ошибки
+ 400.
+
+ *) Добавление: модуль ngx_http_spdy_module.
+ Спасибо Automattic за спонсирование разработки.
+
+ *) Добавление: директивы limit_req_status и limit_conn_status.
+ Спасибо Nick Marden.
+
+ *) Добавление: директива image_filter_interlace.
+ Спасибо Ивану Боброву.
+
+ *) Добавление: переменная $connections_waiting в модуле
+ ngx_http_stub_status_module.
+
+ *) Добавление: теперь почтовый прокси-сервер поддерживает IPv6-бэкенды.
+
+ *) Исправление: при повторной отправке запроса на бэкенд тело запроса
+ могло передаваться неправильно; ошибка появилась в 1.3.9.
Спасибо Piotr Sikora.
+ *) Исправление: в директиве client_body_in_file_only; ошибка появилась в
+ 1.3.9.
+
*) Исправление: ответы могли зависать, если использовались подзапросы и
при обработке подзапроса происходила DNS-ошибка.
Спасибо Lanshun Zhou.
+ *) Исправление: в процедуре учёта использования бэкендов.
+
+
+Изменения в nginx 1.3.14 05.03.2013
+
+ *) Добавление: переменные $connections_active, $connections_reading и
+ $connections_writing в модуле ngx_http_stub_status_module.
+
+ *) Добавление: поддержка WebSocket-соединений в модулях
+ ngx_http_uwsgi_module и ngx_http_scgi_module.
+
+ *) Исправление: в обработке виртуальных серверов при использовании SNI.
+
+ *) Исправление: при использовании директивы "ssl_session_cache shared"
+ новые сессии могли не сохраняться, если заканчивалось место в
+ разделяемой памяти.
+ Спасибо Piotr Sikora.
+
+ *) Исправление: несколько заголовков X-Forwarded-For обрабатывались
+ неправильно.
+ Спасибо Neal Poole за спонсирование разработки.
+
*) Исправление: в модуле ngx_http_mp4_module.
Спасибо Gernot Vormayr.
- *) Исправление: в процедуре учёта использования бэкендов.
+Изменения в nginx 1.3.13 19.02.2013
-Изменения в nginx 1.2.7 12.02.2013
+ *) Изменение: теперь для сборки по умолчанию используется компилятор с
+ именем "cc".
- *) Изменение: теперь при использовании директивы include с маской на
- Unix-системах включаемые файлы сортируются в алфавитном порядке.
+ *) Добавление: поддержка проксирования WebSocket-соединений.
+ Спасибо Apcera и CloudBees за спонсирование разработки.
- *) Изменение: директива add_header добавляет строки в ответы с кодом
- 201.
+ *) Добавление: директива auth_basic_user_file поддерживает шифрование
+ паролей методом "{SHA}".
+ Спасибо Louis Opter.
- *) Добавление: директива geo теперь поддерживает IPv6 адреса в формате
- CIDR.
- *) Добавление: параметры flush и gzip в директиве access_log.
+Изменения в nginx 1.3.12 05.02.2013
- *) Добавление: директива auth_basic поддерживает переменные.
+ *) Добавление: директивы proxy_bind, fastcgi_bind, memcached_bind,
+ scgi_bind и uwsgi_bind поддерживают переменные.
*) Добавление: переменные $pipe, $request_length, $time_iso8601 и
$time_local теперь можно использовать не только в директиве
@@ -46,6 +115,58 @@
*) Добавление: поддержка IPv6 в модуле ngx_http_geoip_module.
Спасибо Gregor Kališnik.
+ *) Исправление: директива proxy_method работала неверно, если была
+ указана на уровне http.
+
+ *) Исправление: в рабочем процессе мог произойти segmentation fault,
+ если использовался resolver и метод poll.
+
+ *) Исправление: nginx мог нагружать процессор во время SSL handshake с
+ бэкендом при использовании методов обработки соединений select, poll
+ и /dev/poll.
+
+ *) Исправление: ошибка "[crit] SSL_write() failed (SSL:)".
+
+ *) Исправление: в директиве client_body_in_file_only; ошибка появилась в
+ 1.3.9.
+
+ *) Исправление: в директиве fastcgi_keep_conn.
+
+
+Изменения в nginx 1.3.11 10.01.2013
+
+ *) Исправление: при записи в лог мог происходить segmentation fault;
+ ошибка появилась в 1.3.10.
+
+ *) Исправление: директива proxy_pass не работала с IP-адресами без
+ явного указания порта; ошибка появилась в 1.3.10.
+
+ *) Исправление: на старте или во время переконфигурации происходил
+ segmentation fault, если директива keepalive была указана несколько
+ раз в одном блоке upstream.
+
+ *) Исправление: параметр default директивы geo не определял значение по
+ умолчанию для IPv6-адресов.
+
+
+Изменения в nginx 1.3.10 25.12.2012
+
+ *) Изменение: для указанных в конфигурационном файле доменных имён
+ теперь используются не только IPv4, но и IPv6 адреса.
+
+ *) Изменение: теперь при использовании директивы include с маской на
+ Unix-системах включаемые файлы сортируются в алфавитном порядке.
+
+ *) Изменение: директива add_header добавляет строки в ответы с кодом
+ 201.
+
+ *) Добавление: директива geo теперь поддерживает IPv6 адреса в формате
+ CIDR.
+
+ *) Добавление: параметры flush и gzip в директиве access_log.
+
+ *) Добавление: директива auth_basic поддерживает переменные.
+
*) Исправление: nginx в некоторых случаях не собирался с модулем
ngx_http_perl_module.
@@ -80,26 +201,11 @@
*) Исправление: при проксировании HEAD-запросов мог возвращаться
некорректный ответ, если использовалась директива gzip.
- *) Исправление: на старте или во время переконфигурации происходил
- segmentation fault, если директива keepalive была указана несколько
- раз в одном блоке upstream.
- *) Исправление: директива proxy_method работала неверно, если была
- указана на уровне http.
+Изменения в nginx 1.3.9 27.11.2012
- *) Исправление: в рабочем процессе мог произойти segmentation fault,
- если использовался resolver и метод poll.
-
- *) Исправление: nginx мог нагружать процессор во время SSL handshake с
- бэкендом при использовании методов обработки соединений select, poll
- и /dev/poll.
-
- *) Исправление: ошибка "[crit] SSL_write() failed (SSL:)".
-
- *) Исправление: в директиве fastcgi_keep_conn.
-
-
-Изменения в nginx 1.2.6 11.12.2012
+ *) Добавление: поддержка chunked transfer encoding при получении тела
+ запроса.
*) Добавление: переменные $request_time и $msec теперь можно
использовать не только в директиве log_format.
@@ -110,7 +216,7 @@
*) Исправление: в модуле ngx_http_dav_module.
-Изменения в nginx 1.2.5 13.11.2012
+Изменения в nginx 1.3.8 30.10.2012
*) Добавление: параметр optional_no_ca директивы ssl_verify_client.
Спасибо Михаилу Казанцеву и Eric O'Connor.
@@ -120,18 +226,36 @@
log_format.
Спасибо Benjamin Grössing.
+ *) Добавление: параметр auto директивы worker_processes.
+
+ *) Исправление: сообщения "cache file ... has md5 collision".
+
+ *) Исправление: в модуле ngx_http_gunzip_filter_module.
+
+ *) Исправление: в директиве ssl_stapling.
+
+
+Изменения в nginx 1.3.7 02.10.2012
+
+ *) Добавление: поддержка OCSP stapling.
+ Спасибо Comodo, DigiCert и GlobalSign за спонсирование разработки.
+
+ *) Добавление: директива ssl_trusted_certificate.
+
*) Добавление: теперь resolver случайным образом меняет порядок
возвращаемых закэшированных адресов.
Спасибо Антону Жулину.
- *) Добавление: параметр auto директивы worker_processes.
+ *) Исправление: совместимость с OpenSSL 0.9.7.
- *) Исправление: сообщения "cache file ... has md5 collision".
- *) Исправление: совместимость с OpenSSL 0.9.7.
+Изменения в nginx 1.3.6 12.09.2012
+
+ *) Добавление: модуль ngx_http_gunzip_filter_module.
+ *) Добавление: директива memcached_gzip_flag.
-Изменения в nginx 1.2.4 25.09.2012
+ *) Добавление: параметр always директивы gzip_static.
*) Исправление: в директиве "limit_req"; ошибка появилась в 1.1.14.
Спасибо Charles Chen.
@@ -139,6 +263,12 @@
*) Исправление: nginx не собирался gcc 4.7 с оптимизацией -O2 если
использовался параметр --with-ipv6.
+
+Изменения в nginx 1.3.5 21.08.2012
+
+ *) Изменение: модуль ngx_http_mp4_module больше не отфильтровывает
+ дорожки в форматах, отличных от H.264 и AAC.
+
*) Исправление: в рабочем процессе мог произойти segmentation fault,
если в директиве map в качестве значений использовались переменные.
@@ -158,7 +288,10 @@
Спасибо HAYASHI Kentaro.
-Изменения в nginx 1.2.3 07.08.2012
+Изменения в nginx 1.3.4 31.07.2012
+
+ *) Изменение: теперь на слушающих IPv6-сокетах параметр ipv6only включён
+ по умолчанию.
*) Добавление: поддержка компилятора Clang.
@@ -174,6 +307,11 @@
fastcgi_hide_header, scgi_hide_header и uwsgi_hide_header могли
наследоваться некорректно.
+
+Изменения в nginx 1.3.3 10.07.2012
+
+ *) Добавление: поддержка entity tags и директива etag.
+
*) Исправление: при использовании директивы map с параметром hostnames
не игнорировалась конечная точка в исходном значении.
@@ -182,45 +320,19 @@
изменения URI с помощью директивы rewrite.
-Изменения в nginx 1.2.2 03.07.2012
+Изменения в nginx 1.3.2 26.06.2012
*) Изменение: параметр single директивы keepalive теперь игнорируется.
*) Изменение: сжатие SSL теперь отключено в том числе при использовании
OpenSSL cтарее 1.0.0.
- *) Добавление: директивы proxy_pass, fastcgi_pass, scgi_pass, uwsgi_pass
- и директива server в блоке upstream теперь поддерживают IPv6-адреса.
-
- *) Добавление: в директиве resolver теперь можно указывать порт и
- задавать IPv6-адреса DNS-серверов.
-
- *) Добавление: директива least_conn в блоке upstream.
-
- *) Добавление: при использовании директивы ip_hash теперь можно задавать
- веса серверов.
-
*) Добавление: директиву "ip_hash" теперь можно использовать для
балансировки IPv6 клиентов.
*) Добавление: переменную $status теперь можно использовать не только в
директиве log_format.
- *) Исправление: nginx не собирался с модулем ngx_cpp_test_module; ошибка
- появилась в 1.1.12.
-
- *) Исправление: доступ к переменным из SSI и встроенного перла мог не
- работать после переконфигурации.
- Спасибо Yichun Zhang.
-
- *) Исправление: в модуле ngx_http_xslt_filter_module.
- Спасибо Kuramoto Eiji.
-
- *) Исправление: утечки памяти при использовании переменной $geoip_org.
- Спасибо Денису Латыпову.
-
- *) Исправление: в директивах proxy_cookie_domain и proxy_cookie_path.
-
*) Исправление: при завершении рабочего процесса мог произойти
segmentation fault, если использовалась директива resolver.
@@ -239,13 +351,45 @@
alert "sendmsg() failed (9: Bad file number)".
-Изменения в nginx 1.2.1 05.06.2012
+Изменения в nginx 1.3.1 05.06.2012
*) Безопасность: теперь nginx/Windows игнорирует точку в конце
компонента URI и не разрешает URI, содержащие последовательность
":$".
Спасибо Владимиру Кочеткову, Positive Research Center.
+ *) Добавление: директивы proxy_pass, fastcgi_pass, scgi_pass, uwsgi_pass
+ и директива server в блоке upstream теперь поддерживают IPv6-адреса.
+
+ *) Добавление: в директиве resolver теперь можно указывать порт и
+ задавать IPv6-адреса DNS-серверов.
+
+ *) Добавление: директива least_conn в блоке upstream.
+
+ *) Добавление: при использовании директивы ip_hash теперь можно задавать
+ веса серверов.
+
+ *) Исправление: в рабочем процессе мог произойти segmentation fault,
+ если использовалась директива image_filter; ошибка появилась в 1.3.0.
+
+ *) Исправление: nginx не собирался с модулем ngx_cpp_test_module; ошибка
+ появилась в 1.1.12.
+
+ *) Исправление: доступ к переменным из SSI и встроенного перла мог не
+ работать после переконфигурации.
+ Спасибо Yichun Zhang.
+
+ *) Исправление: в модуле ngx_http_xslt_filter_module.
+ Спасибо Kuramoto Eiji.
+
+ *) Исправление: утечки памяти при использовании переменной $geoip_org.
+ Спасибо Денису Латыпову.
+
+ *) Исправление: в директивах proxy_cookie_domain и proxy_cookie_path.
+
+
+Изменения в nginx 1.3.0 15.05.2012
+
*) Добавление: директива debug_connection теперь поддерживает
IPv6-адреса и параметр "unix:".
diff --git a/usr.sbin/nginx/Makefile.bsd-wrapper b/usr.sbin/nginx/Makefile.bsd-wrapper
index 70978673882..79e0fe892e7 100644
--- a/usr.sbin/nginx/Makefile.bsd-wrapper
+++ b/usr.sbin/nginx/Makefile.bsd-wrapper
@@ -1,5 +1,5 @@
# Build wrapper for Nginx
-# $OpenBSD: Makefile.bsd-wrapper,v 1.10 2013/02/05 15:01:02 ajacoutot Exp $
+# $OpenBSD: Makefile.bsd-wrapper,v 1.11 2013/06/01 16:12:54 robert Exp $
LNDIR= /usr/bin/lndir
@@ -26,6 +26,7 @@ CONFIGURE_ARGS= --prefix=/var/www \
--with-http_gzip_static_module \
--with-http_ssl_module \
--with-http_stub_status_module \
+ --with-http_spdy_module \
--with-ipv6 \
--with-cc-opt="-DNGX_ENABLE_SYSLOG" \
--without-mail_pop3_module \
diff --git a/usr.sbin/nginx/auto/lib/md5/conf b/usr.sbin/nginx/auto/lib/md5/conf
index f9c1ed90296..eb5dfd1f236 100644
--- a/usr.sbin/nginx/auto/lib/md5/conf
+++ b/usr.sbin/nginx/auto/lib/md5/conf
@@ -52,7 +52,7 @@ else
# FreeBSD, Solaris 10
- ngx_feature="system md library"
+ ngx_feature="md5 in system md library"
ngx_feature_name=NGX_HAVE_MD5
ngx_feature_run=no
ngx_feature_incs="#include <md5.h>"
@@ -67,7 +67,7 @@ else
# Solaris 8/9
- ngx_feature="system md5 library"
+ ngx_feature="md5 in system md5 library"
ngx_feature_libs="-lmd5"
. auto/feature
@@ -78,7 +78,7 @@ else
# OpenSSL crypto library
- ngx_feature="OpenSSL md5 crypto library"
+ ngx_feature="md5 in system OpenSSL crypto library"
ngx_feature_name="NGX_OPENSSL_MD5"
ngx_feature_incs="#include <openssl/md5.h>"
ngx_feature_libs="-lcrypto"
diff --git a/usr.sbin/nginx/auto/lib/perl/conf b/usr.sbin/nginx/auto/lib/perl/conf
index 5ce6c91e613..2fbaa76b716 100644
--- a/usr.sbin/nginx/auto/lib/perl/conf
+++ b/usr.sbin/nginx/auto/lib/perl/conf
@@ -40,6 +40,8 @@ if test -n "$NGX_PERL_VER"; then
ngx_perl_ldopts=`$NGX_PERL -MExtUtils::Embed -e ldopts`
+ ngx_perl_dlext=`$NGX_PERL -MConfig -e 'print $Config{dlext}'`
+
if $NGX_PERL -V:usemultiplicity | grep define > /dev/null; then
have=NGX_HAVE_PERL_MULTIPLICITY . auto/have
echo " + perl interpreter multiplicity found"
@@ -51,7 +53,7 @@ if test -n "$NGX_PERL_VER"; then
fi
CORE_LINK="$CORE_LINK $ngx_perl_ldopts"
- LINK_DEPS="$LINK_DEPS $NGX_OBJS/src/http/modules/perl/blib/arch/auto/nginx/nginx.so"
+ LINK_DEPS="$LINK_DEPS $NGX_OBJS/src/http/modules/perl/blib/arch/auto/nginx/nginx.$ngx_perl_dlext"
if test -n "$NGX_PERL_MODULES"; then
have=NGX_PERL_MODULES value="(u_char *) \"$NGX_PERL_MODULES\""
diff --git a/usr.sbin/nginx/auto/lib/perl/make b/usr.sbin/nginx/auto/lib/perl/make
index b40352abfe2..260bd95a0b7 100644
--- a/usr.sbin/nginx/auto/lib/perl/make
+++ b/usr.sbin/nginx/auto/lib/perl/make
@@ -3,33 +3,38 @@
# Copyright (C) Nginx, Inc.
+v=`grep 'define NGINX_VERSION' src/core/nginx.h | sed -e 's/^.*"\(.*\)".*/\1/'`
+
+
cat << END >> $NGX_MAKEFILE
-$NGX_OBJS/src/http/modules/perl/blib/arch/auto/nginx/nginx.so: \
- \$(CORE_DEPS) \$(HTTP_DEPS) \
- src/http/modules/perl/nginx.pm \
- src/http/modules/perl/nginx.xs \
- src/http/modules/perl/ngx_http_perl_module.h \
+$NGX_OBJS/src/http/modules/perl/blib/arch/auto/nginx/nginx.$ngx_perl_dlext: \\
+ \$(CORE_DEPS) \$(HTTP_DEPS) \\
+ src/http/modules/perl/ngx_http_perl_module.h \\
$NGX_OBJS/src/http/modules/perl/Makefile
- cp src/http/modules/perl/nginx.* $NGX_OBJS/src/http/modules/perl/
-
cd $NGX_OBJS/src/http/modules/perl && \$(MAKE)
rm -rf $NGX_OBJS/install_perl
-$NGX_OBJS/src/http/modules/perl/Makefile: \
- src/http/modules/perl/Makefile.PL \
- src/http/modules/perl/nginx.pm
- cp -p src/http/modules/perl/nginx.* $NGX_OBJS/src/http/modules/perl/
+$NGX_OBJS/src/http/modules/perl/Makefile: \\
+ src/core/nginx.h \\
+ src/http/modules/perl/Makefile.PL \\
+ src/http/modules/perl/nginx.pm \\
+ src/http/modules/perl/nginx.xs \\
+ src/http/modules/perl/typemap
+ sed "s/%%VERSION%%/$v/" src/http/modules/perl/nginx.pm > \\
+ $NGX_OBJS/src/http/modules/perl/nginx.pm
+ cp -p src/http/modules/perl/nginx.xs $NGX_OBJS/src/http/modules/perl/
cp -p src/http/modules/perl/typemap $NGX_OBJS/src/http/modules/perl/
cp -p src/http/modules/perl/Makefile.PL $NGX_OBJS/src/http/modules/perl/
- cd $NGX_OBJS/src/http/modules/perl \
- && NGX_PM_CFLAGS="\$(NGX_PM_CFLAGS) -g $NGX_CC_OPT" \
- NGX_INCS="$CORE_INCS $NGX_OBJS $HTTP_INCS" \
- $NGX_PERL Makefile.PL \
- LIB=$NGX_PERL_MODULES \
+ cd $NGX_OBJS/src/http/modules/perl \\
+ && NGX_PM_CFLAGS="\$(NGX_PM_CFLAGS) -g $NGX_CC_OPT" \\
+ NGX_INCS="$CORE_INCS $NGX_OBJS $HTTP_INCS" \\
+ NGX_DEPS="\$(CORE_DEPS) \$(HTTP_DEPS)" \\
+ $NGX_PERL Makefile.PL \\
+ LIB=$NGX_PERL_MODULES \\
INSTALLSITEMAN3DIR=$NGX_PERL_MODULES_MAN
END
diff --git a/usr.sbin/nginx/auto/lib/sha1/conf b/usr.sbin/nginx/auto/lib/sha1/conf
index 1e644c02f12..fd69afda22b 100644
--- a/usr.sbin/nginx/auto/lib/sha1/conf
+++ b/usr.sbin/nginx/auto/lib/sha1/conf
@@ -57,7 +57,7 @@ else
# OpenSSL crypto library
- ngx_feature="OpenSSL sha1 crypto library"
+ ngx_feature="sha1 in system OpenSSL crypto library"
ngx_feature_incs="#include <openssl/sha.h>"
ngx_feature_libs="-lcrypto"
. auto/feature
diff --git a/usr.sbin/nginx/auto/modules b/usr.sbin/nginx/auto/modules
index d872c4e392c..a78e785e5c0 100644
--- a/usr.sbin/nginx/auto/modules
+++ b/usr.sbin/nginx/auto/modules
@@ -100,6 +100,7 @@ fi
# ngx_http_write_filter
# ngx_http_header_filter
# ngx_http_chunked_filter
+# ngx_http_spdy_filter
# ngx_http_range_header_filter
# ngx_http_gzip_filter
# ngx_http_postpone_filter
@@ -109,6 +110,7 @@ fi
# ngx_http_image_filter
# ngx_http_sub_filter
# ngx_http_addition_filter
+# ngx_http_gunzip_filter
# ngx_http_userid_filter
# ngx_http_headers_filter
# ngx_http_copy_filter
@@ -117,8 +119,13 @@ fi
HTTP_FILTER_MODULES="$HTTP_WRITE_FILTER_MODULE \
$HTTP_HEADER_FILTER_MODULE \
- $HTTP_CHUNKED_FILTER_MODULE \
- $HTTP_RANGE_HEADER_FILTER_MODULE"
+ $HTTP_CHUNKED_FILTER_MODULE"
+
+if [ $HTTP_SPDY = YES ]; then
+ HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $HTTP_SPDY_FILTER_MODULE"
+fi
+
+HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $HTTP_RANGE_HEADER_FILTER_MODULE"
if [ $HTTP_GZIP = YES ]; then
have=NGX_HTTP_GZIP . auto/have
@@ -166,11 +173,27 @@ if [ $HTTP_ADDITION = YES ]; then
HTTP_SRCS="$HTTP_SRCS $HTTP_ADDITION_SRCS"
fi
+if [ $HTTP_GUNZIP = YES ]; then
+ have=NGX_HTTP_GZIP . auto/have
+ USE_ZLIB=YES
+ HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $HTTP_GUNZIP_FILTER_MODULE"
+ HTTP_SRCS="$HTTP_SRCS $HTTP_GUNZIP_SRCS"
+fi
+
if [ $HTTP_USERID = YES ]; then
HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $HTTP_USERID_FILTER_MODULE"
HTTP_SRCS="$HTTP_SRCS $HTTP_USERID_SRCS"
fi
+
+if [ $HTTP_SPDY = YES ]; then
+ have=NGX_HTTP_SPDY . auto/have
+ USE_ZLIB=YES
+ HTTP_MODULES="$HTTP_MODULES $HTTP_SPDY_MODULE"
+ HTTP_DEPS="$HTTP_DEPS $HTTP_SPDY_DEPS"
+ HTTP_SRCS="$HTTP_SRCS $HTTP_SPDY_SRCS"
+fi
+
HTTP_MODULES="$HTTP_MODULES $HTTP_STATIC_MODULE"
if [ $HTTP_GZIP_STATIC = YES ]; then
@@ -234,7 +257,6 @@ if [ $HTTP_STATUS = YES ]; then
fi
if [ $HTTP_GEO = YES ]; then
- have=NGX_HTTP_GEO . auto/have
have=NGX_HTTP_X_FORWARDED_FOR . auto/have
HTTP_MODULES="$HTTP_MODULES $HTTP_GEO_MODULE"
HTTP_SRCS="$HTTP_SRCS $HTTP_GEO_SRCS"
@@ -276,7 +298,6 @@ if [ $HTTP_SSL = YES ]; then
fi
if [ $HTTP_PROXY = YES ]; then
- have=NGX_HTTP_PROXY . auto/have
have=NGX_HTTP_X_FORWARDED_FOR . auto/have
#USE_MD5=YES
HTTP_MODULES="$HTTP_MODULES $HTTP_PROXY_MODULE"
diff --git a/usr.sbin/nginx/auto/options b/usr.sbin/nginx/auto/options
index 6c3a4db9742..6713379e3b9 100644
--- a/usr.sbin/nginx/auto/options
+++ b/usr.sbin/nginx/auto/options
@@ -15,7 +15,7 @@ NGX_LOCK_PATH=
NGX_USER=
NGX_GROUP=
-CC=${CC:-gcc}
+CC=${CC:-cc}
CPP=
NGX_OBJS=objs
@@ -60,6 +60,7 @@ HTTP_CACHE=YES
HTTP_CHARSET=YES
HTTP_GZIP=YES
HTTP_SSL=NO
+HTTP_SPDY=NO
HTTP_SSI=YES
HTTP_POSTPONE=NO
HTTP_REALIP=NO
@@ -94,6 +95,7 @@ HTTP_SECURE_LINK=NO
HTTP_DEGRADATION=NO
HTTP_FLV=NO
HTTP_MP4=NO
+HTTP_GUNZIP=NO
HTTP_GZIP_STATIC=NO
HTTP_UPSTREAM_IP_HASH=YES
HTTP_UPSTREAM_LEAST_CONN=YES
@@ -201,6 +203,7 @@ do
--http-scgi-temp-path=*) NGX_HTTP_SCGI_TEMP_PATH="$value" ;;
--with-http_ssl_module) HTTP_SSL=YES ;;
+ --with-http_spdy_module) HTTP_SPDY=YES ;;
--with-http_realip_module) HTTP_REALIP=YES ;;
--with-http_addition_module) HTTP_ADDITION=YES ;;
--with-http_xslt_module) HTTP_XSLT=YES ;;
@@ -210,6 +213,7 @@ do
--with-http_dav_module) HTTP_DAV=YES ;;
--with-http_flv_module) HTTP_FLV=YES ;;
--with-http_mp4_module) HTTP_MP4=YES ;;
+ --with-http_gunzip_module) HTTP_GUNZIP=YES ;;
--with-http_gzip_static_module) HTTP_GZIP_STATIC=YES ;;
--with-http_random_index_module) HTTP_RANDOM_INDEX=YES ;;
--with-http_secure_link_module) HTTP_SECURE_LINK=YES ;;
@@ -347,6 +351,7 @@ cat << END
--with-ipv6 enable IPv6 support
--with-http_ssl_module enable ngx_http_ssl_module
+ --with-http_spdy_module enable ngx_http_spdy_module
--with-http_realip_module enable ngx_http_realip_module
--with-http_addition_module enable ngx_http_addition_module
--with-http_xslt_module enable ngx_http_xslt_module
@@ -356,6 +361,7 @@ cat << END
--with-http_dav_module enable ngx_http_dav_module
--with-http_flv_module enable ngx_http_flv_module
--with-http_mp4_module enable ngx_http_mp4_module
+ --with-http_gunzip_module enable ngx_http_gunzip_module
--with-http_gzip_static_module enable ngx_http_gzip_static_module
--with-http_random_index_module enable ngx_http_random_index_module
--with-http_secure_link_module enable ngx_http_secure_link_module
diff --git a/usr.sbin/nginx/auto/sources b/usr.sbin/nginx/auto/sources
index 7a1b4adcf6e..56701ebb973 100644
--- a/usr.sbin/nginx/auto/sources
+++ b/usr.sbin/nginx/auto/sources
@@ -89,7 +89,8 @@ PCRE_SRCS="src/pcre/pcre_chartables.c \
OPENSSL_MODULE=ngx_openssl_module
OPENSSL_DEPS=src/event/ngx_event_openssl.h
-OPENSSL_SRCS=src/event/ngx_event_openssl.c
+OPENSSL_SRCS="src/event/ngx_event_openssl.c \
+ src/event/ngx_event_openssl_stapling.c"
EVENT_MODULES="ngx_events_module ngx_event_core_module"
@@ -335,6 +336,15 @@ HTTP_POSTPONE_FILTER_SRCS=src/http/ngx_http_postpone_filter_module.c
HTTP_FILE_CACHE_SRCS=src/http/ngx_http_file_cache.c
+HTTP_SPDY_MODULE=ngx_http_spdy_module
+HTTP_SPDY_FILTER_MODULE=ngx_http_spdy_filter_module
+HTTP_SPDY_DEPS="src/http/ngx_http_spdy.h \
+ src/http/ngx_http_spdy_module.h"
+HTTP_SPDY_SRCS="src/http/ngx_http_spdy.c \
+ src/http/ngx_http_spdy_module.c \
+ src/http/ngx_http_spdy_filter_module.c"
+
+
HTTP_CHARSET_FILTER_MODULE=ngx_http_charset_filter_module
HTTP_CHARSET_SRCS=src/http/modules/ngx_http_charset_filter_module.c
@@ -343,6 +353,10 @@ HTTP_GZIP_FILTER_MODULE=ngx_http_gzip_filter_module
HTTP_GZIP_SRCS=src/http/modules/ngx_http_gzip_filter_module.c
+HTTP_GUNZIP_FILTER_MODULE=ngx_http_gunzip_filter_module
+HTTP_GUNZIP_SRCS=src/http/modules/ngx_http_gunzip_filter_module.c
+
+
HTTP_SSI_FILTER_MODULE=ngx_http_ssi_filter_module
HTTP_SSI_DEPS=src/http/modules/ngx_http_ssi_filter_module.h
HTTP_SSI_SRCS=src/http/modules/ngx_http_ssi_filter_module.c
diff --git a/usr.sbin/nginx/auto/unix b/usr.sbin/nginx/auto/unix
index b0a0e4c8895..cd4209e7b2f 100755
--- a/usr.sbin/nginx/auto/unix
+++ b/usr.sbin/nginx/auto/unix
@@ -778,3 +778,17 @@ ngx_feature_test="struct stat sb;
openat(AT_FDCWD, \".\", O_RDONLY|O_NOFOLLOW);
fstatat(AT_FDCWD, \".\", &sb, AT_SYMLINK_NOFOLLOW);"
. auto/feature
+
+
+ngx_feature="getaddrinfo()"
+ngx_feature_name="NGX_HAVE_GETADDRINFO"
+ngx_feature_run=no
+ngx_feature_incs="#include <sys/types.h>
+ #include <sys/socket.h>
+ #include <netdb.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test='struct addrinfo *res;
+ if (getaddrinfo("localhost", NULL, NULL, &res) != 0) return 1;
+ freeaddrinfo(res)'
+. auto/feature
diff --git a/usr.sbin/nginx/src/core/nginx.h b/usr.sbin/nginx/src/core/nginx.h
index 3d6e756f292..6b97454f2a2 100644
--- a/usr.sbin/nginx/src/core/nginx.h
+++ b/usr.sbin/nginx/src/core/nginx.h
@@ -9,8 +9,8 @@
#define _NGINX_H_INCLUDED_
-#define nginx_version 1002009
-#define NGINX_VERSION "1.2.9"
+#define nginx_version 1004001
+#define NGINX_VERSION "1.4.1"
#define NGINX_VER "nginx/" NGINX_VERSION
#define NGINX_VAR "NGINX"
diff --git a/usr.sbin/nginx/src/core/ngx_array.c b/usr.sbin/nginx/src/core/ngx_array.c
index 4627a9912f0..4ea226f0682 100644
--- a/usr.sbin/nginx/src/core/ngx_array.c
+++ b/usr.sbin/nginx/src/core/ngx_array.c
@@ -19,16 +19,10 @@ ngx_array_create(ngx_pool_t *p, ngx_uint_t n, size_t size)
return NULL;
}
- a->elts = ngx_palloc(p, n * size);
- if (a->elts == NULL) {
+ if (ngx_array_init(a, p, n, size) != NGX_OK) {
return NULL;
}
- a->nelts = 0;
- a->size = size;
- a->nalloc = n;
- a->pool = p;
-
return a;
}
diff --git a/usr.sbin/nginx/src/core/ngx_array.h b/usr.sbin/nginx/src/core/ngx_array.h
index 6a60e3088dd..a0f2a744021 100644
--- a/usr.sbin/nginx/src/core/ngx_array.h
+++ b/usr.sbin/nginx/src/core/ngx_array.h
@@ -13,13 +13,13 @@
#include <ngx_core.h>
-struct ngx_array_s {
+typedef struct {
void *elts;
ngx_uint_t nelts;
size_t size;
ngx_uint_t nalloc;
ngx_pool_t *pool;
-};
+} ngx_array_t;
ngx_array_t *ngx_array_create(ngx_pool_t *p, ngx_uint_t n, size_t size);
diff --git a/usr.sbin/nginx/src/core/ngx_connection.c b/usr.sbin/nginx/src/core/ngx_connection.c
index 8fae8c122ce..7ed781e0add 100644
--- a/usr.sbin/nginx/src/core/ngx_connection.c
+++ b/usr.sbin/nginx/src/core/ngx_connection.c
@@ -336,10 +336,10 @@ ngx_open_listening_sockets(ngx_cycle_t *cycle)
#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
- if (ls[i].sockaddr->sa_family == AF_INET6 && ls[i].ipv6only) {
+ if (ls[i].sockaddr->sa_family == AF_INET6) {
int ipv6only;
- ipv6only = (ls[i].ipv6only == 1);
+ ipv6only = ls[i].ipv6only;
if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
(const void *) &ipv6only, sizeof(int))
@@ -900,11 +900,9 @@ ngx_close_connection(ngx_connection_t *c)
c->read->closed = 1;
c->write->closed = 1;
- if (c->single_connection) {
- ngx_unlock(&c->lock);
- c->read->locked = 0;
- c->write->locked = 0;
- }
+ ngx_unlock(&c->lock);
+ c->read->locked = 0;
+ c->write->locked = 0;
ngx_mutex_unlock(ngx_posted_events_mutex);
@@ -972,6 +970,10 @@ ngx_reusable_connection(ngx_connection_t *c, ngx_uint_t reusable)
if (c->reusable) {
ngx_queue_remove(&c->queue);
+
+#if (NGX_STAT_STUB)
+ (void) ngx_atomic_fetch_add(ngx_stat_waiting, -1);
+#endif
}
c->reusable = reusable;
@@ -981,6 +983,10 @@ ngx_reusable_connection(ngx_connection_t *c, ngx_uint_t reusable)
ngx_queue_insert_head(
(ngx_queue_t *) &ngx_cycle->reusable_connections_queue, &c->queue);
+
+#if (NGX_STAT_STUB)
+ (void) ngx_atomic_fetch_add(ngx_stat_waiting, 1);
+#endif
}
}
diff --git a/usr.sbin/nginx/src/core/ngx_connection.h b/usr.sbin/nginx/src/core/ngx_connection.h
index 34af12e4f34..3daf2eee4cb 100644
--- a/usr.sbin/nginx/src/core/ngx_connection.h
+++ b/usr.sbin/nginx/src/core/ngx_connection.h
@@ -64,7 +64,7 @@ struct ngx_listening_s {
unsigned addr_ntop:1;
#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
- unsigned ipv6only:2;
+ unsigned ipv6only:1;
#endif
unsigned keepalive:2;
@@ -152,7 +152,6 @@ struct ngx_connection_s {
unsigned log_error:3; /* ngx_connection_log_error_e */
- unsigned single_connection:1;
unsigned unexpected_eof:1;
unsigned timedout:1;
unsigned error:1;
diff --git a/usr.sbin/nginx/src/core/ngx_core.h b/usr.sbin/nginx/src/core/ngx_core.h
index 435ce64e7bb..dfcf2d56cf8 100644
--- a/usr.sbin/nginx/src/core/ngx_core.h
+++ b/usr.sbin/nginx/src/core/ngx_core.h
@@ -15,7 +15,6 @@ typedef struct ngx_cycle_s ngx_cycle_t;
typedef struct ngx_pool_s ngx_pool_t;
typedef struct ngx_chain_s ngx_chain_t;
typedef struct ngx_log_s ngx_log_t;
-typedef struct ngx_array_s ngx_array_t;
typedef struct ngx_open_file_s ngx_open_file_t;
typedef struct ngx_command_s ngx_command_t;
typedef struct ngx_file_s ngx_file_t;
@@ -69,12 +68,12 @@ typedef void (*ngx_connection_handler_pt)(ngx_connection_t *c);
#include <ngx_slab.h>
#include <ngx_inet.h>
#include <ngx_cycle.h>
+#include <ngx_resolver.h>
#if (NGX_OPENSSL)
#include <ngx_event_openssl.h>
#endif
#include <ngx_process_cycle.h>
#include <ngx_conf_file.h>
-#include <ngx_resolver.h>
#include <ngx_open_file_cache.h>
#include <ngx_os.h>
#include <ngx_connection.h>
diff --git a/usr.sbin/nginx/src/core/ngx_crypt.c b/usr.sbin/nginx/src/core/ngx_crypt.c
index b2e25b90101..629d160e8f0 100644
--- a/usr.sbin/nginx/src/core/ngx_crypt.c
+++ b/usr.sbin/nginx/src/core/ngx_crypt.c
@@ -24,6 +24,8 @@ static ngx_int_t ngx_crypt_plain(ngx_pool_t *pool, u_char *key, u_char *salt,
static ngx_int_t ngx_crypt_ssha(ngx_pool_t *pool, u_char *key, u_char *salt,
u_char **encrypted);
+static ngx_int_t ngx_crypt_sha(ngx_pool_t *pool, u_char *key, u_char *salt,
+ u_char **encrypted);
#endif
@@ -43,6 +45,9 @@ ngx_crypt(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)
#if (NGX_HAVE_SHA1)
} else if (ngx_strncmp(salt, "{SSHA}", sizeof("{SSHA}") - 1) == 0) {
return ngx_crypt_ssha(pool, key, salt, encrypted);
+
+ } else if (ngx_strncmp(salt, "{SHA}", sizeof("{SHA}") - 1) == 0) {
+ return ngx_crypt_sha(pool, key, salt, encrypted);
#endif
}
@@ -241,6 +246,38 @@ ngx_crypt_ssha(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)
return NGX_OK;
}
+
+static ngx_int_t
+ngx_crypt_sha(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)
+{
+ size_t len;
+ ngx_str_t encoded, decoded;
+ ngx_sha1_t sha1;
+ u_char digest[20];
+
+ /* "{SHA}" base64(SHA1(key)) */
+
+ decoded.len = sizeof(digest);
+ decoded.data = digest;
+
+ ngx_sha1_init(&sha1);
+ ngx_sha1_update(&sha1, key, ngx_strlen(key));
+ ngx_sha1_final(digest, &sha1);
+
+ len = sizeof("{SHA}") - 1 + ngx_base64_encoded_length(decoded.len) + 1;
+
+ *encrypted = ngx_pnalloc(pool, len);
+ if (*encrypted == NULL) {
+ return NGX_ERROR;
+ }
+
+ encoded.data = ngx_cpymem(*encrypted, "{SHA}", sizeof("{SHA}") - 1);
+ ngx_encode_base64(&encoded, &decoded);
+ encoded.data[encoded.len] = '\0';
+
+ return NGX_OK;
+}
+
#endif /* NGX_HAVE_SHA1 */
#endif /* NGX_CRYPT */
diff --git a/usr.sbin/nginx/src/core/ngx_cycle.h b/usr.sbin/nginx/src/core/ngx_cycle.h
index b55fee0f192..2e4bc4ba278 100644
--- a/usr.sbin/nginx/src/core/ngx_cycle.h
+++ b/usr.sbin/nginx/src/core/ngx_cycle.h
@@ -14,7 +14,7 @@
#ifndef NGX_CYCLE_POOL_SIZE
-#define NGX_CYCLE_POOL_SIZE 16384
+#define NGX_CYCLE_POOL_SIZE NGX_DEFAULT_POOL_SIZE
#endif
diff --git a/usr.sbin/nginx/src/core/ngx_inet.c b/usr.sbin/nginx/src/core/ngx_inet.c
index a4acc8ca5c4..7757ab7d905 100644
--- a/usr.sbin/nginx/src/core/ngx_inet.c
+++ b/usr.sbin/nginx/src/core/ngx_inet.c
@@ -611,11 +611,13 @@ ngx_parse_unix_domain_url(ngx_pool_t *pool, ngx_url_t *u)
static ngx_int_t
ngx_parse_inet_url(ngx_pool_t *pool, ngx_url_t *u)
{
- u_char *p, *host, *port, *last, *uri, *args;
- size_t len;
- ngx_int_t n;
- struct hostent *h;
- struct sockaddr_in *sin;
+ u_char *p, *host, *port, *last, *uri, *args;
+ size_t len;
+ ngx_int_t n;
+ struct sockaddr_in *sin;
+#if (NGX_HAVE_INET6)
+ struct sockaddr_in6 *sin6;
+#endif
u->socklen = sizeof(struct sockaddr_in);
sin = (struct sockaddr_in *) &u->sockaddr;
@@ -705,6 +707,8 @@ ngx_parse_inet_url(ngx_pool_t *pool, ngx_url_t *u)
}
u->no_port = 1;
+ u->port = u->default_port;
+ sin->sin_port = htons(u->default_port);
}
len = last - host;
@@ -714,59 +718,88 @@ ngx_parse_inet_url(ngx_pool_t *pool, ngx_url_t *u)
return NGX_ERROR;
}
- if (len == 1 && *host == '*') {
- len = 0;
- }
-
u->host.len = len;
u->host.data = host;
- if (u->no_resolve) {
+ if (u->listen && len == 1 && *host == '*') {
+ sin->sin_addr.s_addr = INADDR_ANY;
+ u->wildcard = 1;
return NGX_OK;
}
- if (len) {
- sin->sin_addr.s_addr = ngx_inet_addr(host, len);
+ sin->sin_addr.s_addr = ngx_inet_addr(host, len);
- if (sin->sin_addr.s_addr == INADDR_NONE) {
- p = ngx_alloc(++len, pool->log);
- if (p == NULL) {
- return NGX_ERROR;
- }
+ if (sin->sin_addr.s_addr != INADDR_NONE) {
- (void) ngx_cpystrn(p, host, len);
-
- h = gethostbyname((const char *) p);
+ if (sin->sin_addr.s_addr == INADDR_ANY) {
+ u->wildcard = 1;
+ }
- ngx_free(p);
+ u->naddrs = 1;
- if (h == NULL || h->h_addr_list[0] == NULL) {
- u->err = "host not found";
- return NGX_ERROR;
- }
+ u->addrs = ngx_pcalloc(pool, sizeof(ngx_addr_t));
+ if (u->addrs == NULL) {
+ return NGX_ERROR;
+ }
- sin->sin_addr.s_addr = *(in_addr_t *) (h->h_addr_list[0]);
+ sin = ngx_pcalloc(pool, sizeof(struct sockaddr_in));
+ if (sin == NULL) {
+ return NGX_ERROR;
}
- if (sin->sin_addr.s_addr == INADDR_ANY) {
- u->wildcard = 1;
+ ngx_memcpy(sin, u->sockaddr, sizeof(struct sockaddr_in));
+
+ u->addrs[0].sockaddr = (struct sockaddr *) sin;
+ u->addrs[0].socklen = sizeof(struct sockaddr_in);
+
+ p = ngx_pnalloc(pool, u->host.len + sizeof(":65535") - 1);
+ if (p == NULL) {
+ return NGX_ERROR;
}
- } else {
- sin->sin_addr.s_addr = INADDR_ANY;
- u->wildcard = 1;
- }
+ u->addrs[0].name.len = ngx_sprintf(p, "%V:%d",
+ &u->host, u->port) - p;
+ u->addrs[0].name.data = p;
- if (u->no_port) {
- u->port = u->default_port;
- sin->sin_port = htons(u->default_port);
+ return NGX_OK;
}
- if (u->listen) {
+ if (u->no_resolve) {
return NGX_OK;
}
- return ngx_inet_resolve_host(pool, u);
+ if (ngx_inet_resolve_host(pool, u) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ u->family = u->addrs[0].sockaddr->sa_family;
+ u->socklen = u->addrs[0].socklen;
+ ngx_memcpy(u->sockaddr, u->addrs[0].sockaddr, u->addrs[0].socklen);
+
+ switch (u->family) {
+
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *) &u->sockaddr;
+
+ if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
+ u->wildcard = 1;
+ }
+
+ break;
+#endif
+
+ default: /* AF_INET */
+ sin = (struct sockaddr_in *) &u->sockaddr;
+
+ if (sin->sin_addr.s_addr == INADDR_ANY) {
+ u->wildcard = 1;
+ }
+
+ break;
+ }
+
+ return NGX_OK;
}
@@ -832,6 +865,8 @@ ngx_parse_inet6_url(ngx_pool_t *pool, ngx_url_t *u)
} else {
u->no_port = 1;
+ u->port = u->default_port;
+ sin6->sin6_port = htons(u->default_port);
}
}
@@ -854,11 +889,6 @@ ngx_parse_inet6_url(ngx_pool_t *pool, ngx_url_t *u)
u->wildcard = 1;
}
- if (u->no_port) {
- u->port = u->default_port;
- sin6->sin6_port = htons(u->default_port);
- }
-
u->family = AF_INET6;
u->naddrs = 1;
@@ -898,6 +928,150 @@ ngx_parse_inet6_url(ngx_pool_t *pool, ngx_url_t *u)
}
+#if (NGX_HAVE_GETADDRINFO && NGX_HAVE_INET6)
+
+ngx_int_t
+ngx_inet_resolve_host(ngx_pool_t *pool, ngx_url_t *u)
+{
+ u_char *p, *host;
+ size_t len;
+ in_port_t port;
+ ngx_uint_t i;
+ struct addrinfo hints, *res, *rp;
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+
+ port = htons(u->port);
+
+ host = ngx_alloc(u->host.len + 1, pool->log);
+ if (host == NULL) {
+ return NGX_ERROR;
+ }
+
+ (void) ngx_cpystrn(host, u->host.data, u->host.len + 1);
+
+ ngx_memzero(&hints, sizeof(struct addrinfo));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+
+ if (getaddrinfo((char *) host, NULL, &hints, &res) != 0) {
+ u->err = "host not found";
+ ngx_free(host);
+ return NGX_ERROR;
+ }
+
+ ngx_free(host);
+
+ for (i = 0, rp = res; rp != NULL; rp = rp->ai_next) {
+
+ switch (rp->ai_family) {
+
+ case AF_INET:
+ case AF_INET6:
+ break;
+
+ default:
+ continue;
+ }
+
+ i++;
+ }
+
+ if (i == 0) {
+ u->err = "host not found";
+ goto failed;
+ }
+
+ /* MP: ngx_shared_palloc() */
+
+ u->addrs = ngx_pcalloc(pool, i * sizeof(ngx_addr_t));
+ if (u->addrs == NULL) {
+ goto failed;
+ }
+
+ u->naddrs = i;
+
+ i = 0;
+
+ /* AF_INET addresses first */
+
+ for (rp = res; rp != NULL; rp = rp->ai_next) {
+
+ if (rp->ai_family != AF_INET) {
+ continue;
+ }
+
+ sin = ngx_pcalloc(pool, rp->ai_addrlen);
+ if (sin == NULL) {
+ goto failed;
+ }
+
+ ngx_memcpy(sin, rp->ai_addr, rp->ai_addrlen);
+
+ sin->sin_port = port;
+
+ u->addrs[i].sockaddr = (struct sockaddr *) sin;
+ u->addrs[i].socklen = rp->ai_addrlen;
+
+ len = NGX_INET_ADDRSTRLEN + sizeof(":65535") - 1;
+
+ p = ngx_pnalloc(pool, len);
+ if (p == NULL) {
+ goto failed;
+ }
+
+ len = ngx_sock_ntop((struct sockaddr *) sin, p, len, 1);
+
+ u->addrs[i].name.len = len;
+ u->addrs[i].name.data = p;
+
+ i++;
+ }
+
+ for (rp = res; rp != NULL; rp = rp->ai_next) {
+
+ if (rp->ai_family != AF_INET6) {
+ continue;
+ }
+
+ sin6 = ngx_pcalloc(pool, rp->ai_addrlen);
+ if (sin6 == NULL) {
+ goto failed;
+ }
+
+ ngx_memcpy(sin6, rp->ai_addr, rp->ai_addrlen);
+
+ sin6->sin6_port = port;
+
+ u->addrs[i].sockaddr = (struct sockaddr *) sin6;
+ u->addrs[i].socklen = rp->ai_addrlen;
+
+ len = NGX_INET6_ADDRSTRLEN + sizeof("[]:65535") - 1;
+
+ p = ngx_pnalloc(pool, len);
+ if (p == NULL) {
+ goto failed;
+ }
+
+ len = ngx_sock_ntop((struct sockaddr *) sin6, p, len, 1);
+
+ u->addrs[i].name.len = len;
+ u->addrs[i].name.data = p;
+
+ i++;
+ }
+
+ freeaddrinfo(res);
+ return NGX_OK;
+
+failed:
+
+ freeaddrinfo(res);
+ return NGX_ERROR;
+}
+
+#else /* !NGX_HAVE_GETADDRINFO || !NGX_HAVE_INET6 */
+
ngx_int_t
ngx_inet_resolve_host(ngx_pool_t *pool, ngx_url_t *u)
{
@@ -932,12 +1106,7 @@ ngx_inet_resolve_host(ngx_pool_t *pool, ngx_url_t *u)
return NGX_ERROR;
}
- if (u->one_addr == 0) {
- for (i = 0; h->h_addr_list[i] != NULL; i++) { /* void */ }
-
- } else {
- i = 1;
- }
+ for (i = 0; h->h_addr_list[i] != NULL; i++) { /* void */ }
/* MP: ngx_shared_palloc() */
@@ -1010,3 +1179,5 @@ ngx_inet_resolve_host(ngx_pool_t *pool, ngx_url_t *u)
return NGX_OK;
}
+
+#endif /* NGX_HAVE_GETADDRINFO && NGX_HAVE_INET6 */
diff --git a/usr.sbin/nginx/src/core/ngx_inet.h b/usr.sbin/nginx/src/core/ngx_inet.h
index e30dcb51168..6a5a3687d8c 100644
--- a/usr.sbin/nginx/src/core/ngx_inet.h
+++ b/usr.sbin/nginx/src/core/ngx_inet.h
@@ -87,7 +87,7 @@ typedef struct {
unsigned listen:1;
unsigned uri_part:1;
unsigned no_resolve:1;
- unsigned one_addr:1;
+ unsigned one_addr:1; /* compatibility */
unsigned no_port:1;
unsigned wildcard:1;
diff --git a/usr.sbin/nginx/src/event/modules/ngx_devpoll_module.c b/usr.sbin/nginx/src/event/modules/ngx_devpoll_module.c
index d09b5bc07ad..6fdd00233e6 100644
--- a/usr.sbin/nginx/src/event/modules/ngx_devpoll_module.c
+++ b/usr.sbin/nginx/src/event/modules/ngx_devpoll_module.c
@@ -343,7 +343,7 @@ ngx_devpoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,
ngx_fd_t fd;
ngx_err_t err;
ngx_int_t i;
- ngx_uint_t level;
+ ngx_uint_t level, instance;
ngx_event_t *rev, *wev, **queue;
ngx_connection_t *c;
struct pollfd pfd;
@@ -510,7 +510,13 @@ ngx_devpoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,
ngx_locked_post_event(rev, queue);
} else {
+ instance = rev->instance;
+
rev->handler(rev);
+
+ if (c->fd == -1 || rev->instance != instance) {
+ continue;
+ }
}
}
diff --git a/usr.sbin/nginx/src/event/modules/ngx_eventport_module.c b/usr.sbin/nginx/src/event/modules/ngx_eventport_module.c
index d6dcb0bedf4..5f9cf4e35fb 100644
--- a/usr.sbin/nginx/src/event/modules/ngx_eventport_module.c
+++ b/usr.sbin/nginx/src/event/modules/ngx_eventport_module.c
@@ -551,7 +551,7 @@ ngx_eventport_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,
} else {
rev->handler(rev);
- if (ev->closed) {
+ if (ev->closed || ev->instance != instance) {
continue;
}
}
diff --git a/usr.sbin/nginx/src/event/ngx_event.c b/usr.sbin/nginx/src/event/ngx_event.c
index cbae0ee6ad7..b7205f45b07 100644
--- a/usr.sbin/nginx/src/event/ngx_event.c
+++ b/usr.sbin/nginx/src/event/ngx_event.c
@@ -73,6 +73,8 @@ ngx_atomic_t ngx_stat_reading0;
ngx_atomic_t *ngx_stat_reading = &ngx_stat_reading0;
ngx_atomic_t ngx_stat_writing0;
ngx_atomic_t *ngx_stat_writing = &ngx_stat_writing0;
+ngx_atomic_t ngx_stat_waiting0;
+ngx_atomic_t *ngx_stat_waiting = &ngx_stat_waiting0;
#endif
@@ -511,7 +513,8 @@ ngx_event_module_init(ngx_cycle_t *cycle)
+ cl /* ngx_stat_requests */
+ cl /* ngx_stat_active */
+ cl /* ngx_stat_reading */
- + cl; /* ngx_stat_writing */
+ + cl /* ngx_stat_writing */
+ + cl; /* ngx_stat_waiting */
#endif
@@ -558,6 +561,7 @@ ngx_event_module_init(ngx_cycle_t *cycle)
ngx_stat_active = (ngx_atomic_t *) (shared + 6 * cl);
ngx_stat_reading = (ngx_atomic_t *) (shared + 7 * cl);
ngx_stat_writing = (ngx_atomic_t *) (shared + 8 * cl);
+ ngx_stat_waiting = (ngx_atomic_t *) (shared + 9 * cl);
#endif
diff --git a/usr.sbin/nginx/src/event/ngx_event.h b/usr.sbin/nginx/src/event/ngx_event.h
index 2096da234b1..93c457c7b9a 100644
--- a/usr.sbin/nginx/src/event/ngx_event.h
+++ b/usr.sbin/nginx/src/event/ngx_event.h
@@ -511,6 +511,7 @@ extern ngx_atomic_t *ngx_stat_requests;
extern ngx_atomic_t *ngx_stat_active;
extern ngx_atomic_t *ngx_stat_reading;
extern ngx_atomic_t *ngx_stat_writing;
+extern ngx_atomic_t *ngx_stat_waiting;
#endif
diff --git a/usr.sbin/nginx/src/event/ngx_event_connect.c b/usr.sbin/nginx/src/event/ngx_event_connect.c
index 978f39b42a5..e6ae6564e6c 100644
--- a/usr.sbin/nginx/src/event/ngx_event_connect.c
+++ b/usr.sbin/nginx/src/event/ngx_event_connect.c
@@ -84,7 +84,7 @@ ngx_event_connect_peer(ngx_peer_connection_t *pc)
c->log_error = pc->log_error;
- if (pc->sockaddr->sa_family != AF_INET) {
+ if (pc->sockaddr->sa_family == AF_UNIX) {
c->tcp_nopush = NGX_TCP_NOPUSH_DISABLED;
c->tcp_nodelay = NGX_TCP_NODELAY_DISABLED;
diff --git a/usr.sbin/nginx/src/event/ngx_event_openssl.c b/usr.sbin/nginx/src/event/ngx_event_openssl.c
index 5c88e4ad642..62ce12c1435 100644
--- a/usr.sbin/nginx/src/event/ngx_event_openssl.c
+++ b/usr.sbin/nginx/src/event/ngx_event_openssl.c
@@ -82,6 +82,8 @@ ngx_module_t ngx_openssl_module = {
int ngx_ssl_connection_index;
int ngx_ssl_server_conf_index;
int ngx_ssl_session_cache_index;
+int ngx_ssl_certificate_index;
+int ngx_ssl_stapling_index;
ngx_int_t
@@ -137,6 +139,22 @@ ngx_ssl_init(ngx_log_t *log)
return NGX_ERROR;
}
+ ngx_ssl_certificate_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL,
+ NULL);
+ if (ngx_ssl_certificate_index == -1) {
+ ngx_ssl_error(NGX_LOG_ALERT, log, 0,
+ "SSL_CTX_get_ex_new_index() failed");
+ return NGX_ERROR;
+ }
+
+ ngx_ssl_stapling_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL,
+ NULL);
+ if (ngx_ssl_stapling_index == -1) {
+ ngx_ssl_error(NGX_LOG_ALERT, log, 0,
+ "SSL_CTX_get_ex_new_index() failed");
+ return NGX_ERROR;
+ }
+
return NGX_OK;
}
@@ -218,19 +236,89 @@ ngx_int_t
ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
ngx_str_t *key)
{
+ BIO *bio;
+ X509 *x509;
+ u_long n;
+
if (ngx_conf_full_name(cf->cycle, cert, 1) != NGX_OK) {
return NGX_ERROR;
}
- if (SSL_CTX_use_certificate_chain_file(ssl->ctx, (char *) cert->data)
+ /*
+ * we can't use SSL_CTX_use_certificate_chain_file() as it doesn't
+ * allow to access certificate later from SSL_CTX, so we reimplement
+ * it here
+ */
+
+ bio = BIO_new_file((char *) cert->data, "r");
+ if (bio == NULL) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "BIO_new_file(\"%s\") failed", cert->data);
+ return NGX_ERROR;
+ }
+
+ x509 = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL);
+ if (x509 == NULL) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "PEM_read_bio_X509_AUX(\"%s\") failed", cert->data);
+ BIO_free(bio);
+ return NGX_ERROR;
+ }
+
+ if (SSL_CTX_use_certificate(ssl->ctx, x509) == 0) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "SSL_CTX_use_certificate(\"%s\") failed", cert->data);
+ X509_free(x509);
+ BIO_free(bio);
+ return NGX_ERROR;
+ }
+
+ if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_certificate_index, x509)
== 0)
{
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
- "SSL_CTX_use_certificate_chain_file(\"%s\") failed",
- cert->data);
+ "SSL_CTX_set_ex_data() failed");
return NGX_ERROR;
}
+ X509_free(x509);
+
+ /* read rest of the chain */
+
+ for ( ;; ) {
+
+ x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
+ if (x509 == NULL) {
+ n = ERR_peek_last_error();
+
+ if (ERR_GET_LIB(n) == ERR_LIB_PEM
+ && ERR_GET_REASON(n) == PEM_R_NO_START_LINE)
+ {
+ /* end of file */
+ ERR_clear_error();
+ break;
+ }
+
+ /* some real error */
+
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "PEM_read_bio_X509(\"%s\") failed", cert->data);
+ BIO_free(bio);
+ return NGX_ERROR;
+ }
+
+ if (SSL_CTX_add_extra_chain_cert(ssl->ctx, x509) == 0) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "SSL_CTX_add_extra_chain_cert(\"%s\") failed",
+ cert->data);
+ X509_free(x509);
+ BIO_free(bio);
+ return NGX_ERROR;
+ }
+ }
+
+ BIO_free(bio);
+
if (ngx_conf_full_name(cf->cycle, key, 1) != NGX_OK) {
return NGX_ERROR;
}
@@ -297,6 +385,33 @@ ngx_ssl_client_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
ngx_int_t
+ngx_ssl_trusted_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
+ ngx_int_t depth)
+{
+ SSL_CTX_set_verify_depth(ssl->ctx, depth);
+
+ if (cert->len == 0) {
+ return NGX_OK;
+ }
+
+ if (ngx_conf_full_name(cf->cycle, cert, 1) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ if (SSL_CTX_load_verify_locations(ssl->ctx, (char *) cert->data, NULL)
+ == 0)
+ {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "SSL_CTX_load_verify_locations(\"%s\") failed",
+ cert->data);
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
ngx_ssl_crl(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *crl)
{
X509_STORE *store;
@@ -1489,10 +1604,12 @@ ngx_ssl_clear_error(ngx_log_t *log)
void ngx_cdecl
ngx_ssl_error(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, char *fmt, ...)
{
- u_long n;
- va_list args;
- u_char *p, *last;
- u_char errstr[NGX_MAX_CONF_ERRSTR];
+ int flags;
+ u_long n;
+ va_list args;
+ u_char *p, *last;
+ u_char errstr[NGX_MAX_CONF_ERRSTR];
+ const char *data;
last = errstr + NGX_MAX_CONF_ERRSTR;
@@ -1504,14 +1621,14 @@ ngx_ssl_error(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, char *fmt, ...)
for ( ;; ) {
- n = ERR_get_error();
+ n = ERR_peek_error_line_data(NULL, NULL, &data, &flags);
if (n == 0) {
break;
}
if (p >= last) {
- continue;
+ goto next;
}
*p++ = ' ';
@@ -1521,6 +1638,15 @@ ngx_ssl_error(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, char *fmt, ...)
while (p < last && *p) {
p++;
}
+
+ if (p < last && *data && (flags & ERR_TXT_STRING)) {
+ *p++ = ':';
+ p = ngx_cpystrn(p, (u_char *) data, last - p);
+ }
+
+ next:
+
+ (void) ERR_get_error();
}
ngx_log_error(level, log, err, "%s)", errstr);
diff --git a/usr.sbin/nginx/src/event/ngx_event_openssl.h b/usr.sbin/nginx/src/event/ngx_event_openssl.h
index b8061f0ce4b..bf81d2529c9 100644
--- a/usr.sbin/nginx/src/event/ngx_event_openssl.h
+++ b/usr.sbin/nginx/src/event/ngx_event_openssl.h
@@ -17,6 +17,7 @@
#include <openssl/conf.h>
#include <openssl/engine.h>
#include <openssl/evp.h>
+#include <openssl/ocsp.h>
#define NGX_SSL_NAME "OpenSSL"
@@ -101,7 +102,13 @@ ngx_int_t ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl,
ngx_str_t *cert, ngx_str_t *key);
ngx_int_t ngx_ssl_client_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl,
ngx_str_t *cert, ngx_int_t depth);
+ngx_int_t ngx_ssl_trusted_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl,
+ ngx_str_t *cert, ngx_int_t depth);
ngx_int_t ngx_ssl_crl(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *crl);
+ngx_int_t ngx_ssl_stapling(ngx_conf_t *cf, ngx_ssl_t *ssl,
+ ngx_str_t *file, ngx_str_t *responder, ngx_uint_t verify);
+ngx_int_t ngx_ssl_stapling_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl,
+ ngx_resolver_t *resolver, ngx_msec_t resolver_timeout);
RSA *ngx_ssl_rsa512_key_callback(SSL *ssl, int is_export, int key_length);
ngx_int_t ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file);
ngx_int_t ngx_ssl_ecdh_curve(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *name);
@@ -164,6 +171,8 @@ void ngx_ssl_cleanup_ctx(void *data);
extern int ngx_ssl_connection_index;
extern int ngx_ssl_server_conf_index;
extern int ngx_ssl_session_cache_index;
+extern int ngx_ssl_certificate_index;
+extern int ngx_ssl_stapling_index;
#endif /* _NGX_EVENT_OPENSSL_H_INCLUDED_ */
diff --git a/usr.sbin/nginx/src/event/ngx_event_openssl_stapling.c b/usr.sbin/nginx/src/event/ngx_event_openssl_stapling.c
new file mode 100644
index 00000000000..aaa8d8ac44c
--- /dev/null
+++ b/usr.sbin/nginx/src/event/ngx_event_openssl_stapling.c
@@ -0,0 +1,1749 @@
+
+/*
+ * Copyright (C) Maxim Dounin
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_event_connect.h>
+
+
+#ifdef SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB
+
+
+typedef struct {
+ ngx_str_t staple;
+ ngx_msec_t timeout;
+
+ ngx_resolver_t *resolver;
+ ngx_msec_t resolver_timeout;
+
+ ngx_addr_t *addrs;
+ ngx_str_t host;
+ ngx_str_t uri;
+ in_port_t port;
+
+ SSL_CTX *ssl_ctx;
+
+ X509 *cert;
+ X509 *issuer;
+
+ time_t valid;
+
+ unsigned verify:1;
+ unsigned loading:1;
+} ngx_ssl_stapling_t;
+
+
+typedef struct ngx_ssl_ocsp_ctx_s ngx_ssl_ocsp_ctx_t;
+
+struct ngx_ssl_ocsp_ctx_s {
+ X509 *cert;
+ X509 *issuer;
+
+ ngx_uint_t naddrs;
+
+ ngx_addr_t *addrs;
+ ngx_str_t host;
+ ngx_str_t uri;
+ in_port_t port;
+
+ ngx_resolver_t *resolver;
+ ngx_msec_t resolver_timeout;
+
+ ngx_msec_t timeout;
+
+ void (*handler)(ngx_ssl_ocsp_ctx_t *r);
+ void *data;
+
+ ngx_buf_t *request;
+ ngx_buf_t *response;
+ ngx_peer_connection_t peer;
+
+ ngx_int_t (*process)(ngx_ssl_ocsp_ctx_t *r);
+
+ ngx_uint_t state;
+
+ ngx_uint_t code;
+ ngx_uint_t count;
+
+ ngx_uint_t done;
+
+ u_char *header_name_start;
+ u_char *header_name_end;
+ u_char *header_start;
+ u_char *header_end;
+
+ ngx_pool_t *pool;
+ ngx_log_t *log;
+};
+
+
+static ngx_int_t ngx_ssl_stapling_file(ngx_conf_t *cf, ngx_ssl_t *ssl,
+ ngx_str_t *file);
+static ngx_int_t ngx_ssl_stapling_issuer(ngx_conf_t *cf, ngx_ssl_t *ssl);
+static ngx_int_t ngx_ssl_stapling_responder(ngx_conf_t *cf, ngx_ssl_t *ssl,
+ ngx_str_t *responder);
+
+static int ngx_ssl_certificate_status_callback(ngx_ssl_conn_t *ssl_conn,
+ void *data);
+static void ngx_ssl_stapling_update(ngx_ssl_stapling_t *staple);
+static void ngx_ssl_stapling_ocsp_handler(ngx_ssl_ocsp_ctx_t *ctx);
+
+static void ngx_ssl_stapling_cleanup(void *data);
+
+static ngx_ssl_ocsp_ctx_t *ngx_ssl_ocsp_start(void);
+static void ngx_ssl_ocsp_done(ngx_ssl_ocsp_ctx_t *ctx);
+static void ngx_ssl_ocsp_request(ngx_ssl_ocsp_ctx_t *ctx);
+static void ngx_ssl_ocsp_resolve_handler(ngx_resolver_ctx_t *resolve);
+static void ngx_ssl_ocsp_connect(ngx_ssl_ocsp_ctx_t *ctx);
+static void ngx_ssl_ocsp_write_handler(ngx_event_t *wev);
+static void ngx_ssl_ocsp_read_handler(ngx_event_t *rev);
+static void ngx_ssl_ocsp_dummy_handler(ngx_event_t *ev);
+
+static ngx_int_t ngx_ssl_ocsp_create_request(ngx_ssl_ocsp_ctx_t *ctx);
+static ngx_int_t ngx_ssl_ocsp_process_status_line(ngx_ssl_ocsp_ctx_t *ctx);
+static ngx_int_t ngx_ssl_ocsp_parse_status_line(ngx_ssl_ocsp_ctx_t *ctx);
+static ngx_int_t ngx_ssl_ocsp_process_headers(ngx_ssl_ocsp_ctx_t *ctx);
+static ngx_int_t ngx_ssl_ocsp_parse_header_line(ngx_ssl_ocsp_ctx_t *ctx);
+static ngx_int_t ngx_ssl_ocsp_process_body(ngx_ssl_ocsp_ctx_t *ctx);
+
+static u_char *ngx_ssl_ocsp_log_error(ngx_log_t *log, u_char *buf, size_t len);
+
+
+ngx_int_t
+ngx_ssl_stapling(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file,
+ ngx_str_t *responder, ngx_uint_t verify)
+{
+ ngx_int_t rc;
+ ngx_pool_cleanup_t *cln;
+ ngx_ssl_stapling_t *staple;
+
+ staple = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_stapling_t));
+ if (staple == NULL) {
+ return NGX_ERROR;
+ }
+
+ cln = ngx_pool_cleanup_add(cf->pool, 0);
+ if (cln == NULL) {
+ return NGX_ERROR;
+ }
+
+ cln->handler = ngx_ssl_stapling_cleanup;
+ cln->data = staple;
+
+ if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_stapling_index, staple)
+ == 0)
+ {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "SSL_CTX_set_ex_data() failed");
+ return NGX_ERROR;
+ }
+
+ staple->ssl_ctx = ssl->ctx;
+ staple->timeout = 60000;
+ staple->verify = verify;
+
+ if (file->len) {
+ /* use OCSP response from the file */
+
+ if (ngx_ssl_stapling_file(cf, ssl, file) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ goto done;
+ }
+
+ rc = ngx_ssl_stapling_issuer(cf, ssl);
+
+ if (rc == NGX_DECLINED) {
+ return NGX_OK;
+ }
+
+ if (rc != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ rc = ngx_ssl_stapling_responder(cf, ssl, responder);
+
+ if (rc == NGX_DECLINED) {
+ return NGX_OK;
+ }
+
+ if (rc != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+done:
+
+ SSL_CTX_set_tlsext_status_cb(ssl->ctx, ngx_ssl_certificate_status_callback);
+ SSL_CTX_set_tlsext_status_arg(ssl->ctx, staple);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_ssl_stapling_file(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file)
+{
+ BIO *bio;
+ int len;
+ u_char *p, *buf;
+ OCSP_RESPONSE *response;
+ ngx_ssl_stapling_t *staple;
+
+ staple = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_stapling_index);
+
+ if (ngx_conf_full_name(cf->cycle, file, 1) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ bio = BIO_new_file((char *) file->data, "r");
+ if (bio == NULL) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "BIO_new_file(\"%s\") failed", file->data);
+ return NGX_ERROR;
+ }
+
+ response = d2i_OCSP_RESPONSE_bio(bio, NULL);
+ if (response == NULL) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "d2i_OCSP_RESPONSE_bio(\"%s\") failed", file->data);
+ BIO_free(bio);
+ return NGX_ERROR;
+ }
+
+ len = i2d_OCSP_RESPONSE(response, NULL);
+ if (len <= 0) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "i2d_OCSP_RESPONSE(\"%s\") failed", file->data);
+ goto failed;
+ }
+
+ buf = ngx_alloc(len, ssl->log);
+ if (buf == NULL) {
+ goto failed;
+ }
+
+ p = buf;
+ len = i2d_OCSP_RESPONSE(response, &p);
+ if (len <= 0) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "i2d_OCSP_RESPONSE(\"%s\") failed", file->data);
+ ngx_free(buf);
+ goto failed;
+ }
+
+ OCSP_RESPONSE_free(response);
+ BIO_free(bio);
+
+ staple->staple.data = buf;
+ staple->staple.len = len;
+
+ return NGX_OK;
+
+failed:
+
+ OCSP_RESPONSE_free(response);
+ BIO_free(bio);
+
+ return NGX_ERROR;
+}
+
+
+static ngx_int_t
+ngx_ssl_stapling_issuer(ngx_conf_t *cf, ngx_ssl_t *ssl)
+{
+ int i, n, rc;
+ X509 *cert, *issuer;
+ X509_STORE *store;
+ X509_STORE_CTX *store_ctx;
+ STACK_OF(X509) *chain;
+ ngx_ssl_stapling_t *staple;
+
+ staple = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_stapling_index);
+ cert = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index);
+
+#if OPENSSL_VERSION_NUMBER >= 0x10001000L
+ SSL_CTX_get_extra_chain_certs(ssl->ctx, &chain);
+#else
+ chain = ssl->ctx->extra_certs;
+#endif
+
+ n = sk_X509_num(chain);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ssl->log, 0,
+ "SSL get issuer: %d extra certs", n);
+
+ for (i = 0; i < n; i++) {
+ issuer = sk_X509_value(chain, i);
+ if (X509_check_issued(issuer, cert) == X509_V_OK) {
+ CRYPTO_add(&issuer->references, 1, CRYPTO_LOCK_X509);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ssl->log, 0,
+ "SSL get issuer: found %p in extra certs", issuer);
+
+ staple->cert = cert;
+ staple->issuer = issuer;
+
+ return NGX_OK;
+ }
+ }
+
+ store = SSL_CTX_get_cert_store(ssl->ctx);
+ if (store == NULL) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "SSL_CTX_get_cert_store() failed");
+ return NGX_ERROR;
+ }
+
+ store_ctx = X509_STORE_CTX_new();
+ if (store_ctx == NULL) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "X509_STORE_CTX_new() failed");
+ return NGX_ERROR;
+ }
+
+ if (X509_STORE_CTX_init(store_ctx, store, NULL, NULL) == 0) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "X509_STORE_CTX_init() failed");
+ return NGX_ERROR;
+ }
+
+ rc = X509_STORE_CTX_get1_issuer(&issuer, store_ctx, cert);
+
+ if (rc == -1) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "X509_STORE_CTX_get1_issuer() failed");
+ X509_STORE_CTX_free(store_ctx);
+ return NGX_ERROR;
+ }
+
+ if (rc == 0) {
+ ngx_log_error(NGX_LOG_WARN, ssl->log, 0,
+ "\"ssl_stapling\" ignored, issuer certificate not found");
+ X509_STORE_CTX_free(store_ctx);
+ return NGX_DECLINED;
+ }
+
+ X509_STORE_CTX_free(store_ctx);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ssl->log, 0,
+ "SSL get issuer: found %p in cert store", issuer);
+
+ staple->cert = cert;
+ staple->issuer = issuer;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_ssl_stapling_responder(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *responder)
+{
+ ngx_url_t u;
+ char *s;
+ ngx_ssl_stapling_t *staple;
+ STACK_OF(OPENSSL_STRING) *aia;
+
+ staple = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_stapling_index);
+
+ if (responder->len == 0) {
+
+ /* extract OCSP responder URL from certificate */
+
+ aia = X509_get1_ocsp(staple->cert);
+ if (aia == NULL) {
+ ngx_log_error(NGX_LOG_WARN, ssl->log, 0,
+ "\"ssl_stapling\" ignored, "
+ "no OCSP responder URL in the certificate");
+ return NGX_DECLINED;
+ }
+
+#if OPENSSL_VERSION_NUMBER >= 0x10000000L
+ s = sk_OPENSSL_STRING_value(aia, 0);
+#else
+ s = sk_value(aia, 0);
+#endif
+ if (s == NULL) {
+ ngx_log_error(NGX_LOG_WARN, ssl->log, 0,
+ "\"ssl_stapling\" ignored, "
+ "no OCSP responder URL in the certificate");
+ X509_email_free(aia);
+ return NGX_DECLINED;
+ }
+
+ responder->len = ngx_strlen(s);
+ responder->data = ngx_palloc(cf->pool, responder->len);
+ if (responder->data == NULL) {
+ X509_email_free(aia);
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(responder->data, s, responder->len);
+ X509_email_free(aia);
+ }
+
+ ngx_memzero(&u, sizeof(ngx_url_t));
+
+ u.url = *responder;
+ u.default_port = 80;
+ u.uri_part = 1;
+
+ if (u.url.len > 7
+ && ngx_strncasecmp(u.url.data, (u_char *) "http://", 7) == 0)
+ {
+ u.url.len -= 7;
+ u.url.data += 7;
+
+ } else {
+ ngx_log_error(NGX_LOG_WARN, ssl->log, 0,
+ "\"ssl_stapling\" ignored, "
+ "invalid URL prefix in OCSP responder \"%V\"", &u.url);
+ return NGX_DECLINED;
+ }
+
+ if (ngx_parse_url(cf->pool, &u) != NGX_OK) {
+ if (u.err) {
+ ngx_log_error(NGX_LOG_WARN, ssl->log, 0,
+ "\"ssl_stapling\" ignored, "
+ "%s in OCSP responder \"%V\"", u.err, &u.url);
+ return NGX_DECLINED;
+ }
+
+ return NGX_ERROR;
+ }
+
+ staple->addrs = u.addrs;
+ staple->host = u.host;
+ staple->uri = u.uri;
+ staple->port = u.port;
+
+ if (staple->uri.len == 0) {
+ ngx_str_set(&staple->uri, "/");
+ }
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_ssl_stapling_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl,
+ ngx_resolver_t *resolver, ngx_msec_t resolver_timeout)
+{
+ ngx_ssl_stapling_t *staple;
+
+ staple = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_stapling_index);
+
+ staple->resolver = resolver;
+ staple->resolver_timeout = resolver_timeout;
+
+ return NGX_OK;
+}
+
+
+static int
+ngx_ssl_certificate_status_callback(ngx_ssl_conn_t *ssl_conn, void *data)
+{
+ int rc;
+ u_char *p;
+ ngx_connection_t *c;
+ ngx_ssl_stapling_t *staple;
+
+ c = ngx_ssl_get_connection(ssl_conn);
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "SSL certificate status callback");
+
+ staple = data;
+ rc = SSL_TLSEXT_ERR_NOACK;
+
+ if (staple->staple.len) {
+ /* we have to copy ocsp response as OpenSSL will free it by itself */
+
+ p = OPENSSL_malloc(staple->staple.len);
+ if (p == NULL) {
+ ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "OPENSSL_malloc() failed");
+ return SSL_TLSEXT_ERR_NOACK;
+ }
+
+ ngx_memcpy(p, staple->staple.data, staple->staple.len);
+
+ SSL_set_tlsext_status_ocsp_resp(ssl_conn, p, staple->staple.len);
+
+ rc = SSL_TLSEXT_ERR_OK;
+ }
+
+ ngx_ssl_stapling_update(staple);
+
+ return rc;
+}
+
+
+static void
+ngx_ssl_stapling_update(ngx_ssl_stapling_t *staple)
+{
+ ngx_ssl_ocsp_ctx_t *ctx;
+
+ if (staple->host.len == 0
+ || staple->loading || staple->valid >= ngx_time())
+ {
+ return;
+ }
+
+ staple->loading = 1;
+
+ ctx = ngx_ssl_ocsp_start();
+ if (ctx == NULL) {
+ return;
+ }
+
+ ctx->cert = staple->cert;
+ ctx->issuer = staple->issuer;
+
+ ctx->addrs = staple->addrs;
+ ctx->host = staple->host;
+ ctx->uri = staple->uri;
+ ctx->port = staple->port;
+ ctx->timeout = staple->timeout;
+
+ ctx->resolver = staple->resolver;
+ ctx->resolver_timeout = staple->resolver_timeout;
+
+ ctx->handler = ngx_ssl_stapling_ocsp_handler;
+ ctx->data = staple;
+
+ ngx_ssl_ocsp_request(ctx);
+
+ return;
+}
+
+
+static void
+ngx_ssl_stapling_ocsp_handler(ngx_ssl_ocsp_ctx_t *ctx)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x0090707fL
+ const
+#endif
+ u_char *p;
+ int n;
+ size_t len;
+ ngx_str_t response;
+ X509_STORE *store;
+ STACK_OF(X509) *chain;
+ OCSP_CERTID *id;
+ OCSP_RESPONSE *ocsp;
+ OCSP_BASICRESP *basic;
+ ngx_ssl_stapling_t *staple;
+ ASN1_GENERALIZEDTIME *thisupdate, *nextupdate;
+
+ staple = ctx->data;
+ ocsp = NULL;
+ basic = NULL;
+ id = NULL;
+
+ if (ctx->code != 200) {
+ goto error;
+ }
+
+ /* check the response */
+
+ len = ctx->response->last - ctx->response->pos;
+ p = ctx->response->pos;
+
+ ocsp = d2i_OCSP_RESPONSE(NULL, &p, len);
+ if (ocsp == NULL) {
+ ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0,
+ "d2i_OCSP_RESPONSE() failed");
+ goto error;
+ }
+
+ n = OCSP_response_status(ocsp);
+
+ if (n != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
+ ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
+ "OCSP response not successful (%d: %s)",
+ n, OCSP_response_status_str(n));
+ goto error;
+ }
+
+ basic = OCSP_response_get1_basic(ocsp);
+ if (basic == NULL) {
+ ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0,
+ "OCSP_response_get1_basic() failed");
+ goto error;
+ }
+
+ store = SSL_CTX_get_cert_store(staple->ssl_ctx);
+ if (store == NULL) {
+ ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,
+ "SSL_CTX_get_cert_store() failed");
+ goto error;
+ }
+
+#if OPENSSL_VERSION_NUMBER >= 0x10001000L
+ SSL_CTX_get_extra_chain_certs(staple->ssl_ctx, &chain);
+#else
+ chain = staple->ssl_ctx->extra_certs;
+#endif
+
+ if (OCSP_basic_verify(basic, chain, store,
+ staple->verify ? OCSP_TRUSTOTHER : OCSP_NOVERIFY)
+ != 1)
+ {
+ ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0,
+ "OCSP_basic_verify() failed");
+ goto error;
+ }
+
+ id = OCSP_cert_to_id(NULL, ctx->cert, ctx->issuer);
+ if (id == NULL) {
+ ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,
+ "OCSP_cert_to_id() failed");
+ goto error;
+ }
+
+ if (OCSP_resp_find_status(basic, id, &n, NULL, NULL,
+ &thisupdate, &nextupdate)
+ != 1)
+ {
+ ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
+ "certificate status not found in the OCSP response",
+ n, OCSP_response_status_str(n));
+ goto error;
+ }
+
+ if (n != V_OCSP_CERTSTATUS_GOOD) {
+ ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
+ "certificate status \"%s\" in the OCSP response",
+ n, OCSP_cert_status_str(n));
+ goto error;
+ }
+
+ if (OCSP_check_validity(thisupdate, nextupdate, 300, -1) != 1) {
+ ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0,
+ "OCSP_check_validity() failed");
+ goto error;
+ }
+
+ OCSP_CERTID_free(id);
+ OCSP_BASICRESP_free(basic);
+ OCSP_RESPONSE_free(ocsp);
+
+ /* copy the response to memory not in ctx->pool */
+
+ response.len = len;
+ response.data = ngx_alloc(response.len, ctx->log);
+
+ if (response.data == NULL) {
+ goto done;
+ }
+
+ ngx_memcpy(response.data, ctx->response->pos, response.len);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+ "ssl ocsp response, %s, %uz",
+ OCSP_cert_status_str(n), response.len);
+
+ if (staple->staple.data) {
+ ngx_free(staple->staple.data);
+ }
+
+ staple->staple = response;
+
+done:
+
+ staple->loading = 0;
+ staple->valid = ngx_time() + 3600; /* ssl_stapling_valid */
+
+ ngx_ssl_ocsp_done(ctx);
+ return;
+
+error:
+
+ staple->loading = 0;
+ staple->valid = ngx_time() + 300; /* ssl_stapling_err_valid */
+
+ if (id) {
+ OCSP_CERTID_free(id);
+ }
+
+ if (basic) {
+ OCSP_BASICRESP_free(basic);
+ }
+
+ if (ocsp) {
+ OCSP_RESPONSE_free(ocsp);
+ }
+
+ ngx_ssl_ocsp_done(ctx);
+}
+
+
+static void
+ngx_ssl_stapling_cleanup(void *data)
+{
+ ngx_ssl_stapling_t *staple = data;
+
+ if (staple->issuer) {
+ X509_free(staple->issuer);
+ }
+
+ if (staple->staple.data) {
+ ngx_free(staple->staple.data);
+ }
+}
+
+
+static ngx_ssl_ocsp_ctx_t *
+ngx_ssl_ocsp_start(void)
+{
+ ngx_log_t *log;
+ ngx_pool_t *pool;
+ ngx_ssl_ocsp_ctx_t *ctx;
+
+ pool = ngx_create_pool(2048, ngx_cycle->log);
+ if (pool == NULL) {
+ return NULL;
+ }
+
+ ctx = ngx_pcalloc(pool, sizeof(ngx_ssl_ocsp_ctx_t));
+ if (ctx == NULL) {
+ ngx_destroy_pool(pool);
+ return NULL;
+ }
+
+ log = ngx_palloc(pool, sizeof(ngx_log_t));
+ if (log == NULL) {
+ ngx_destroy_pool(pool);
+ return NULL;
+ }
+
+ ctx->pool = pool;
+
+ *log = *ctx->pool->log;
+
+ ctx->pool->log = log;
+ ctx->log = log;
+
+ log->handler = ngx_ssl_ocsp_log_error;
+ log->data = ctx;
+ log->action = "requesting certificate status";
+
+ return ctx;
+}
+
+
+static void
+ngx_ssl_ocsp_done(ngx_ssl_ocsp_ctx_t *ctx)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+ "ssl ocsp done");
+
+ if (ctx->peer.connection) {
+ ngx_close_connection(ctx->peer.connection);
+ }
+
+ ngx_destroy_pool(ctx->pool);
+}
+
+
+static void
+ngx_ssl_ocsp_error(ngx_ssl_ocsp_ctx_t *ctx)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+ "ssl ocsp error");
+
+ ctx->code = 0;
+ ctx->handler(ctx);
+}
+
+
+static void
+ngx_ssl_ocsp_request(ngx_ssl_ocsp_ctx_t *ctx)
+{
+ ngx_resolver_ctx_t *resolve, temp;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+ "ssl ocsp request");
+
+ if (ngx_ssl_ocsp_create_request(ctx) != NGX_OK) {
+ ngx_ssl_ocsp_error(ctx);
+ return;
+ }
+
+ if (ctx->resolver) {
+ /* resolve OCSP responder hostname */
+
+ temp.name = ctx->host;
+
+ resolve = ngx_resolve_start(ctx->resolver, &temp);
+ if (resolve == NULL) {
+ ngx_ssl_ocsp_error(ctx);
+ return;
+ }
+
+ if (resolve == NGX_NO_RESOLVER) {
+ ngx_log_error(NGX_LOG_WARN, ctx->log, 0,
+ "no resolver defined to resolve %V", &ctx->host);
+ goto connect;
+ }
+
+ resolve->name = ctx->host;
+ resolve->type = NGX_RESOLVE_A;
+ resolve->handler = ngx_ssl_ocsp_resolve_handler;
+ resolve->data = ctx;
+ resolve->timeout = ctx->resolver_timeout;
+
+ if (ngx_resolve_name(resolve) != NGX_OK) {
+ ngx_ssl_ocsp_error(ctx);
+ return;
+ }
+
+ return;
+ }
+
+connect:
+
+ ngx_ssl_ocsp_connect(ctx);
+}
+
+
+static void
+ngx_ssl_ocsp_resolve_handler(ngx_resolver_ctx_t *resolve)
+{
+ ngx_ssl_ocsp_ctx_t *ctx = resolve->data;
+
+ u_char *p;
+ size_t len;
+ in_port_t port;
+ ngx_uint_t i;
+ struct sockaddr_in *sin;
+
+ ngx_log_debug0(NGX_LOG_ALERT, ctx->log, 0,
+ "ssl ocsp resolve handler");
+
+ if (resolve->state) {
+ ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
+ "%V could not be resolved (%i: %s)",
+ &resolve->name, resolve->state,
+ ngx_resolver_strerror(resolve->state));
+ goto failed;
+ }
+
+#if (NGX_DEBUG)
+ {
+ in_addr_t addr;
+
+ for (i = 0; i < resolve->naddrs; i++) {
+ addr = ntohl(resolve->addrs[i]);
+
+ ngx_log_debug4(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+ "name was resolved to %ud.%ud.%ud.%ud",
+ (addr >> 24) & 0xff, (addr >> 16) & 0xff,
+ (addr >> 8) & 0xff, addr & 0xff);
+ }
+ }
+#endif
+
+ ctx->naddrs = resolve->naddrs;
+ ctx->addrs = ngx_pcalloc(ctx->pool, ctx->naddrs * sizeof(ngx_addr_t));
+
+ if (ctx->addrs == NULL) {
+ goto failed;
+ }
+
+ port = htons(ctx->port);
+
+ for (i = 0; i < resolve->naddrs; i++) {
+
+ sin = ngx_pcalloc(ctx->pool, sizeof(struct sockaddr_in));
+ if (sin == NULL) {
+ goto failed;
+ }
+
+ sin->sin_family = AF_INET;
+ sin->sin_port = port;
+ sin->sin_addr.s_addr = resolve->addrs[i];
+
+ ctx->addrs[i].sockaddr = (struct sockaddr *) sin;
+ ctx->addrs[i].socklen = sizeof(struct sockaddr_in);
+
+ len = NGX_INET_ADDRSTRLEN + sizeof(":65535") - 1;
+
+ p = ngx_pnalloc(ctx->pool, len);
+ if (p == NULL) {
+ goto failed;
+ }
+
+ len = ngx_sock_ntop((struct sockaddr *) sin, p, len, 1);
+
+ ctx->addrs[i].name.len = len;
+ ctx->addrs[i].name.data = p;
+ }
+
+ ngx_resolve_name_done(resolve);
+
+ ngx_ssl_ocsp_connect(ctx);
+ return;
+
+failed:
+
+ ngx_resolve_name_done(resolve);
+ ngx_ssl_ocsp_error(ctx);
+}
+
+
+static void
+ngx_ssl_ocsp_connect(ngx_ssl_ocsp_ctx_t *ctx)
+{
+ ngx_int_t rc;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+ "ssl ocsp connect");
+
+ /* TODO: use all ip addresses */
+
+ ctx->peer.sockaddr = ctx->addrs[0].sockaddr;
+ ctx->peer.socklen = ctx->addrs[0].socklen;
+ ctx->peer.name = &ctx->addrs[0].name;
+ ctx->peer.get = ngx_event_get_peer;
+ ctx->peer.log = ctx->log;
+ ctx->peer.log_error = NGX_ERROR_ERR;
+
+ rc = ngx_event_connect_peer(&ctx->peer);
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+ "ssl ocsp connect peer done");
+
+ if (rc == NGX_ERROR || rc == NGX_BUSY || rc == NGX_DECLINED) {
+ ngx_ssl_ocsp_error(ctx);
+ return;
+ }
+
+ ctx->peer.connection->data = ctx;
+ ctx->peer.connection->pool = ctx->pool;
+
+ ctx->peer.connection->read->handler = ngx_ssl_ocsp_read_handler;
+ ctx->peer.connection->write->handler = ngx_ssl_ocsp_write_handler;
+
+ ctx->process = ngx_ssl_ocsp_process_status_line;
+
+ ngx_add_timer(ctx->peer.connection->read, ctx->timeout);
+ ngx_add_timer(ctx->peer.connection->write, ctx->timeout);
+
+ if (rc == NGX_OK) {
+ ngx_ssl_ocsp_write_handler(ctx->peer.connection->write);
+ return;
+ }
+}
+
+
+static void
+ngx_ssl_ocsp_write_handler(ngx_event_t *wev)
+{
+ ssize_t n, size;
+ ngx_connection_t *c;
+ ngx_ssl_ocsp_ctx_t *ctx;
+
+ c = wev->data;
+ ctx = c->data;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, wev->log, 0,
+ "ssl ocsp write handler");
+
+ if (wev->timedout) {
+ ngx_log_error(NGX_LOG_ERR, wev->log, NGX_ETIMEDOUT,
+ "OCSP responder timed out");
+ ngx_ssl_ocsp_error(ctx);
+ return;
+ }
+
+ size = ctx->request->last - ctx->request->pos;
+
+ n = ngx_send(c, ctx->request->pos, size);
+
+ if (n == NGX_ERROR) {
+ ngx_ssl_ocsp_error(ctx);
+ return;
+ }
+
+ if (n > 0) {
+ ctx->request->pos += n;
+
+ if (n == size) {
+ wev->handler = ngx_ssl_ocsp_dummy_handler;
+
+ if (wev->timer_set) {
+ ngx_del_timer(wev);
+ }
+
+ if (ngx_handle_write_event(wev, 0) != NGX_OK) {
+ ngx_ssl_ocsp_error(ctx);
+ }
+
+ return;
+ }
+ }
+
+ if (!wev->timer_set) {
+ ngx_add_timer(wev, ctx->timeout);
+ }
+}
+
+
+static void
+ngx_ssl_ocsp_read_handler(ngx_event_t *rev)
+{
+ ssize_t n, size;
+ ngx_int_t rc;
+ ngx_ssl_ocsp_ctx_t *ctx;
+ ngx_connection_t *c;
+
+ c = rev->data;
+ ctx = c->data;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, rev->log, 0,
+ "ssl ocsp read handler");
+
+ if (rev->timedout) {
+ ngx_log_error(NGX_LOG_ERR, rev->log, NGX_ETIMEDOUT,
+ "OCSP responder timed out");
+ ngx_ssl_ocsp_error(ctx);
+ return;
+ }
+
+ if (ctx->response == NULL) {
+ ctx->response = ngx_create_temp_buf(ctx->pool, 16384);
+ if (ctx->response == NULL) {
+ ngx_ssl_ocsp_error(ctx);
+ return;
+ }
+ }
+
+ for ( ;; ) {
+
+ size = ctx->response->end - ctx->response->last;
+
+ n = ngx_recv(c, ctx->response->last, size);
+
+ if (n > 0) {
+ ctx->response->last += n;
+
+ rc = ctx->process(ctx);
+
+ if (rc == NGX_ERROR) {
+ ngx_ssl_ocsp_error(ctx);
+ return;
+ }
+
+ continue;
+ }
+
+ if (n == NGX_AGAIN) {
+
+ if (ngx_handle_read_event(rev, 0) != NGX_OK) {
+ ngx_ssl_ocsp_error(ctx);
+ }
+
+ return;
+ }
+
+ break;
+ }
+
+ ctx->done = 1;
+
+ rc = ctx->process(ctx);
+
+ if (rc == NGX_DONE) {
+ /* ctx->handler() was called */
+ return;
+ }
+
+ ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
+ "OCSP responder prematurely closed connection");
+
+ ngx_ssl_ocsp_error(ctx);
+}
+
+
+static void
+ngx_ssl_ocsp_dummy_handler(ngx_event_t *ev)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0,
+ "ssl ocsp dummy handler");
+}
+
+
+static ngx_int_t
+ngx_ssl_ocsp_create_request(ngx_ssl_ocsp_ctx_t *ctx)
+{
+ int len;
+ u_char *p;
+ uintptr_t escape;
+ ngx_str_t binary, base64;
+ ngx_buf_t *b;
+ OCSP_CERTID *id;
+ OCSP_REQUEST *ocsp;
+
+ ocsp = OCSP_REQUEST_new();
+ if (ocsp == NULL) {
+ ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,
+ "OCSP_REQUEST_new() failed");
+ return NGX_ERROR;
+ }
+
+ id = OCSP_cert_to_id(NULL, ctx->cert, ctx->issuer);
+ if (id == NULL) {
+ ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,
+ "OCSP_cert_to_id() failed");
+ goto failed;
+ }
+
+ if (OCSP_request_add0_id(ocsp, id) == NULL) {
+ ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,
+ "OCSP_request_add0_id() failed");
+ goto failed;
+ }
+
+ len = i2d_OCSP_REQUEST(ocsp, NULL);
+ if (len <= 0) {
+ ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,
+ "i2d_OCSP_REQUEST() failed");
+ goto failed;
+ }
+
+ binary.len = len;
+ binary.data = ngx_palloc(ctx->pool, len);
+ if (binary.data == NULL) {
+ goto failed;
+ }
+
+ p = binary.data;
+ len = i2d_OCSP_REQUEST(ocsp, &p);
+ if (len <= 0) {
+ ngx_ssl_error(NGX_LOG_EMERG, ctx->log, 0,
+ "i2d_OCSP_REQUEST() failed");
+ goto failed;
+ }
+
+ base64.len = ngx_base64_encoded_length(binary.len);
+ base64.data = ngx_palloc(ctx->pool, base64.len);
+ if (base64.data == NULL) {
+ goto failed;
+ }
+
+ ngx_encode_base64(&base64, &binary);
+
+ escape = ngx_escape_uri(NULL, base64.data, base64.len,
+ NGX_ESCAPE_URI_COMPONENT);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+ "ssl ocsp request length %z, escape %d",
+ base64.len, escape);
+
+ len = sizeof("GET ") - 1 + ctx->uri.len + sizeof("/") - 1
+ + base64.len + 2 * escape + sizeof(" HTTP/1.0" CRLF) - 1
+ + sizeof("Host: ") - 1 + ctx->host.len + sizeof(CRLF) - 1
+ + sizeof(CRLF) - 1;
+
+ b = ngx_create_temp_buf(ctx->pool, len);
+ if (b == NULL) {
+ goto failed;
+ }
+
+ p = b->last;
+
+ p = ngx_cpymem(p, "GET ", sizeof("GET ") - 1);
+ p = ngx_cpymem(p, ctx->uri.data, ctx->uri.len);
+
+ if (ctx->uri.data[ctx->uri.len - 1] != '/') {
+ *p++ = '/';
+ }
+
+ if (escape == 0) {
+ p = ngx_cpymem(p, base64.data, base64.len);
+
+ } else {
+ p = (u_char *) ngx_escape_uri(p, base64.data, base64.len,
+ NGX_ESCAPE_URI_COMPONENT);
+ }
+
+ p = ngx_cpymem(p, " HTTP/1.0" CRLF, sizeof(" HTTP/1.0" CRLF) - 1);
+ p = ngx_cpymem(p, "Host: ", sizeof("Host: ") - 1);
+ p = ngx_cpymem(p, ctx->host.data, ctx->host.len);
+ *p++ = CR; *p++ = LF;
+
+ /* add "\r\n" at the header end */
+ *p++ = CR; *p++ = LF;
+
+ b->last = p;
+ ctx->request = b;
+
+ return NGX_OK;
+
+failed:
+
+ OCSP_REQUEST_free(ocsp);
+
+ return NGX_ERROR;
+}
+
+
+static ngx_int_t
+ngx_ssl_ocsp_process_status_line(ngx_ssl_ocsp_ctx_t *ctx)
+{
+ ngx_int_t rc;
+
+ rc = ngx_ssl_ocsp_parse_status_line(ctx);
+
+ if (rc == NGX_OK) {
+#if 0
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+ "ssl ocsp status line \"%*s\"",
+ ctx->response->pos - ctx->response->start,
+ ctx->response->start);
+#endif
+
+ ctx->process = ngx_ssl_ocsp_process_headers;
+ return ctx->process(ctx);
+ }
+
+ if (rc == NGX_AGAIN) {
+ return NGX_AGAIN;
+ }
+
+ /* rc == NGX_ERROR */
+
+ ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
+ "OCSP responder sent invalid response");
+
+ return NGX_ERROR;
+}
+
+
+static ngx_int_t
+ngx_ssl_ocsp_parse_status_line(ngx_ssl_ocsp_ctx_t *ctx)
+{
+ u_char ch;
+ u_char *p;
+ ngx_buf_t *b;
+ enum {
+ sw_start = 0,
+ sw_H,
+ sw_HT,
+ sw_HTT,
+ sw_HTTP,
+ sw_first_major_digit,
+ sw_major_digit,
+ sw_first_minor_digit,
+ sw_minor_digit,
+ sw_status,
+ sw_space_after_status,
+ sw_status_text,
+ sw_almost_done
+ } state;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+ "ssl ocsp process status line");
+
+ state = ctx->state;
+ b = ctx->response;
+
+ for (p = b->pos; p < b->last; p++) {
+ ch = *p;
+
+ switch (state) {
+
+ /* "HTTP/" */
+ case sw_start:
+ switch (ch) {
+ case 'H':
+ state = sw_H;
+ break;
+ default:
+ return NGX_ERROR;
+ }
+ break;
+
+ case sw_H:
+ switch (ch) {
+ case 'T':
+ state = sw_HT;
+ break;
+ default:
+ return NGX_ERROR;
+ }
+ break;
+
+ case sw_HT:
+ switch (ch) {
+ case 'T':
+ state = sw_HTT;
+ break;
+ default:
+ return NGX_ERROR;
+ }
+ break;
+
+ case sw_HTT:
+ switch (ch) {
+ case 'P':
+ state = sw_HTTP;
+ break;
+ default:
+ return NGX_ERROR;
+ }
+ break;
+
+ case sw_HTTP:
+ switch (ch) {
+ case '/':
+ state = sw_first_major_digit;
+ break;
+ default:
+ return NGX_ERROR;
+ }
+ break;
+
+ /* the first digit of major HTTP version */
+ case sw_first_major_digit:
+ if (ch < '1' || ch > '9') {
+ return NGX_ERROR;
+ }
+
+ state = sw_major_digit;
+ break;
+
+ /* the major HTTP version or dot */
+ case sw_major_digit:
+ if (ch == '.') {
+ state = sw_first_minor_digit;
+ break;
+ }
+
+ if (ch < '0' || ch > '9') {
+ return NGX_ERROR;
+ }
+
+ break;
+
+ /* the first digit of minor HTTP version */
+ case sw_first_minor_digit:
+ if (ch < '0' || ch > '9') {
+ return NGX_ERROR;
+ }
+
+ state = sw_minor_digit;
+ break;
+
+ /* the minor HTTP version or the end of the request line */
+ case sw_minor_digit:
+ if (ch == ' ') {
+ state = sw_status;
+ break;
+ }
+
+ if (ch < '0' || ch > '9') {
+ return NGX_ERROR;
+ }
+
+ break;
+
+ /* HTTP status code */
+ case sw_status:
+ if (ch == ' ') {
+ break;
+ }
+
+ if (ch < '0' || ch > '9') {
+ return NGX_ERROR;
+ }
+
+ ctx->code = ctx->code * 10 + ch - '0';
+
+ if (++ctx->count == 3) {
+ state = sw_space_after_status;
+ }
+
+ break;
+
+ /* space or end of line */
+ case sw_space_after_status:
+ switch (ch) {
+ case ' ':
+ state = sw_status_text;
+ break;
+ case '.': /* IIS may send 403.1, 403.2, etc */
+ state = sw_status_text;
+ break;
+ case CR:
+ state = sw_almost_done;
+ break;
+ case LF:
+ goto done;
+ default:
+ return NGX_ERROR;
+ }
+ break;
+
+ /* any text until end of line */
+ case sw_status_text:
+ switch (ch) {
+ case CR:
+ state = sw_almost_done;
+ break;
+ case LF:
+ goto done;
+ }
+ break;
+
+ /* end of status line */
+ case sw_almost_done:
+ switch (ch) {
+ case LF:
+ goto done;
+ default:
+ return NGX_ERROR;
+ }
+ }
+ }
+
+ b->pos = p;
+ ctx->state = state;
+
+ return NGX_AGAIN;
+
+done:
+
+ b->pos = p + 1;
+ ctx->state = sw_start;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_ssl_ocsp_process_headers(ngx_ssl_ocsp_ctx_t *ctx)
+{
+ size_t len;
+ ngx_int_t rc;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+ "ssl ocsp process headers");
+
+ for ( ;; ) {
+ rc = ngx_ssl_ocsp_parse_header_line(ctx);
+
+ if (rc == NGX_OK) {
+
+ ngx_log_debug4(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+ "ssl ocsp header \"%*s: %*s\"",
+ ctx->header_name_end - ctx->header_name_start,
+ ctx->header_name_start,
+ ctx->header_end - ctx->header_start,
+ ctx->header_start);
+
+ len = ctx->header_name_end - ctx->header_name_start;
+
+ if (len == sizeof("Content-Type") - 1
+ && ngx_strncasecmp(ctx->header_name_start,
+ (u_char *) "Content-Type",
+ sizeof("Content-Type") - 1)
+ == 0)
+ {
+ len = ctx->header_end - ctx->header_start;
+
+ if (len != sizeof("application/ocsp-response") - 1
+ || ngx_strncasecmp(ctx->header_start,
+ (u_char *) "application/ocsp-response",
+ sizeof("application/ocsp-response") - 1)
+ != 0)
+ {
+ ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
+ "OCSP responder sent invalid "
+ "\"Content-Type\" header: \"%*s\"",
+ ctx->header_end - ctx->header_start,
+ ctx->header_start);
+ return NGX_ERROR;
+ }
+
+ continue;
+ }
+
+ /* TODO: honor Content-Length */
+
+ continue;
+ }
+
+ if (rc == NGX_DONE) {
+ break;
+ }
+
+ if (rc == NGX_AGAIN) {
+ return NGX_AGAIN;
+ }
+
+ /* rc == NGX_ERROR */
+
+ ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
+ "OCSP responder sent invalid response");
+
+ return NGX_ERROR;
+ }
+
+ ctx->process = ngx_ssl_ocsp_process_body;
+ return ctx->process(ctx);
+}
+
+static ngx_int_t
+ngx_ssl_ocsp_parse_header_line(ngx_ssl_ocsp_ctx_t *ctx)
+{
+ u_char c, ch, *p;
+ enum {
+ sw_start = 0,
+ sw_name,
+ sw_space_before_value,
+ sw_value,
+ sw_space_after_value,
+ sw_almost_done,
+ sw_header_almost_done
+ } state;
+
+ state = ctx->state;
+
+ for (p = ctx->response->pos; p < ctx->response->last; p++) {
+ ch = *p;
+
+#if 0
+ ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+ "s:%d in:'%02Xd:%c'", state, ch, ch);
+#endif
+
+ switch (state) {
+
+ /* first char */
+ case sw_start:
+
+ switch (ch) {
+ case CR:
+ ctx->header_end = p;
+ state = sw_header_almost_done;
+ break;
+ case LF:
+ ctx->header_end = p;
+ goto header_done;
+ default:
+ state = sw_name;
+ ctx->header_name_start = p;
+
+ c = (u_char) (ch | 0x20);
+ if (c >= 'a' && c <= 'z') {
+ break;
+ }
+
+ if (ch >= '0' && ch <= '9') {
+ break;
+ }
+
+ return NGX_ERROR;
+ }
+ break;
+
+ /* header name */
+ case sw_name:
+ c = (u_char) (ch | 0x20);
+ if (c >= 'a' && c <= 'z') {
+ break;
+ }
+
+ if (ch == ':') {
+ ctx->header_name_end = p;
+ state = sw_space_before_value;
+ break;
+ }
+
+ if (ch == '-') {
+ break;
+ }
+
+ if (ch >= '0' && ch <= '9') {
+ break;
+ }
+
+ if (ch == CR) {
+ ctx->header_name_end = p;
+ ctx->header_start = p;
+ ctx->header_end = p;
+ state = sw_almost_done;
+ break;
+ }
+
+ if (ch == LF) {
+ ctx->header_name_end = p;
+ ctx->header_start = p;
+ ctx->header_end = p;
+ goto done;
+ }
+
+ return NGX_ERROR;
+
+ /* space* before header value */
+ case sw_space_before_value:
+ switch (ch) {
+ case ' ':
+ break;
+ case CR:
+ ctx->header_start = p;
+ ctx->header_end = p;
+ state = sw_almost_done;
+ break;
+ case LF:
+ ctx->header_start = p;
+ ctx->header_end = p;
+ goto done;
+ default:
+ ctx->header_start = p;
+ state = sw_value;
+ break;
+ }
+ break;
+
+ /* header value */
+ case sw_value:
+ switch (ch) {
+ case ' ':
+ ctx->header_end = p;
+ state = sw_space_after_value;
+ break;
+ case CR:
+ ctx->header_end = p;
+ state = sw_almost_done;
+ break;
+ case LF:
+ ctx->header_end = p;
+ goto done;
+ }
+ break;
+
+ /* space* before end of header line */
+ case sw_space_after_value:
+ switch (ch) {
+ case ' ':
+ break;
+ case CR:
+ state = sw_almost_done;
+ break;
+ case LF:
+ goto done;
+ default:
+ state = sw_value;
+ break;
+ }
+ break;
+
+ /* end of header line */
+ case sw_almost_done:
+ switch (ch) {
+ case LF:
+ goto done;
+ default:
+ return NGX_ERROR;
+ }
+
+ /* end of header */
+ case sw_header_almost_done:
+ switch (ch) {
+ case LF:
+ goto header_done;
+ default:
+ return NGX_ERROR;
+ }
+ }
+ }
+
+ ctx->response->pos = p;
+ ctx->state = state;
+
+ return NGX_AGAIN;
+
+done:
+
+ ctx->response->pos = p + 1;
+ ctx->state = sw_start;
+
+ return NGX_OK;
+
+header_done:
+
+ ctx->response->pos = p + 1;
+ ctx->state = sw_start;
+
+ return NGX_DONE;
+}
+
+
+static ngx_int_t
+ngx_ssl_ocsp_process_body(ngx_ssl_ocsp_ctx_t *ctx)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+ "ssl ocsp process body");
+
+ if (ctx->done) {
+ ctx->handler(ctx);
+ return NGX_DONE;
+ }
+
+ return NGX_AGAIN;
+}
+
+
+static u_char *
+ngx_ssl_ocsp_log_error(ngx_log_t *log, u_char *buf, size_t len)
+{
+ u_char *p;
+ ngx_ssl_ocsp_ctx_t *ctx;
+
+ p = buf;
+
+ if (log->action) {
+ p = ngx_snprintf(buf, len, " while %s", log->action);
+ len -= p - buf;
+ }
+
+ ctx = log->data;
+
+ if (ctx) {
+ p = ngx_snprintf(p, len, ", responder: %V", &ctx->host);
+ }
+
+ return p;
+}
+
+
+#else
+
+
+ngx_int_t
+ngx_ssl_stapling(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file,
+ ngx_str_t *responder, ngx_uint_t verify)
+{
+ ngx_log_error(NGX_LOG_WARN, ssl->log, 0,
+ "\"ssl_stapling\" ignored, not supported");
+
+ return NGX_OK;
+}
+
+ngx_int_t
+ngx_ssl_stapling_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl,
+ ngx_resolver_t *resolver, ngx_msec_t resolver_timeout)
+{
+ return NGX_OK;
+}
+
+
+#endif
diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_addition_filter_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_addition_filter_module.c
index 24747c564b8..f598ceab3f7 100644
--- a/usr.sbin/nginx/src/http/modules/ngx_http_addition_filter_module.c
+++ b/usr.sbin/nginx/src/http/modules/ngx_http_addition_filter_module.c
@@ -121,6 +121,7 @@ ngx_http_addition_header_filter(ngx_http_request_t *r)
ngx_http_clear_content_length(r);
ngx_http_clear_accept_ranges(r);
+ ngx_http_clear_etag(r);
return ngx_http_next_header_filter(r);
}
diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_chunked_filter_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_chunked_filter_module.c
index 94313a8f650..a7dc5bf4d4b 100644
--- a/usr.sbin/nginx/src/http/modules/ngx_http_chunked_filter_module.c
+++ b/usr.sbin/nginx/src/http/modules/ngx_http_chunked_filter_module.c
@@ -62,6 +62,7 @@ ngx_http_chunked_header_filter(ngx_http_request_t *r)
if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED
|| r->headers_out.status == NGX_HTTP_NO_CONTENT
+ || r->headers_out.status < NGX_HTTP_OK
|| r != r->main
|| (r->method & NGX_HTTP_HEAD))
{
diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_fastcgi_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_fastcgi_module.c
index 42eb86e4994..f386926da64 100644
--- a/usr.sbin/nginx/src/http/modules/ngx_http_fastcgi_module.c
+++ b/usr.sbin/nginx/src/http/modules/ngx_http_fastcgi_module.c
@@ -2091,6 +2091,8 @@ ngx_http_fastcgi_create_loc_conf(ngx_conf_t *cf)
conf->upstream.buffering = NGX_CONF_UNSET;
conf->upstream.ignore_client_abort = NGX_CONF_UNSET;
+ conf->upstream.local = NGX_CONF_UNSET_PTR;
+
conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC;
conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC;
conf->upstream.read_timeout = NGX_CONF_UNSET_MSEC;
@@ -2162,6 +2164,9 @@ ngx_http_fastcgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_conf_merge_value(conf->upstream.ignore_client_abort,
prev->upstream.ignore_client_abort, 0);
+ ngx_conf_merge_ptr_value(conf->upstream.local,
+ prev->upstream.local, NULL);
+
ngx_conf_merge_msec_value(conf->upstream.connect_timeout,
prev->upstream.connect_timeout, 60000);
diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_flv_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_flv_module.c
index 03a74680379..cc2532027cc 100644
--- a/usr.sbin/nginx/src/http/modules/ngx_http_flv_module.c
+++ b/usr.sbin/nginx/src/http/modules/ngx_http_flv_module.c
@@ -194,6 +194,10 @@ ngx_http_flv_handler(ngx_http_request_t *r)
r->headers_out.content_length_n = len;
r->headers_out.last_modified_time = of.mtime;
+ if (ngx_http_set_etag(r) != NGX_OK) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
if (ngx_http_set_content_type(r) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_geo_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_geo_module.c
index a927ab798c1..34c3b190d18 100644
--- a/usr.sbin/nginx/src/http/modules/ngx_http_geo_module.c
+++ b/usr.sbin/nginx/src/http/modules/ngx_http_geo_module.c
@@ -314,18 +314,17 @@ static ngx_int_t
ngx_http_geo_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx,
ngx_addr_t *addr)
{
- ngx_table_elt_t *xfwd;
+ ngx_array_t *xfwd;
if (ngx_http_geo_real_addr(r, ctx, addr) != NGX_OK) {
return NGX_ERROR;
}
- xfwd = r->headers_in.x_forwarded_for;
+ xfwd = &r->headers_in.x_forwarded_for;
- if (xfwd != NULL && ctx->proxies != NULL) {
- (void) ngx_http_get_forwarded_addr(r, addr, xfwd->value.data,
- xfwd->value.len, ctx->proxies,
- ctx->proxy_recursive);
+ if (xfwd->nelts > 0 && ctx->proxies != NULL) {
+ (void) ngx_http_get_forwarded_addr(r, addr, xfwd, NULL,
+ ctx->proxies, ctx->proxy_recursive);
}
return NGX_OK;
@@ -431,14 +430,14 @@ ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
return NGX_CONF_ERROR;
}
- pool = ngx_create_pool(16384, cf->log);
+ pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log);
if (pool == NULL) {
return NGX_CONF_ERROR;
}
ngx_memzero(&ctx, sizeof(ngx_http_geo_conf_ctx_t));
- ctx.temp_pool = ngx_create_pool(16384, cf->log);
+ ctx.temp_pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log);
if (ctx.temp_pool == NULL) {
return NGX_CONF_ERROR;
}
diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_geoip_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_geoip_module.c
index 36410651917..576fc5f3cd3 100644
--- a/usr.sbin/nginx/src/http/modules/ngx_http_geoip_module.c
+++ b/usr.sbin/nginx/src/http/modules/ngx_http_geoip_module.c
@@ -240,19 +240,18 @@ static u_long
ngx_http_geoip_addr(ngx_http_request_t *r, ngx_http_geoip_conf_t *gcf)
{
ngx_addr_t addr;
- ngx_table_elt_t *xfwd;
+ ngx_array_t *xfwd;
struct sockaddr_in *sin;
addr.sockaddr = r->connection->sockaddr;
addr.socklen = r->connection->socklen;
/* addr.name = r->connection->addr_text; */
- xfwd = r->headers_in.x_forwarded_for;
+ xfwd = &r->headers_in.x_forwarded_for;
- if (xfwd != NULL && gcf->proxies != NULL) {
- (void) ngx_http_get_forwarded_addr(r, &addr, xfwd->value.data,
- xfwd->value.len, gcf->proxies,
- gcf->proxy_recursive);
+ if (xfwd->nelts > 0 && gcf->proxies != NULL) {
+ (void) ngx_http_get_forwarded_addr(r, &addr, xfwd, NULL,
+ gcf->proxies, gcf->proxy_recursive);
}
#if (NGX_HAVE_INET6)
@@ -293,7 +292,7 @@ static geoipv6_t
ngx_http_geoip_addr_v6(ngx_http_request_t *r, ngx_http_geoip_conf_t *gcf)
{
ngx_addr_t addr;
- ngx_table_elt_t *xfwd;
+ ngx_array_t *xfwd;
in_addr_t addr4;
struct in6_addr addr6;
struct sockaddr_in *sin;
@@ -303,12 +302,11 @@ ngx_http_geoip_addr_v6(ngx_http_request_t *r, ngx_http_geoip_conf_t *gcf)
addr.socklen = r->connection->socklen;
/* addr.name = r->connection->addr_text; */
- xfwd = r->headers_in.x_forwarded_for;
+ xfwd = &r->headers_in.x_forwarded_for;
- if (xfwd != NULL && gcf->proxies != NULL) {
- (void) ngx_http_get_forwarded_addr(r, &addr, xfwd->value.data,
- xfwd->value.len, gcf->proxies,
- gcf->proxy_recursive);
+ if (xfwd->nelts > 0 && gcf->proxies != NULL) {
+ (void) ngx_http_get_forwarded_addr(r, &addr, xfwd, NULL,
+ gcf->proxies, gcf->proxy_recursive);
}
switch (addr.sockaddr->sa_family) {
diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_gunzip_filter_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_gunzip_filter_module.c
new file mode 100644
index 00000000000..d4e41e4a007
--- /dev/null
+++ b/usr.sbin/nginx/src/http/modules/ngx_http_gunzip_filter_module.c
@@ -0,0 +1,677 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Maxim Dounin
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+#include <zlib.h>
+
+
+typedef struct {
+ ngx_flag_t enable;
+ ngx_bufs_t bufs;
+} ngx_http_gunzip_conf_t;
+
+
+typedef struct {
+ ngx_chain_t *in;
+ ngx_chain_t *free;
+ ngx_chain_t *busy;
+ ngx_chain_t *out;
+ ngx_chain_t **last_out;
+
+ ngx_buf_t *in_buf;
+ ngx_buf_t *out_buf;
+ ngx_int_t bufs;
+
+ unsigned started:1;
+ unsigned flush:4;
+ unsigned redo:1;
+ unsigned done:1;
+ unsigned nomem:1;
+
+ z_stream zstream;
+ ngx_http_request_t *request;
+} ngx_http_gunzip_ctx_t;
+
+
+static ngx_int_t ngx_http_gunzip_filter_inflate_start(ngx_http_request_t *r,
+ ngx_http_gunzip_ctx_t *ctx);
+static ngx_int_t ngx_http_gunzip_filter_add_data(ngx_http_request_t *r,
+ ngx_http_gunzip_ctx_t *ctx);
+static ngx_int_t ngx_http_gunzip_filter_get_buf(ngx_http_request_t *r,
+ ngx_http_gunzip_ctx_t *ctx);
+static ngx_int_t ngx_http_gunzip_filter_inflate(ngx_http_request_t *r,
+ ngx_http_gunzip_ctx_t *ctx);
+static ngx_int_t ngx_http_gunzip_filter_inflate_end(ngx_http_request_t *r,
+ ngx_http_gunzip_ctx_t *ctx);
+
+static void *ngx_http_gunzip_filter_alloc(void *opaque, u_int items,
+ u_int size);
+static void ngx_http_gunzip_filter_free(void *opaque, void *address);
+
+static ngx_int_t ngx_http_gunzip_filter_init(ngx_conf_t *cf);
+static void *ngx_http_gunzip_create_conf(ngx_conf_t *cf);
+static char *ngx_http_gunzip_merge_conf(ngx_conf_t *cf,
+ void *parent, void *child);
+
+
+static ngx_command_t ngx_http_gunzip_filter_commands[] = {
+
+ { ngx_string("gunzip"),
+ 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_gunzip_conf_t, enable),
+ NULL },
+
+ { ngx_string("gunzip_buffers"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
+ ngx_conf_set_bufs_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_gunzip_conf_t, bufs),
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_gunzip_filter_module_ctx = {
+ NULL, /* preconfiguration */
+ ngx_http_gunzip_filter_init, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ ngx_http_gunzip_create_conf, /* create location configuration */
+ ngx_http_gunzip_merge_conf /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_gunzip_filter_module = {
+ NGX_MODULE_V1,
+ &ngx_http_gunzip_filter_module_ctx, /* module context */
+ ngx_http_gunzip_filter_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_http_output_header_filter_pt ngx_http_next_header_filter;
+static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
+
+
+static ngx_int_t
+ngx_http_gunzip_header_filter(ngx_http_request_t *r)
+{
+ ngx_http_gunzip_ctx_t *ctx;
+ ngx_http_gunzip_conf_t *conf;
+
+ conf = ngx_http_get_module_loc_conf(r, ngx_http_gunzip_filter_module);
+
+ /* TODO support multiple content-codings */
+ /* TODO always gunzip - due to configuration or module request */
+ /* TODO ignore content encoding? */
+
+ if (!conf->enable
+ || r->headers_out.content_encoding == NULL
+ || r->headers_out.content_encoding->value.len != 4
+ || ngx_strncasecmp(r->headers_out.content_encoding->value.data,
+ (u_char *) "gzip", 4) != 0)
+ {
+ return ngx_http_next_header_filter(r);
+ }
+
+ r->gzip_vary = 1;
+
+ if (!r->gzip_tested) {
+ if (ngx_http_gzip_ok(r) == NGX_OK) {
+ return ngx_http_next_header_filter(r);
+ }
+
+ } else if (r->gzip_ok) {
+ return ngx_http_next_header_filter(r);
+ }
+
+ ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_gunzip_ctx_t));
+ if (ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_http_set_ctx(r, ctx, ngx_http_gunzip_filter_module);
+
+ ctx->request = r;
+
+ r->filter_need_in_memory = 1;
+
+ r->headers_out.content_encoding->hash = 0;
+ r->headers_out.content_encoding = NULL;
+
+ ngx_http_clear_content_length(r);
+ ngx_http_clear_accept_ranges(r);
+ ngx_http_clear_etag(r);
+
+ return ngx_http_next_header_filter(r);
+}
+
+
+static ngx_int_t
+ngx_http_gunzip_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
+{
+ int rc;
+ ngx_chain_t *cl;
+ ngx_http_gunzip_ctx_t *ctx;
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_gunzip_filter_module);
+
+ if (ctx == NULL || ctx->done) {
+ return ngx_http_next_body_filter(r, in);
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http gunzip filter");
+
+ if (!ctx->started) {
+ if (ngx_http_gunzip_filter_inflate_start(r, ctx) != NGX_OK) {
+ goto failed;
+ }
+ }
+
+ if (in) {
+ if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) {
+ goto failed;
+ }
+ }
+
+ if (ctx->nomem) {
+
+ /* flush busy buffers */
+
+ if (ngx_http_next_body_filter(r, NULL) == NGX_ERROR) {
+ goto failed;
+ }
+
+ cl = NULL;
+
+ ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &cl,
+ (ngx_buf_tag_t) &ngx_http_gunzip_filter_module);
+ ctx->nomem = 0;
+ }
+
+ for ( ;; ) {
+
+ /* cycle while we can write to a client */
+
+ for ( ;; ) {
+
+ /* cycle while there is data to feed zlib and ... */
+
+ rc = ngx_http_gunzip_filter_add_data(r, ctx);
+
+ if (rc == NGX_DECLINED) {
+ break;
+ }
+
+ if (rc == NGX_AGAIN) {
+ continue;
+ }
+
+
+ /* ... there are buffers to write zlib output */
+
+ rc = ngx_http_gunzip_filter_get_buf(r, ctx);
+
+ if (rc == NGX_DECLINED) {
+ break;
+ }
+
+ if (rc == NGX_ERROR) {
+ goto failed;
+ }
+
+ rc = ngx_http_gunzip_filter_inflate(r, ctx);
+
+ if (rc == NGX_OK) {
+ break;
+ }
+
+ if (rc == NGX_ERROR) {
+ goto failed;
+ }
+
+ /* rc == NGX_AGAIN */
+ }
+
+ if (ctx->out == NULL) {
+ return ctx->busy ? NGX_AGAIN : NGX_OK;
+ }
+
+ rc = ngx_http_next_body_filter(r, ctx->out);
+
+ if (rc == NGX_ERROR) {
+ goto failed;
+ }
+
+ ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &ctx->out,
+ (ngx_buf_tag_t) &ngx_http_gunzip_filter_module);
+ ctx->last_out = &ctx->out;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "gunzip out: %p", ctx->out);
+
+ ctx->nomem = 0;
+
+ if (ctx->done) {
+ return rc;
+ }
+ }
+
+ /* unreachable */
+
+failed:
+
+ ctx->done = 1;
+
+ return NGX_ERROR;
+}
+
+
+static ngx_int_t
+ngx_http_gunzip_filter_inflate_start(ngx_http_request_t *r,
+ ngx_http_gunzip_ctx_t *ctx)
+{
+ int rc;
+
+ ctx->zstream.next_in = Z_NULL;
+ ctx->zstream.avail_in = 0;
+
+ ctx->zstream.zalloc = ngx_http_gunzip_filter_alloc;
+ ctx->zstream.zfree = ngx_http_gunzip_filter_free;
+ ctx->zstream.opaque = ctx;
+
+ /* windowBits +16 to decode gzip, zlib 1.2.0.4+ */
+ rc = inflateInit2(&ctx->zstream, MAX_WBITS + 16);
+
+ if (rc != Z_OK) {
+ ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+ "inflateInit2() failed: %d", rc);
+ return NGX_ERROR;
+ }
+
+ ctx->started = 1;
+
+ ctx->last_out = &ctx->out;
+ ctx->flush = Z_NO_FLUSH;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_gunzip_filter_add_data(ngx_http_request_t *r,
+ ngx_http_gunzip_ctx_t *ctx)
+{
+ if (ctx->zstream.avail_in || ctx->flush != Z_NO_FLUSH || ctx->redo) {
+ return NGX_OK;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "gunzip in: %p", ctx->in);
+
+ if (ctx->in == NULL) {
+ return NGX_DECLINED;
+ }
+
+ ctx->in_buf = ctx->in->buf;
+ ctx->in = ctx->in->next;
+
+ ctx->zstream.next_in = ctx->in_buf->pos;
+ ctx->zstream.avail_in = ctx->in_buf->last - ctx->in_buf->pos;
+
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "gunzip in_buf:%p ni:%p ai:%ud",
+ ctx->in_buf,
+ ctx->zstream.next_in, ctx->zstream.avail_in);
+
+ if (ctx->in_buf->last_buf || ctx->in_buf->last_in_chain) {
+ ctx->flush = Z_FINISH;
+
+ } else if (ctx->in_buf->flush) {
+ ctx->flush = Z_SYNC_FLUSH;
+
+ } else if (ctx->zstream.avail_in == 0) {
+ /* ctx->flush == Z_NO_FLUSH */
+ return NGX_AGAIN;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_gunzip_filter_get_buf(ngx_http_request_t *r,
+ ngx_http_gunzip_ctx_t *ctx)
+{
+ ngx_http_gunzip_conf_t *conf;
+
+ if (ctx->zstream.avail_out) {
+ return NGX_OK;
+ }
+
+ conf = ngx_http_get_module_loc_conf(r, ngx_http_gunzip_filter_module);
+
+ if (ctx->free) {
+ ctx->out_buf = ctx->free->buf;
+ ctx->free = ctx->free->next;
+
+ ctx->out_buf->flush = 0;
+
+ } else if (ctx->bufs < conf->bufs.num) {
+
+ ctx->out_buf = ngx_create_temp_buf(r->pool, conf->bufs.size);
+ if (ctx->out_buf == NULL) {
+ return NGX_ERROR;
+ }
+
+ ctx->out_buf->tag = (ngx_buf_tag_t) &ngx_http_gunzip_filter_module;
+ ctx->out_buf->recycled = 1;
+ ctx->bufs++;
+
+ } else {
+ ctx->nomem = 1;
+ return NGX_DECLINED;
+ }
+
+ ctx->zstream.next_out = ctx->out_buf->pos;
+ ctx->zstream.avail_out = conf->bufs.size;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_gunzip_filter_inflate(ngx_http_request_t *r,
+ ngx_http_gunzip_ctx_t *ctx)
+{
+ int rc;
+ ngx_buf_t *b;
+ ngx_chain_t *cl;
+
+ ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "inflate in: ni:%p no:%p ai:%ud ao:%ud fl:%d redo:%d",
+ ctx->zstream.next_in, ctx->zstream.next_out,
+ ctx->zstream.avail_in, ctx->zstream.avail_out,
+ ctx->flush, ctx->redo);
+
+ rc = inflate(&ctx->zstream, ctx->flush);
+
+ if (rc != Z_OK && rc != Z_STREAM_END && rc != Z_BUF_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+ "inflate() failed: %d, %d", ctx->flush, rc);
+ return NGX_ERROR;
+ }
+
+ ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "inflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d",
+ ctx->zstream.next_in, ctx->zstream.next_out,
+ ctx->zstream.avail_in, ctx->zstream.avail_out,
+ rc);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "gunzip in_buf:%p pos:%p",
+ ctx->in_buf, ctx->in_buf->pos);
+
+ if (ctx->zstream.next_in) {
+ ctx->in_buf->pos = ctx->zstream.next_in;
+
+ if (ctx->zstream.avail_in == 0) {
+ ctx->zstream.next_in = NULL;
+ }
+ }
+
+ ctx->out_buf->last = ctx->zstream.next_out;
+
+ if (ctx->zstream.avail_out == 0) {
+
+ /* zlib wants to output some more data */
+
+ cl = ngx_alloc_chain_link(r->pool);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl->buf = ctx->out_buf;
+ cl->next = NULL;
+ *ctx->last_out = cl;
+ ctx->last_out = &cl->next;
+
+ ctx->redo = 1;
+
+ return NGX_AGAIN;
+ }
+
+ ctx->redo = 0;
+
+ if (ctx->flush == Z_SYNC_FLUSH) {
+
+ ctx->flush = Z_NO_FLUSH;
+
+ cl = ngx_alloc_chain_link(r->pool);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ b = ctx->out_buf;
+
+ if (ngx_buf_size(b) == 0) {
+
+ b = ngx_calloc_buf(ctx->request->pool);
+ if (b == NULL) {
+ return NGX_ERROR;
+ }
+
+ } else {
+ ctx->zstream.avail_out = 0;
+ }
+
+ b->flush = 1;
+
+ cl->buf = b;
+ cl->next = NULL;
+ *ctx->last_out = cl;
+ ctx->last_out = &cl->next;
+
+ return NGX_OK;
+ }
+
+ if (rc == Z_STREAM_END && ctx->flush == Z_FINISH
+ && ctx->zstream.avail_in == 0)
+ {
+
+ if (ngx_http_gunzip_filter_inflate_end(r, ctx) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+ }
+
+ if (rc == Z_STREAM_END && ctx->zstream.avail_in > 0) {
+
+ rc = inflateReset(&ctx->zstream);
+
+ if (rc != Z_OK) {
+ ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+ "inflateReset() failed: %d", rc);
+ return NGX_ERROR;
+ }
+
+ ctx->redo = 1;
+
+ return NGX_AGAIN;
+ }
+
+ if (ctx->in == NULL) {
+
+ b = ctx->out_buf;
+
+ if (ngx_buf_size(b) == 0) {
+ return NGX_OK;
+ }
+
+ cl = ngx_alloc_chain_link(r->pool);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ ctx->zstream.avail_out = 0;
+
+ cl->buf = b;
+ cl->next = NULL;
+ *ctx->last_out = cl;
+ ctx->last_out = &cl->next;
+
+ return NGX_OK;
+ }
+
+ return NGX_AGAIN;
+}
+
+
+static ngx_int_t
+ngx_http_gunzip_filter_inflate_end(ngx_http_request_t *r,
+ ngx_http_gunzip_ctx_t *ctx)
+{
+ int rc;
+ ngx_buf_t *b;
+ ngx_chain_t *cl;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "gunzip inflate end");
+
+ rc = inflateEnd(&ctx->zstream);
+
+ if (rc != Z_OK) {
+ ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+ "inflateEnd() failed: %d", rc);
+ return NGX_ERROR;
+ }
+
+ b = ctx->out_buf;
+
+ if (ngx_buf_size(b) == 0) {
+
+ b = ngx_calloc_buf(ctx->request->pool);
+ if (b == NULL) {
+ return NGX_ERROR;
+ }
+ }
+
+ cl = ngx_alloc_chain_link(r->pool);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl->buf = b;
+ cl->next = NULL;
+ *ctx->last_out = cl;
+ ctx->last_out = &cl->next;
+
+ b->last_buf = (r == r->main) ? 1 : 0;
+ b->last_in_chain = 1;
+ b->sync = 1;
+
+ ctx->done = 1;
+
+ return NGX_OK;
+}
+
+
+static void *
+ngx_http_gunzip_filter_alloc(void *opaque, u_int items, u_int size)
+{
+ ngx_http_gunzip_ctx_t *ctx = opaque;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0,
+ "gunzip alloc: n:%ud s:%ud",
+ items, size);
+
+ return ngx_palloc(ctx->request->pool, items * size);
+}
+
+
+static void
+ngx_http_gunzip_filter_free(void *opaque, void *address)
+{
+#if 0
+ ngx_http_gunzip_ctx_t *ctx = opaque;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0,
+ "gunzip free: %p", address);
+#endif
+}
+
+
+static void *
+ngx_http_gunzip_create_conf(ngx_conf_t *cf)
+{
+ ngx_http_gunzip_conf_t *conf;
+
+ conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_gunzip_conf_t));
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ /*
+ * set by ngx_pcalloc():
+ *
+ * conf->bufs.num = 0;
+ */
+
+ conf->enable = NGX_CONF_UNSET;
+
+ return conf;
+}
+
+
+static char *
+ngx_http_gunzip_merge_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_http_gunzip_conf_t *prev = parent;
+ ngx_http_gunzip_conf_t *conf = child;
+
+ ngx_conf_merge_value(conf->enable, prev->enable, 0);
+
+ ngx_conf_merge_bufs_value(conf->bufs, prev->bufs,
+ (128 * 1024) / ngx_pagesize, ngx_pagesize);
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_gunzip_filter_init(ngx_conf_t *cf)
+{
+ ngx_http_next_header_filter = ngx_http_top_header_filter;
+ ngx_http_top_header_filter = ngx_http_gunzip_header_filter;
+
+ ngx_http_next_body_filter = ngx_http_top_body_filter;
+ ngx_http_top_body_filter = ngx_http_gunzip_body_filter;
+
+ return NGX_OK;
+}
diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_gzip_filter_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_gzip_filter_module.c
index f70987ee6fd..35dd07ec473 100644
--- a/usr.sbin/nginx/src/http/modules/ngx_http_gzip_filter_module.c
+++ b/usr.sbin/nginx/src/http/modules/ngx_http_gzip_filter_module.c
@@ -306,6 +306,7 @@ ngx_http_gzip_header_filter(ngx_http_request_t *r)
ngx_http_clear_content_length(r);
ngx_http_clear_accept_ranges(r);
+ ngx_http_clear_etag(r);
return ngx_http_next_header_filter(r);
}
@@ -619,6 +620,8 @@ ngx_http_gzip_filter_deflate_start(ngx_http_request_t *r,
return NGX_ERROR;
}
+ r->connection->buffered |= NGX_HTTP_GZIP_BUFFERED;
+
ctx->last_out = &ctx->out;
ctx->crc32 = crc32(0L, Z_NULL, 0);
ctx->flush = Z_NO_FLUSH;
diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_gzip_static_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_gzip_static_module.c
index b0183e0aaa1..6e777619b43 100644
--- a/usr.sbin/nginx/src/http/modules/ngx_http_gzip_static_module.c
+++ b/usr.sbin/nginx/src/http/modules/ngx_http_gzip_static_module.c
@@ -10,8 +10,13 @@
#include <ngx_http.h>
+#define NGX_HTTP_GZIP_STATIC_OFF 0
+#define NGX_HTTP_GZIP_STATIC_ON 1
+#define NGX_HTTP_GZIP_STATIC_ALWAYS 2
+
+
typedef struct {
- ngx_flag_t enable;
+ ngx_uint_t enable;
} ngx_http_gzip_static_conf_t;
@@ -22,14 +27,22 @@ static char *ngx_http_gzip_static_merge_conf(ngx_conf_t *cf, void *parent,
static ngx_int_t ngx_http_gzip_static_init(ngx_conf_t *cf);
+static ngx_conf_enum_t ngx_http_gzip_static[] = {
+ { ngx_string("off"), NGX_HTTP_GZIP_STATIC_OFF },
+ { ngx_string("on"), NGX_HTTP_GZIP_STATIC_ON },
+ { ngx_string("always"), NGX_HTTP_GZIP_STATIC_ALWAYS },
+ { ngx_null_string, 0 }
+};
+
+
static ngx_command_t ngx_http_gzip_static_commands[] = {
{ ngx_string("gzip_static"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
- ngx_conf_set_flag_slot,
+ ngx_conf_set_enum_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_gzip_static_conf_t, enable),
- NULL },
+ &ngx_http_gzip_static },
ngx_null_command
};
@@ -92,11 +105,17 @@ ngx_http_gzip_static_handler(ngx_http_request_t *r)
gzcf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_static_module);
- if (!gzcf->enable) {
+ if (gzcf->enable == NGX_HTTP_GZIP_STATIC_OFF) {
return NGX_DECLINED;
}
- rc = ngx_http_gzip_ok(r);
+ if (gzcf->enable == NGX_HTTP_GZIP_STATIC_ON) {
+ rc = ngx_http_gzip_ok(r);
+
+ } else {
+ /* always */
+ rc = NGX_OK;
+ }
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
@@ -169,10 +188,12 @@ ngx_http_gzip_static_handler(ngx_http_request_t *r)
return NGX_DECLINED;
}
- r->gzip_vary = 1;
+ if (gzcf->enable == NGX_HTTP_GZIP_STATIC_ON) {
+ r->gzip_vary = 1;
- if (rc != NGX_OK) {
- return NGX_DECLINED;
+ if (rc != NGX_OK) {
+ return NGX_DECLINED;
+ }
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "http static fd: %d", of.fd);
@@ -207,6 +228,10 @@ ngx_http_gzip_static_handler(ngx_http_request_t *r)
r->headers_out.content_length_n = of.size;
r->headers_out.last_modified_time = of.mtime;
+ if (ngx_http_set_etag(r) != NGX_OK) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
if (ngx_http_set_content_type(r) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
@@ -270,7 +295,7 @@ ngx_http_gzip_static_create_conf(ngx_conf_t *cf)
return NULL;
}
- conf->enable = NGX_CONF_UNSET;
+ conf->enable = NGX_CONF_UNSET_UINT;
return conf;
}
@@ -282,7 +307,8 @@ ngx_http_gzip_static_merge_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_http_gzip_static_conf_t *prev = parent;
ngx_http_gzip_static_conf_t *conf = child;
- ngx_conf_merge_value(conf->enable, prev->enable, 0);
+ ngx_conf_merge_uint_value(conf->enable, prev->enable,
+ NGX_HTTP_GZIP_STATIC_OFF);
return NGX_CONF_OK;
}
diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_headers_filter_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_headers_filter_module.c
index 80b77769121..0dfe8101ed6 100644
--- a/usr.sbin/nginx/src/http/modules/ngx_http_headers_filter_module.c
+++ b/usr.sbin/nginx/src/http/modules/ngx_http_headers_filter_module.c
@@ -57,6 +57,8 @@ static ngx_int_t ngx_http_add_header(ngx_http_request_t *r,
ngx_http_header_val_t *hv, ngx_str_t *value);
static ngx_int_t ngx_http_set_last_modified(ngx_http_request_t *r,
ngx_http_header_val_t *hv, ngx_str_t *value);
+static ngx_int_t ngx_http_set_response_header(ngx_http_request_t *r,
+ ngx_http_header_val_t *hv, ngx_str_t *value);
static void *ngx_http_headers_create_conf(ngx_conf_t *cf);
static char *ngx_http_headers_merge_conf(ngx_conf_t *cf,
@@ -76,6 +78,10 @@ static ngx_http_set_header_t ngx_http_set_headers[] = {
offsetof(ngx_http_headers_out_t, last_modified),
ngx_http_set_last_modified },
+ { ngx_string("ETag"),
+ offsetof(ngx_http_headers_out_t, etag),
+ ngx_http_set_response_header },
+
{ ngx_null_string, 0, NULL }
};
@@ -369,32 +375,44 @@ static ngx_int_t
ngx_http_set_last_modified(ngx_http_request_t *r, ngx_http_header_val_t *hv,
ngx_str_t *value)
{
+ if (ngx_http_set_response_header(r, hv, value) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ r->headers_out.last_modified_time =
+ (value->len) ? ngx_http_parse_time(value->data, value->len) : -1;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_set_response_header(ngx_http_request_t *r, ngx_http_header_val_t *hv,
+ ngx_str_t *value)
+{
ngx_table_elt_t *h, **old;
old = (ngx_table_elt_t **) ((char *) &r->headers_out + hv->offset);
- r->headers_out.last_modified_time = -1;
+ if (value->len == 0) {
+ if (*old) {
+ (*old)->hash = 0;
+ *old = NULL;
+ }
- if (*old == NULL) {
+ return NGX_OK;
+ }
- if (value->len == 0) {
- return NGX_OK;
- }
+ if (*old) {
+ h = *old;
+ } else {
h = ngx_list_push(&r->headers_out.headers);
if (h == NULL) {
return NGX_ERROR;
}
*old = h;
-
- } else {
- h = *old;
-
- if (value->len == 0) {
- h->hash = 0;
- return NGX_OK;
- }
}
h->hash = 1;
diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_image_filter_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_image_filter_module.c
index 1550aeecbd7..c6c3b747a32 100644
--- a/usr.sbin/nginx/src/http/modules/ngx_http_image_filter_module.c
+++ b/usr.sbin/nginx/src/http/modules/ngx_http_image_filter_module.c
@@ -45,6 +45,7 @@ typedef struct {
ngx_uint_t sharpen;
ngx_flag_t transparency;
+ ngx_flag_t interlace;
ngx_http_complex_value_t *wcv;
ngx_http_complex_value_t *hcv;
@@ -143,6 +144,13 @@ static ngx_command_t ngx_http_image_filter_commands[] = {
offsetof(ngx_http_image_filter_conf_t, transparency),
NULL },
+ { ngx_string("image_filter_interlace"),
+ 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_image_filter_conf_t, interlace),
+ NULL },
+
{ ngx_string("image_filter_buffer"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,
@@ -963,6 +971,8 @@ transparent:
gdImageSharpen(dst, sharpen);
}
+ gdImageInterlace(dst, (int) conf->interlace);
+
out = ngx_http_image_out(r, ctx->type, dst, &size);
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
@@ -1186,6 +1196,7 @@ ngx_http_image_filter_create_conf(ngx_conf_t *cf)
conf->jpeg_quality = NGX_CONF_UNSET_UINT;
conf->sharpen = NGX_CONF_UNSET_UINT;
conf->transparency = NGX_CONF_UNSET;
+ conf->interlace = NGX_CONF_UNSET;
conf->buffer_size = NGX_CONF_UNSET_SIZE;
return conf;
@@ -1234,6 +1245,8 @@ ngx_http_image_filter_merge_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_conf_merge_value(conf->transparency, prev->transparency, 1);
+ ngx_conf_merge_value(conf->interlace, prev->interlace, 0);
+
ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size,
1 * 1024 * 1024);
diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_limit_conn_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_limit_conn_module.c
index e82ca493dab..7f0eea7ab00 100644
--- a/usr.sbin/nginx/src/http/modules/ngx_http_limit_conn_module.c
+++ b/usr.sbin/nginx/src/http/modules/ngx_http_limit_conn_module.c
@@ -40,6 +40,7 @@ typedef struct {
typedef struct {
ngx_array_t limits;
ngx_uint_t log_level;
+ ngx_uint_t status_code;
} ngx_http_limit_conn_conf_t;
@@ -74,6 +75,11 @@ static ngx_conf_enum_t ngx_http_limit_conn_log_levels[] = {
};
+static ngx_conf_num_bounds_t ngx_http_limit_conn_status_bounds = {
+ ngx_conf_check_num_bounds, 400, 599
+};
+
+
static ngx_command_t ngx_http_limit_conn_commands[] = {
{ ngx_string("limit_conn_zone"),
@@ -104,6 +110,13 @@ static ngx_command_t ngx_http_limit_conn_commands[] = {
offsetof(ngx_http_limit_conn_conf_t, log_level),
&ngx_http_limit_conn_log_levels },
+ { ngx_string("limit_conn_status"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_limit_conn_conf_t, status_code),
+ &ngx_http_limit_conn_status_bounds },
+
ngx_null_command
};
@@ -206,7 +219,7 @@ ngx_http_limit_conn_handler(ngx_http_request_t *r)
if (node == NULL) {
ngx_shmtx_unlock(&shpool->mutex);
ngx_http_limit_conn_cleanup_all(r->pool);
- return NGX_HTTP_SERVICE_UNAVAILABLE;
+ return lccf->status_code;
}
lc = (ngx_http_limit_conn_node_t *) &node->color;
@@ -231,7 +244,7 @@ ngx_http_limit_conn_handler(ngx_http_request_t *r)
&limits[i].shm_zone->shm.name);
ngx_http_limit_conn_cleanup_all(r->pool);
- return NGX_HTTP_SERVICE_UNAVAILABLE;
+ return lccf->status_code;
}
lc->conn++;
@@ -467,6 +480,7 @@ ngx_http_limit_conn_create_conf(ngx_conf_t *cf)
*/
conf->log_level = NGX_CONF_UNSET_UINT;
+ conf->status_code = NGX_CONF_UNSET_UINT;
return conf;
}
@@ -483,6 +497,8 @@ ngx_http_limit_conn_merge_conf(ngx_conf_t *cf, void *parent, void *child)
}
ngx_conf_merge_uint_value(conf->log_level, prev->log_level, NGX_LOG_ERR);
+ ngx_conf_merge_uint_value(conf->status_code, prev->status_code,
+ NGX_HTTP_SERVICE_UNAVAILABLE);
return NGX_CONF_OK;
}
diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_limit_req_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_limit_req_module.c
index 31a8b987a65..90434c956a6 100644
--- a/usr.sbin/nginx/src/http/modules/ngx_http_limit_req_module.c
+++ b/usr.sbin/nginx/src/http/modules/ngx_http_limit_req_module.c
@@ -53,6 +53,7 @@ typedef struct {
ngx_array_t limits;
ngx_uint_t limit_log_level;
ngx_uint_t delay_log_level;
+ ngx_uint_t status_code;
} ngx_http_limit_req_conf_t;
@@ -84,6 +85,11 @@ static ngx_conf_enum_t ngx_http_limit_req_log_levels[] = {
};
+static ngx_conf_num_bounds_t ngx_http_limit_req_status_bounds = {
+ ngx_conf_check_num_bounds, 400, 599
+};
+
+
static ngx_command_t ngx_http_limit_req_commands[] = {
{ ngx_string("limit_req_zone"),
@@ -107,6 +113,13 @@ static ngx_command_t ngx_http_limit_req_commands[] = {
offsetof(ngx_http_limit_req_conf_t, limit_log_level),
&ngx_http_limit_req_log_levels },
+ { ngx_string("limit_req_status"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_limit_req_conf_t, status_code),
+ &ngx_http_limit_req_status_bounds },
+
ngx_null_command
};
@@ -245,7 +258,7 @@ ngx_http_limit_req_handler(ngx_http_request_t *r)
ctx->node = NULL;
}
- return NGX_HTTP_SERVICE_UNAVAILABLE;
+ return lrcf->status_code;
}
/* rc == NGX_AGAIN || rc == NGX_OK */
@@ -682,6 +695,7 @@ ngx_http_limit_req_create_conf(ngx_conf_t *cf)
*/
conf->limit_log_level = NGX_CONF_UNSET_UINT;
+ conf->status_code = NGX_CONF_UNSET_UINT;
return conf;
}
@@ -703,6 +717,9 @@ ngx_http_limit_req_merge_conf(ngx_conf_t *cf, void *parent, void *child)
conf->delay_log_level = (conf->limit_log_level == NGX_LOG_INFO) ?
NGX_LOG_INFO : conf->limit_log_level + 1;
+ ngx_conf_merge_uint_value(conf->status_code, prev->status_code,
+ NGX_HTTP_SERVICE_UNAVAILABLE);
+
return NGX_CONF_OK;
}
@@ -778,7 +795,7 @@ ngx_http_limit_req_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
}
rate = ngx_atoi(value[i].data + 5, len - 5);
- if (rate <= NGX_ERROR) {
+ if (rate <= 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid rate \"%V\"", &value[i]);
return NGX_CONF_ERROR;
diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_map_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_map_module.c
index f5ee7673f16..13c8b97ff66 100644
--- a/usr.sbin/nginx/src/http/modules/ngx_http_map_module.c
+++ b/usr.sbin/nginx/src/http/modules/ngx_http_map_module.c
@@ -227,7 +227,7 @@ ngx_http_map_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
var->get_handler = ngx_http_map_variable;
var->data = (uintptr_t) map;
- pool = ngx_create_pool(16384, cf->log);
+ pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log);
if (pool == NULL) {
return NGX_CONF_ERROR;
}
diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_memcached_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_memcached_module.c
index 5077ded9acb..278b1ed8ad6 100644
--- a/usr.sbin/nginx/src/http/modules/ngx_http_memcached_module.c
+++ b/usr.sbin/nginx/src/http/modules/ngx_http_memcached_module.c
@@ -13,6 +13,7 @@
typedef struct {
ngx_http_upstream_conf_t upstream;
ngx_int_t index;
+ ngx_uint_t gzip_flag;
} ngx_http_memcached_loc_conf_t;
@@ -101,6 +102,13 @@ static ngx_command_t ngx_http_memcached_commands[] = {
offsetof(ngx_http_memcached_loc_conf_t, upstream.next_upstream),
&ngx_http_memcached_next_upstream_masks },
+ { ngx_string("memcached_gzip_flag"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_memcached_loc_conf_t, gzip_flag),
+ NULL },
+
ngx_null_command
};
@@ -281,10 +289,13 @@ ngx_http_memcached_reinit_request(ngx_http_request_t *r)
static ngx_int_t
ngx_http_memcached_process_header(ngx_http_request_t *r)
{
- u_char *p, *len;
- ngx_str_t line;
- ngx_http_upstream_t *u;
- ngx_http_memcached_ctx_t *ctx;
+ u_char *p, *start;
+ ngx_str_t line;
+ ngx_uint_t flags;
+ ngx_table_elt_t *h;
+ ngx_http_upstream_t *u;
+ ngx_http_memcached_ctx_t *ctx;
+ ngx_http_memcached_loc_conf_t *mlcf;
u = r->upstream;
@@ -309,6 +320,7 @@ found:
p = u->buffer.pos;
ctx = ngx_http_get_module_ctx(r, ngx_http_memcached_module);
+ mlcf = ngx_http_get_module_loc_conf(r, ngx_http_memcached_module);
if (ngx_strncmp(p, "VALUE ", sizeof("VALUE ") - 1) == 0) {
@@ -329,23 +341,56 @@ found:
goto no_valid;
}
- /* skip flags */
+ /* flags */
+
+ start = p;
while (*p) {
if (*p++ == ' ') {
- goto length;
+ if (mlcf->gzip_flag) {
+ goto flags;
+ } else {
+ goto length;
+ }
}
}
goto no_valid;
+ flags:
+
+ flags = ngx_atoi(start, p - start - 1);
+
+ if (flags == (ngx_uint_t) NGX_ERROR) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "memcached sent invalid flags in response \"%V\" "
+ "for key \"%V\"",
+ &line, &ctx->key);
+ return NGX_HTTP_UPSTREAM_INVALID_HEADER;
+ }
+
+ if (flags & mlcf->gzip_flag) {
+ h = ngx_list_push(&r->headers_out.headers);
+ if (h == NULL) {
+ return NGX_ERROR;
+ }
+
+ h->hash = 1;
+ h->key.len = sizeof("Content-Encoding") - 1;
+ h->key.data = (u_char *) "Content-Encoding";
+ h->value.len = sizeof("gzip") - 1;
+ h->value.data = (u_char *) "gzip";
+
+ r->headers_out.content_encoding = h;
+ }
+
length:
- len = p;
+ start = p;
while (*p && *p++ != CR) { /* void */ }
- u->headers_in.content_length_n = ngx_atoof(len, p - len - 1);
+ u->headers_in.content_length_n = ngx_atoof(start, p - start - 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\" "
@@ -529,6 +574,7 @@ ngx_http_memcached_create_loc_conf(ngx_conf_t *cf)
* conf->upstream.location = NULL;
*/
+ conf->upstream.local = NGX_CONF_UNSET_PTR;
conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC;
conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC;
conf->upstream.read_timeout = NGX_CONF_UNSET_MSEC;
@@ -550,6 +596,7 @@ ngx_http_memcached_create_loc_conf(ngx_conf_t *cf)
conf->upstream.pass_request_body = 0;
conf->index = NGX_CONF_UNSET;
+ conf->gzip_flag = NGX_CONF_UNSET_UINT;
return conf;
}
@@ -561,6 +608,9 @@ ngx_http_memcached_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_http_memcached_loc_conf_t *prev = parent;
ngx_http_memcached_loc_conf_t *conf = child;
+ ngx_conf_merge_ptr_value(conf->upstream.local,
+ prev->upstream.local, NULL);
+
ngx_conf_merge_msec_value(conf->upstream.connect_timeout,
prev->upstream.connect_timeout, 60000);
@@ -593,6 +643,8 @@ ngx_http_memcached_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
conf->index = prev->index;
}
+ ngx_conf_merge_uint_value(conf->gzip_flag, prev->gzip_flag, 0);
+
return NGX_CONF_OK;
}
diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_mp4_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_mp4_module.c
index e793f77b7fd..20ef51af2b6 100644
--- a/usr.sbin/nginx/src/http/modules/ngx_http_mp4_module.c
+++ b/usr.sbin/nginx/src/http/modules/ngx_http_mp4_module.c
@@ -586,6 +586,10 @@ ngx_http_mp4_handler(ngx_http_request_t *r)
r->headers_out.status = NGX_HTTP_OK;
r->headers_out.last_modified_time = of.mtime;
+ if (ngx_http_set_etag(r) != NGX_OK) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
if (ngx_http_set_content_type(r) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
@@ -1845,14 +1849,6 @@ ngx_http_mp4_read_stsd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
ngx_mp4_get_32value(stsd_atom->entries),
4, stsd_atom->media_name);
- /* supported media format: "avc1" (H.264) and "mp4a" (MPEG-4/AAC) */
-
- if (ngx_strncmp(stsd_atom->media_name, "avc1", 4) != 0
- && ngx_strncmp(stsd_atom->media_name, "mp4a", 4) != 0)
- {
- return NGX_DECLINED;
- }
-
trak = ngx_mp4_last_trak(mp4);
atom = &trak->stsd_atom_buf;
diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_not_modified_filter_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_not_modified_filter_module.c
index 488c36b72d5..7f1ab623694 100644
--- a/usr.sbin/nginx/src/http/modules/ngx_http_not_modified_filter_module.c
+++ b/usr.sbin/nginx/src/http/modules/ngx_http_not_modified_filter_module.c
@@ -10,8 +10,10 @@
#include <ngx_http.h>
-static ngx_int_t ngx_http_test_precondition(ngx_http_request_t *r);
-static ngx_int_t ngx_http_test_not_modified(ngx_http_request_t *r);
+static ngx_uint_t ngx_http_test_if_unmodified(ngx_http_request_t *r);
+static ngx_uint_t ngx_http_test_if_modified(ngx_http_request_t *r);
+static ngx_uint_t ngx_http_test_if_match(ngx_http_request_t *r,
+ ngx_table_elt_t *header);
static ngx_int_t ngx_http_not_modified_filter_init(ngx_conf_t *cf);
@@ -59,20 +61,56 @@ ngx_http_not_modified_header_filter(ngx_http_request_t *r)
return ngx_http_next_header_filter(r);
}
- if (r->headers_in.if_unmodified_since) {
- return ngx_http_test_precondition(r);
+ if (r->headers_in.if_unmodified_since
+ && !ngx_http_test_if_unmodified(r))
+ {
+ return ngx_http_filter_finalize_request(r, NULL,
+ NGX_HTTP_PRECONDITION_FAILED);
}
- if (r->headers_in.if_modified_since) {
- return ngx_http_test_not_modified(r);
+ if (r->headers_in.if_match
+ && !ngx_http_test_if_match(r, r->headers_in.if_match))
+ {
+ return ngx_http_filter_finalize_request(r, NULL,
+ NGX_HTTP_PRECONDITION_FAILED);
+ }
+
+ if (r->headers_in.if_modified_since || r->headers_in.if_none_match) {
+
+ if (r->headers_in.if_modified_since
+ && ngx_http_test_if_modified(r))
+ {
+ return ngx_http_next_header_filter(r);
+ }
+
+ if (r->headers_in.if_none_match
+ && !ngx_http_test_if_match(r, r->headers_in.if_none_match))
+ {
+ return ngx_http_next_header_filter(r);
+ }
+
+ /* not modified */
+
+ r->headers_out.status = NGX_HTTP_NOT_MODIFIED;
+ r->headers_out.status_line.len = 0;
+ r->headers_out.content_type.len = 0;
+ ngx_http_clear_content_length(r);
+ ngx_http_clear_accept_ranges(r);
+
+ if (r->headers_out.content_encoding) {
+ r->headers_out.content_encoding->hash = 0;
+ r->headers_out.content_encoding = NULL;
+ }
+
+ return ngx_http_next_header_filter(r);
}
return ngx_http_next_header_filter(r);
}
-static ngx_int_t
-ngx_http_test_precondition(ngx_http_request_t *r)
+static ngx_uint_t
+ngx_http_test_if_unmodified(ngx_http_request_t *r)
{
time_t iums;
@@ -83,16 +121,15 @@ ngx_http_test_precondition(ngx_http_request_t *r)
"http iums:%d lm:%d", iums, r->headers_out.last_modified_time);
if (iums >= r->headers_out.last_modified_time) {
- return ngx_http_next_header_filter(r);
+ return 1;
}
- return ngx_http_filter_finalize_request(r, NULL,
- NGX_HTTP_PRECONDITION_FAILED);
+ return 0;
}
-static ngx_int_t
-ngx_http_test_not_modified(ngx_http_request_t *r)
+static ngx_uint_t
+ngx_http_test_if_modified(ngx_http_request_t *r)
{
time_t ims;
ngx_http_core_loc_conf_t *clcf;
@@ -100,7 +137,7 @@ ngx_http_test_not_modified(ngx_http_request_t *r)
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
if (clcf->if_modified_since == NGX_HTTP_IMS_OFF) {
- return ngx_http_next_header_filter(r);
+ return 1;
}
ims = ngx_http_parse_time(r->headers_in.if_modified_since->value.data,
@@ -109,27 +146,87 @@ ngx_http_test_not_modified(ngx_http_request_t *r)
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http ims:%d lm:%d", ims, r->headers_out.last_modified_time);
- if (ims != r->headers_out.last_modified_time) {
+ if (ims == r->headers_out.last_modified_time) {
+ return 0;
+ }
- if (clcf->if_modified_since == NGX_HTTP_IMS_EXACT
- || ims < r->headers_out.last_modified_time)
- {
- return ngx_http_next_header_filter(r);
- }
+ if (clcf->if_modified_since == NGX_HTTP_IMS_EXACT
+ || ims < r->headers_out.last_modified_time)
+ {
+ return 1;
}
- r->headers_out.status = NGX_HTTP_NOT_MODIFIED;
- r->headers_out.status_line.len = 0;
- r->headers_out.content_type.len = 0;
- ngx_http_clear_content_length(r);
- ngx_http_clear_accept_ranges(r);
+ return 0;
+}
+
- if (r->headers_out.content_encoding) {
- r->headers_out.content_encoding->hash = 0;
- r->headers_out.content_encoding = NULL;
+static ngx_uint_t
+ngx_http_test_if_match(ngx_http_request_t *r, ngx_table_elt_t *header)
+{
+ u_char *start, *end, ch;
+ ngx_str_t *etag, *list;
+
+ list = &header->value;
+
+ if (list->len == 1 && list->data[0] == '*') {
+ return 1;
}
- return ngx_http_next_header_filter(r);
+ if (r->headers_out.etag == NULL) {
+ return 0;
+ }
+
+ etag = &r->headers_out.etag->value;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http im:\"%V\" etag:%V", list, etag);
+
+ start = list->data;
+ end = list->data + list->len;
+
+ while (start < end) {
+
+ if (etag->len > (size_t) (end - start)) {
+ return 0;
+ }
+
+ if (ngx_strncmp(start, etag->data, etag->len) != 0) {
+ goto skip;
+ }
+
+ start += etag->len;
+
+ while (start < end) {
+ ch = *start;
+
+ if (ch == ' ' || ch == '\t') {
+ start++;
+ continue;
+ }
+
+ break;
+ }
+
+ if (start == end || *start == ',') {
+ return 1;
+ }
+
+ skip:
+
+ while (start < end && *start != ',') { start++; }
+ while (start < end) {
+ ch = *start;
+
+ if (ch == ' ' || ch == '\t' || ch == ',') {
+ start++;
+ continue;
+ }
+
+ break;
+ }
+ }
+
+ return 0;
}
diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_proxy_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_proxy_module.c
index 0566213db83..eadc8c480bb 100644
--- a/usr.sbin/nginx/src/http/modules/ngx_http_proxy_module.c
+++ b/usr.sbin/nginx/src/http/modules/ngx_http_proxy_module.c
@@ -81,12 +81,9 @@ typedef struct {
typedef struct {
ngx_http_status_t status;
+ ngx_http_chunked_t chunked;
ngx_http_proxy_vars_t vars;
- size_t internal_body_length;
-
- ngx_uint_t state;
- off_t size;
- off_t length;
+ off_t internal_body_length;
ngx_uint_t head; /* unsigned head:1 */
} ngx_http_proxy_ctx_t;
@@ -558,6 +555,8 @@ static char ngx_http_proxy_version_11[] = " HTTP/1.1" CRLF;
static ngx_keyval_t ngx_http_proxy_headers[] = {
{ ngx_string("Host"), ngx_string("$proxy_host") },
{ ngx_string("Connection"), ngx_string("close") },
+ { ngx_string("Content-Length"), ngx_string("$proxy_internal_body_length") },
+ { ngx_string("Transfer-Encoding"), ngx_string("") },
{ ngx_string("Keep-Alive"), ngx_string("") },
{ ngx_string("Expect"), ngx_string("") },
{ ngx_string("Upgrade"), ngx_string("") },
@@ -583,6 +582,8 @@ static ngx_str_t ngx_http_proxy_hide_headers[] = {
static ngx_keyval_t ngx_http_proxy_cache_headers[] = {
{ ngx_string("Host"), ngx_string("$proxy_host") },
{ ngx_string("Connection"), ngx_string("close") },
+ { ngx_string("Content-Length"), ngx_string("$proxy_internal_body_length") },
+ { ngx_string("Transfer-Encoding"), ngx_string("") },
{ ngx_string("Keep-Alive"), ngx_string("") },
{ ngx_string("Expect"), ngx_string("") },
{ ngx_string("Upgrade"), ngx_string("") },
@@ -1006,6 +1007,9 @@ ngx_http_proxy_create_request(ngx_http_request_t *r)
ctx->internal_body_length = body_len;
len += body_len;
+
+ } else {
+ ctx->internal_body_length = r->headers_in.content_length_n;
}
le.ip = plcf->headers_set_len->elts;
@@ -1252,7 +1256,7 @@ 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;
+ ctx->chunked.state = 0;
r->upstream->process_header = ngx_http_proxy_process_status_line;
r->upstream->pipe->input_filter = ngx_http_proxy_copy_filter;
@@ -1470,6 +1474,14 @@ ngx_http_proxy_process_header(ngx_http_request_t *r)
u->keepalive = !u->headers_in.connection_close;
}
+ if (u->headers_in.status_n == NGX_HTTP_SWITCHING_PROTOCOLS) {
+ u->keepalive = 0;
+
+ if (r->headers_in.upgrade) {
+ u->upgrade = 1;
+ }
+ }
+
return NGX_OK;
}
@@ -1618,269 +1630,6 @@ ngx_http_proxy_copy_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
}
-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);
-
- if (ctx == NULL) {
- return NGX_ERROR;
- }
-
- 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 ';':
- case ' ':
- case '\t':
- 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 ';':
- case ' ':
- case '\t':
- 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;
-
- }
-
- if (ctx->size < 0 || ctx->length < 0) {
- goto invalid;
- }
-
- return rc;
-
-done:
-
- return NGX_DONE;
-
-invalid:
-
- return NGX_ERROR;
-}
-
-
static ngx_int_t
ngx_http_proxy_chunked_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
{
@@ -1906,7 +1655,7 @@ ngx_http_proxy_chunked_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
for ( ;; ) {
- rc = ngx_http_proxy_parse_chunked(r, buf);
+ rc = ngx_http_parse_chunked(r, buf, &ctx->chunked);
if (rc == NGX_OK) {
@@ -1957,16 +1706,16 @@ ngx_http_proxy_chunked_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
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) {
+ if (buf->last - buf->pos >= ctx->chunked.size) {
- buf->pos += ctx->size;
+ buf->pos += ctx->chunked.size;
b->last = buf->pos;
- ctx->size = 0;
+ ctx->chunked.size = 0;
continue;
}
- ctx->size -= buf->last - buf->pos;
+ ctx->chunked.size -= buf->last - buf->pos;
buf->pos = buf->last;
b->last = buf->last;
@@ -1987,7 +1736,7 @@ ngx_http_proxy_chunked_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
/* set p->length, minimal amount of data we want to see */
- p->length = ctx->length;
+ p->length = ctx->chunked.length;
break;
}
@@ -2002,7 +1751,7 @@ ngx_http_proxy_chunked_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy chunked state %d, length %d",
- ctx->state, p->length);
+ ctx->chunked.state, p->length);
if (b) {
b->shadow = buf;
@@ -2099,7 +1848,7 @@ ngx_http_proxy_non_buffered_chunked_filter(void *data, ssize_t bytes)
for ( ;; ) {
- rc = ngx_http_proxy_parse_chunked(r, buf);
+ rc = ngx_http_parse_chunked(r, buf, &ctx->chunked);
if (rc == NGX_OK) {
@@ -2121,13 +1870,13 @@ ngx_http_proxy_non_buffered_chunked_filter(void *data, ssize_t bytes)
b->pos = buf->pos;
b->tag = u->output.tag;
- if (buf->last - buf->pos >= ctx->size) {
- buf->pos += ctx->size;
+ if (buf->last - buf->pos >= ctx->chunked.size) {
+ buf->pos += ctx->chunked.size;
b->last = buf->pos;
- ctx->size = 0;
+ ctx->chunked.size = 0;
} else {
- ctx->size -= buf->last - buf->pos;
+ ctx->chunked.size -= buf->last - buf->pos;
buf->pos = buf->last;
b->last = buf->last;
}
@@ -2265,32 +2014,44 @@ static ngx_int_t
ngx_http_proxy_add_x_forwarded_for_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
- u_char *p;
+ size_t len;
+ u_char *p;
+ ngx_uint_t i, n;
+ ngx_table_elt_t **h;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
- if (r->headers_in.x_forwarded_for == NULL) {
+ n = r->headers_in.x_forwarded_for.nelts;
+ h = r->headers_in.x_forwarded_for.elts;
+
+ len = 0;
+
+ for (i = 0; i < n; i++) {
+ len += h[i]->value.len + sizeof(", ") - 1;
+ }
+
+ if (len == 0) {
v->len = r->connection->addr_text.len;
v->data = r->connection->addr_text.data;
return NGX_OK;
}
- v->len = r->headers_in.x_forwarded_for->value.len
- + sizeof(", ") - 1 + r->connection->addr_text.len;
+ len += r->connection->addr_text.len;
- p = ngx_pnalloc(r->pool, v->len);
+ p = ngx_pnalloc(r->pool, len);
if (p == NULL) {
return NGX_ERROR;
}
+ v->len = len;
v->data = p;
- p = ngx_copy(p, r->headers_in.x_forwarded_for->value.data,
- r->headers_in.x_forwarded_for->value.len);
-
- *p++ = ','; *p++ = ' ';
+ for (i = 0; i < n; i++) {
+ p = ngx_copy(p, h[i]->value.data, h[i]->value.len);
+ *p++ = ','; *p++ = ' ';
+ }
ngx_memcpy(p, r->connection->addr_text.data, r->connection->addr_text.len);
@@ -2306,7 +2067,7 @@ ngx_http_proxy_internal_body_length_variable(ngx_http_request_t *r,
ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
- if (ctx == NULL) {
+ if (ctx == NULL || ctx->internal_body_length < 0) {
v->not_found = 1;
return NGX_OK;
}
@@ -2315,13 +2076,13 @@ ngx_http_proxy_internal_body_length_variable(ngx_http_request_t *r,
v->no_cacheable = 0;
v->not_found = 0;
- v->data = ngx_pnalloc(r->connection->pool, NGX_SIZE_T_LEN);
+ v->data = ngx_pnalloc(r->connection->pool, NGX_OFF_T_LEN);
if (v->data == NULL) {
return NGX_ERROR;
}
- v->len = ngx_sprintf(v->data, "%uz", ctx->internal_body_length) - v->data;
+ v->len = ngx_sprintf(v->data, "%O", ctx->internal_body_length) - v->data;
return NGX_OK;
}
@@ -2628,6 +2389,8 @@ ngx_http_proxy_create_loc_conf(ngx_conf_t *cf)
conf->upstream.buffering = NGX_CONF_UNSET;
conf->upstream.ignore_client_abort = NGX_CONF_UNSET;
+ conf->upstream.local = NGX_CONF_UNSET_PTR;
+
conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC;
conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC;
conf->upstream.read_timeout = NGX_CONF_UNSET_MSEC;
@@ -2712,6 +2475,9 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_conf_merge_value(conf->upstream.ignore_client_abort,
prev->upstream.ignore_client_abort, 0);
+ ngx_conf_merge_ptr_value(conf->upstream.local,
+ prev->upstream.local, NULL);
+
ngx_conf_merge_msec_value(conf->upstream.connect_timeout,
prev->upstream.connect_timeout, 60000);
@@ -3090,8 +2856,6 @@ ngx_http_proxy_merge_headers(ngx_conf_t *cf, ngx_http_proxy_loc_conf_t *conf,
}
if (conf->headers_set_hash.buckets
- && ((conf->body_source.data == NULL)
- == (prev->body_source.data == NULL))
#if (NGX_HTTP_CACHE)
&& ((conf->upstream.cache == NULL) == (prev->upstream.cache == NULL))
#endif
@@ -3174,16 +2938,6 @@ ngx_http_proxy_merge_headers(ngx_conf_t *cf, ngx_http_proxy_loc_conf_t *conf,
h++;
}
- if (conf->body_source.data) {
- s = ngx_array_push(&headers_merged);
- if (s == NULL) {
- return NGX_ERROR;
- }
-
- ngx_str_set(&s->key, "Content-Length");
- ngx_str_set(&s->value, "$proxy_internal_body_length");
- }
-
src = headers_merged.elts;
for (i = 0; i < headers_merged.nelts; i++) {
diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_range_filter_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_range_filter_module.c
index 0eaeff9158c..82c202d33c5 100644
--- a/usr.sbin/nginx/src/http/modules/ngx_http_range_filter_module.c
+++ b/usr.sbin/nginx/src/http/modules/ngx_http_range_filter_module.c
@@ -146,7 +146,8 @@ static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
static ngx_int_t
ngx_http_range_header_filter(ngx_http_request_t *r)
{
- time_t if_range;
+ time_t if_range_time;
+ ngx_str_t *if_range, *etag;
ngx_http_core_loc_conf_t *clcf;
ngx_http_range_filter_ctx_t *ctx;
@@ -174,20 +175,47 @@ ngx_http_range_header_filter(ngx_http_request_t *r)
goto next_filter;
}
- if (r->headers_in.if_range && r->headers_out.last_modified_time != -1) {
+ if (r->headers_in.if_range) {
- if_range = ngx_http_parse_time(r->headers_in.if_range->value.data,
- r->headers_in.if_range->value.len);
+ if_range = &r->headers_in.if_range->value;
+
+ if (if_range->len >= 2 && if_range->data[if_range->len - 1] == '"') {
+
+ if (r->headers_out.etag == NULL) {
+ goto next_filter;
+ }
+
+ etag = &r->headers_out.etag->value;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http ir:%V etag:%V", if_range, etag);
+
+ if (if_range->len != etag->len
+ || ngx_strncmp(if_range->data, etag->data, etag->len) != 0)
+ {
+ goto next_filter;
+ }
+
+ goto parse;
+ }
+
+ if (r->headers_out.last_modified_time == (time_t) -1) {
+ goto next_filter;
+ }
+
+ if_range_time = ngx_http_parse_time(if_range->data, if_range->len);
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http ir:%d lm:%d",
- if_range, r->headers_out.last_modified_time);
+ if_range_time, r->headers_out.last_modified_time);
- if (if_range != r->headers_out.last_modified_time) {
+ if (if_range_time != r->headers_out.last_modified_time) {
goto next_filter;
}
}
+parse:
+
ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_range_filter_ctx_t));
if (ctx == NULL) {
return NGX_ERROR;
diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_realip_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_realip_module.c
index 4531ea51cd9..ed9c5f9e82c 100644
--- a/usr.sbin/nginx/src/http/modules/ngx_http_realip_module.c
+++ b/usr.sbin/nginx/src/http/modules/ngx_http_realip_module.c
@@ -107,10 +107,12 @@ ngx_module_t ngx_http_realip_module = {
static ngx_int_t
ngx_http_realip_handler(ngx_http_request_t *r)
{
- u_char *ip, *p;
+ u_char *p;
size_t len;
+ ngx_str_t *value;
ngx_uint_t i, hash;
ngx_addr_t addr;
+ ngx_array_t *xfwd;
ngx_list_part_t *part;
ngx_table_elt_t *header;
ngx_connection_t *c;
@@ -137,19 +139,20 @@ ngx_http_realip_handler(ngx_http_request_t *r)
return NGX_DECLINED;
}
- len = r->headers_in.x_real_ip->value.len;
- ip = r->headers_in.x_real_ip->value.data;
+ value = &r->headers_in.x_real_ip->value;
+ xfwd = NULL;
break;
case NGX_HTTP_REALIP_XFWD:
- if (r->headers_in.x_forwarded_for == NULL) {
+ xfwd = &r->headers_in.x_forwarded_for;
+
+ if (xfwd->elts == NULL) {
return NGX_DECLINED;
}
- len = r->headers_in.x_forwarded_for->value.len;
- ip = r->headers_in.x_forwarded_for->value.data;
+ value = NULL;
break;
@@ -178,8 +181,8 @@ ngx_http_realip_handler(ngx_http_request_t *r)
&& len == header[i].key.len
&& ngx_strncmp(p, header[i].lowcase_key, len) == 0)
{
- len = header[i].value.len;
- ip = header[i].value.data;
+ value = &header[i].value;
+ xfwd = NULL;
goto found;
}
@@ -192,15 +195,13 @@ found:
c = r->connection;
- ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "realip: \"%s\"", ip);
-
addr.sockaddr = c->sockaddr;
addr.socklen = c->socklen;
/* addr.name = c->addr_text; */
- if (ngx_http_get_forwarded_addr(r, &addr, ip, len, rlcf->from,
+ if (ngx_http_get_forwarded_addr(r, &addr, xfwd, value, rlcf->from,
rlcf->recursive)
- == NGX_OK)
+ != NGX_DECLINED)
{
return ngx_http_realip_set_addr(r, &addr);
}
diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_scgi_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_scgi_module.c
index f98d82f5dc6..49cc96d248c 100644
--- a/usr.sbin/nginx/src/http/modules/ngx_http_scgi_module.c
+++ b/usr.sbin/nginx/src/http/modules/ngx_http_scgi_module.c
@@ -533,10 +533,11 @@ ngx_http_scgi_create_key(ngx_http_request_t *r)
static ngx_int_t
ngx_http_scgi_create_request(ngx_http_request_t *r)
{
+ off_t content_length_n;
u_char ch, *key, *val, *lowcase_key;
size_t len, key_len, val_len, allocated;
ngx_buf_t *b;
- ngx_str_t *content_length;
+ ngx_str_t content_length;
ngx_uint_t i, n, hash, skip_empty, header_params;
ngx_chain_t *cl, *body;
ngx_list_part_t *part;
@@ -545,12 +546,20 @@ ngx_http_scgi_create_request(ngx_http_request_t *r)
ngx_http_script_engine_t e, le;
ngx_http_scgi_loc_conf_t *scf;
ngx_http_script_len_code_pt lcode;
- static ngx_str_t zero = ngx_string("0");
+ u_char buffer[NGX_OFF_T_LEN];
- content_length = r->headers_in.content_length ?
- &r->headers_in.content_length->value : &zero;
+ content_length_n = 0;
+ body = r->upstream->request_bufs;
- len = sizeof("CONTENT_LENGTH") + content_length->len + 1;
+ while (body) {
+ content_length_n += ngx_buf_size(body->buf);
+ body = body->next;
+ }
+
+ content_length.data = buffer;
+ content_length.len = ngx_sprintf(buffer, "%O", content_length_n) - buffer;
+
+ len = sizeof("CONTENT_LENGTH") + content_length.len + 1;
header_params = 0;
ignored = NULL;
@@ -672,11 +681,8 @@ ngx_http_scgi_create_request(ngx_http_request_t *r)
cl->buf = b;
- b->last = ngx_snprintf(b->last,
- NGX_SIZE_T_LEN + 1 + sizeof("CONTENT_LENGTH")
- + NGX_OFF_T_LEN + 1,
- "%ui:CONTENT_LENGTH%Z%V%Z",
- len, content_length);
+ b->last = ngx_sprintf(b->last, "%ui:CONTENT_LENGTH%Z%V%Z",
+ len, &content_length);
if (scf->params_len) {
ngx_memzero(&e, sizeof(ngx_http_script_engine_t));
@@ -978,7 +984,7 @@ ngx_http_scgi_process_header(ngx_http_request_t *r)
u = r->upstream;
if (u->headers_in.status_n) {
- return NGX_OK;
+ goto done;
}
if (u->headers_in.status) {
@@ -1009,6 +1015,14 @@ ngx_http_scgi_process_header(ngx_http_request_t *r)
u->state->status = u->headers_in.status_n;
}
+ done:
+
+ if (u->headers_in.status_n == NGX_HTTP_SWITCHING_PROTOCOLS
+ && r->headers_in.upgrade)
+ {
+ u->upgrade = 1;
+ }
+
return NGX_OK;
}
@@ -1061,6 +1075,8 @@ ngx_http_scgi_create_loc_conf(ngx_conf_t *cf)
conf->upstream.buffering = NGX_CONF_UNSET;
conf->upstream.ignore_client_abort = NGX_CONF_UNSET;
+ conf->upstream.local = NGX_CONF_UNSET_PTR;
+
conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC;
conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC;
conf->upstream.read_timeout = NGX_CONF_UNSET_MSEC;
@@ -1129,6 +1145,9 @@ ngx_http_scgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_conf_merge_value(conf->upstream.ignore_client_abort,
prev->upstream.ignore_client_abort, 0);
+ ngx_conf_merge_ptr_value(conf->upstream.local,
+ prev->upstream.local, NULL);
+
ngx_conf_merge_msec_value(conf->upstream.connect_timeout,
prev->upstream.connect_timeout, 60000);
diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_ssi_filter_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_ssi_filter_module.c
index 6c2d0a9b042..eb286cc3475 100644
--- a/usr.sbin/nginx/src/http/modules/ngx_http_ssi_filter_module.c
+++ b/usr.sbin/nginx/src/http/modules/ngx_http_ssi_filter_module.c
@@ -361,6 +361,7 @@ ngx_http_ssi_header_filter(ngx_http_request_t *r)
ngx_http_clear_content_length(r);
ngx_http_clear_last_modified(r);
ngx_http_clear_accept_ranges(r);
+ ngx_http_clear_etag(r);
}
return ngx_http_next_header_filter(r);
diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_ssl_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_ssl_module.c
index 33601823603..a6c803da0dc 100644
--- a/usr.sbin/nginx/src/http/modules/ngx_http_ssl_module.c
+++ b/usr.sbin/nginx/src/http/modules/ngx_http_ssl_module.c
@@ -18,6 +18,11 @@ typedef ngx_int_t (*ngx_ssl_variable_handler_pt)(ngx_connection_t *c,
#define NGX_DEFAULT_ECDH_CURVE "prime256v1"
+#ifdef TLSEXT_TYPE_next_proto_neg
+static int ngx_http_ssl_npn_advertised(ngx_ssl_conn_t *ssl_conn,
+ const unsigned char **out, unsigned int *outlen, void *arg);
+#endif
+
static ngx_int_t ngx_http_ssl_static_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_ssl_variable(ngx_http_request_t *r,
@@ -33,6 +38,8 @@ static char *ngx_http_ssl_enable(ngx_conf_t *cf, ngx_command_t *cmd,
static char *ngx_http_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
+static ngx_int_t ngx_http_ssl_init(ngx_conf_t *cf);
+
static ngx_conf_bitmask_t ngx_http_ssl_protocols[] = {
{ ngx_string("SSLv2"), NGX_SSL_SSLv2 },
@@ -125,6 +132,13 @@ static ngx_command_t ngx_http_ssl_commands[] = {
offsetof(ngx_http_ssl_srv_conf_t, client_certificate),
NULL },
+ { ngx_string("ssl_trusted_certificate"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_ssl_srv_conf_t, trusted_certificate),
+ NULL },
+
{ ngx_string("ssl_prefer_server_ciphers"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
@@ -153,13 +167,41 @@ static ngx_command_t ngx_http_ssl_commands[] = {
offsetof(ngx_http_ssl_srv_conf_t, crl),
NULL },
+ { ngx_string("ssl_stapling"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_ssl_srv_conf_t, stapling),
+ NULL },
+
+ { ngx_string("ssl_stapling_file"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_ssl_srv_conf_t, stapling_file),
+ NULL },
+
+ { ngx_string("ssl_stapling_responder"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_ssl_srv_conf_t, stapling_responder),
+ NULL },
+
+ { ngx_string("ssl_stapling_verify"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_ssl_srv_conf_t, stapling_verify),
+ NULL },
+
ngx_null_command
};
static ngx_http_module_t ngx_http_ssl_module_ctx = {
ngx_http_ssl_add_variables, /* preconfiguration */
- NULL, /* postconfiguration */
+ ngx_http_ssl_init, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
@@ -225,6 +267,45 @@ static ngx_http_variable_t ngx_http_ssl_vars[] = {
static ngx_str_t ngx_http_ssl_sess_id_ctx = ngx_string("HTTP");
+#ifdef TLSEXT_TYPE_next_proto_neg
+
+#define NGX_HTTP_NPN_ADVERTISE "\x08http/1.1"
+
+static int
+ngx_http_ssl_npn_advertised(ngx_ssl_conn_t *ssl_conn,
+ const unsigned char **out, unsigned int *outlen, void *arg)
+{
+#if (NGX_HTTP_SPDY || NGX_DEBUG)
+ ngx_connection_t *c;
+
+ c = ngx_ssl_get_connection(ssl_conn);
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "SSL NPN advertised");
+#endif
+
+#if (NGX_HTTP_SPDY)
+ {
+ ngx_http_connection_t *hc;
+
+ hc = c->data;
+
+ if (hc->addr_conf->spdy) {
+ *out = (unsigned char *) NGX_SPDY_NPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE;
+ *outlen = sizeof(NGX_SPDY_NPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE) - 1;
+
+ return SSL_TLSEXT_ERR_OK;
+ }
+ }
+#endif
+
+ *out = (unsigned char *) NGX_HTTP_NPN_ADVERTISE;
+ *outlen = sizeof(NGX_HTTP_NPN_ADVERTISE) - 1;
+
+ return SSL_TLSEXT_ERR_OK;
+}
+
+#endif
+
+
static ngx_int_t
ngx_http_ssl_static_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
@@ -326,9 +407,12 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t *cf)
* sscf->dhparam = { 0, NULL };
* sscf->ecdh_curve = { 0, NULL };
* sscf->client_certificate = { 0, NULL };
+ * sscf->trusted_certificate = { 0, NULL };
* sscf->crl = { 0, NULL };
* sscf->ciphers = { 0, NULL };
* sscf->shm_zone = NULL;
+ * sscf->stapling_file = { 0, NULL };
+ * sscf->stapling_responder = { 0, NULL };
*/
sscf->enable = NGX_CONF_UNSET;
@@ -337,6 +421,8 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t *cf)
sscf->verify_depth = NGX_CONF_UNSET_UINT;
sscf->builtin_session_cache = NGX_CONF_UNSET;
sscf->session_timeout = NGX_CONF_UNSET;
+ sscf->stapling = NGX_CONF_UNSET;
+ sscf->stapling_verify = NGX_CONF_UNSET;
return sscf;
}
@@ -381,6 +467,8 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_conf_merge_str_value(conf->client_certificate, prev->client_certificate,
"");
+ ngx_conf_merge_str_value(conf->trusted_certificate,
+ prev->trusted_certificate, "");
ngx_conf_merge_str_value(conf->crl, prev->crl, "");
ngx_conf_merge_str_value(conf->ecdh_curve, prev->ecdh_curve,
@@ -388,6 +476,11 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFAULT_CIPHERS);
+ ngx_conf_merge_value(conf->stapling, prev->stapling, 0);
+ ngx_conf_merge_value(conf->stapling_verify, prev->stapling_verify, 0);
+ ngx_conf_merge_str_value(conf->stapling_file, prev->stapling_file, "");
+ ngx_conf_merge_str_value(conf->stapling_responder,
+ prev->stapling_responder, "");
conf->ssl.log = cf->log;
@@ -441,6 +534,11 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
#endif
+#ifdef TLSEXT_TYPE_next_proto_neg
+ SSL_CTX_set_next_protos_advertised_cb(conf->ssl.ctx,
+ ngx_http_ssl_npn_advertised, NULL);
+#endif
+
cln = ngx_pool_cleanup_add(cf->pool, 0);
if (cln == NULL) {
return NGX_CONF_ERROR;
@@ -480,10 +578,18 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
{
return NGX_CONF_ERROR;
}
+ }
- if (ngx_ssl_crl(cf, &conf->ssl, &conf->crl) != NGX_OK) {
- return NGX_CONF_ERROR;
- }
+ if (ngx_ssl_trusted_certificate(cf, &conf->ssl,
+ &conf->trusted_certificate,
+ conf->verify_depth)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ if (ngx_ssl_crl(cf, &conf->ssl, &conf->crl) != NGX_OK) {
+ return NGX_CONF_ERROR;
}
if (conf->prefer_server_ciphers) {
@@ -516,6 +622,17 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
return NGX_CONF_ERROR;
}
+ if (conf->stapling) {
+
+ if (ngx_ssl_stapling(cf, &conf->ssl, &conf->stapling_file,
+ &conf->stapling_responder, conf->stapling_verify)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ }
+
return NGX_CONF_OK;
}
@@ -650,3 +767,37 @@ invalid:
return NGX_CONF_ERROR;
}
+
+
+static ngx_int_t
+ngx_http_ssl_init(ngx_conf_t *cf)
+{
+ ngx_uint_t s;
+ ngx_http_ssl_srv_conf_t *sscf;
+ ngx_http_core_loc_conf_t *clcf;
+ ngx_http_core_srv_conf_t **cscfp;
+ ngx_http_core_main_conf_t *cmcf;
+
+ cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
+ cscfp = cmcf->servers.elts;
+
+ for (s = 0; s < cmcf->servers.nelts; s++) {
+
+ sscf = cscfp[s]->ctx->srv_conf[ngx_http_ssl_module.ctx_index];
+
+ if (sscf->ssl.ctx == NULL || !sscf->stapling) {
+ continue;
+ }
+
+ clcf = cscfp[s]->ctx->loc_conf[ngx_http_core_module.ctx_index];
+
+ if (ngx_ssl_stapling_resolver(cf, &sscf->ssl, clcf->resolver,
+ clcf->resolver_timeout)
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+ }
+
+ return NGX_OK;
+}
diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_ssl_module.h b/usr.sbin/nginx/src/http/modules/ngx_http_ssl_module.h
index 58659ab959e..c4c576ef63b 100644
--- a/usr.sbin/nginx/src/http/modules/ngx_http_ssl_module.h
+++ b/usr.sbin/nginx/src/http/modules/ngx_http_ssl_module.h
@@ -35,12 +35,18 @@ typedef struct {
ngx_str_t dhparam;
ngx_str_t ecdh_curve;
ngx_str_t client_certificate;
+ ngx_str_t trusted_certificate;
ngx_str_t crl;
ngx_str_t ciphers;
ngx_shm_zone_t *shm_zone;
+ ngx_flag_t stapling;
+ ngx_flag_t stapling_verify;
+ ngx_str_t stapling_file;
+ ngx_str_t stapling_responder;
+
u_char *file;
ngx_uint_t line;
} ngx_http_ssl_srv_conf_t;
diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_static_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_static_module.c
index 9d77e43b119..631eb17b267 100644
--- a/usr.sbin/nginx/src/http/modules/ngx_http_static_module.c
+++ b/usr.sbin/nginx/src/http/modules/ngx_http_static_module.c
@@ -220,6 +220,10 @@ ngx_http_static_handler(ngx_http_request_t *r)
r->headers_out.content_length_n = of.size;
r->headers_out.last_modified_time = of.mtime;
+ if (ngx_http_set_etag(r) != NGX_OK) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
if (ngx_http_set_content_type(r) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_stub_status_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_stub_status_module.c
index 96abe928ae8..83a35cda814 100644
--- a/usr.sbin/nginx/src/http/modules/ngx_http_stub_status_module.c
+++ b/usr.sbin/nginx/src/http/modules/ngx_http_stub_status_module.c
@@ -10,6 +10,10 @@
#include <ngx_http.h>
+static ngx_int_t ngx_http_stub_status_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_stub_status_add_variables(ngx_conf_t *cf);
+
static char *ngx_http_set_status(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
@@ -28,7 +32,7 @@ static ngx_command_t ngx_http_status_commands[] = {
static ngx_http_module_t ngx_http_stub_status_module_ctx = {
- NULL, /* preconfiguration */
+ ngx_http_stub_status_add_variables, /* preconfiguration */
NULL, /* postconfiguration */
NULL, /* create main configuration */
@@ -58,13 +62,31 @@ ngx_module_t ngx_http_stub_status_module = {
};
+static ngx_http_variable_t ngx_http_stub_status_vars[] = {
+
+ { ngx_string("connections_active"), NULL, ngx_http_stub_status_variable,
+ 0, NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("connections_reading"), NULL, ngx_http_stub_status_variable,
+ 1, NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("connections_writing"), NULL, ngx_http_stub_status_variable,
+ 2, NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("connections_waiting"), NULL, ngx_http_stub_status_variable,
+ 3, NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_null_string, NULL, NULL, 0, 0, 0 }
+};
+
+
static ngx_int_t ngx_http_status_handler(ngx_http_request_t *r)
{
size_t size;
ngx_int_t rc;
ngx_buf_t *b;
ngx_chain_t out;
- ngx_atomic_int_t ap, hn, ac, rq, rd, wr;
+ ngx_atomic_int_t ap, hn, ac, rq, rd, wr, wa;
if (r->method != NGX_HTTP_GET && r->method != NGX_HTTP_HEAD) {
return NGX_HTTP_NOT_ALLOWED;
@@ -107,6 +129,7 @@ static ngx_int_t ngx_http_status_handler(ngx_http_request_t *r)
rq = *ngx_stat_requests;
rd = *ngx_stat_reading;
wr = *ngx_stat_writing;
+ wa = *ngx_stat_waiting;
b->last = ngx_sprintf(b->last, "Active connections: %uA \n", ac);
@@ -116,7 +139,7 @@ static ngx_int_t ngx_http_status_handler(ngx_http_request_t *r)
b->last = ngx_sprintf(b->last, " %uA %uA %uA \n", ap, hn, rq);
b->last = ngx_sprintf(b->last, "Reading: %uA Writing: %uA Waiting: %uA \n",
- rd, wr, ac - (rd + wr));
+ rd, wr, wa);
r->headers_out.status = NGX_HTTP_OK;
r->headers_out.content_length_n = b->last - b->pos;
@@ -133,6 +156,70 @@ static ngx_int_t ngx_http_status_handler(ngx_http_request_t *r)
}
+static ngx_int_t
+ngx_http_stub_status_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ u_char *p;
+ ngx_atomic_int_t value;
+
+ p = ngx_pnalloc(r->pool, NGX_ATOMIC_T_LEN);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ switch (data) {
+ case 0:
+ value = *ngx_stat_active;
+ break;
+
+ case 1:
+ value = *ngx_stat_reading;
+ break;
+
+ case 2:
+ value = *ngx_stat_writing;
+ break;
+
+ case 3:
+ value = *ngx_stat_waiting;
+ break;
+
+ /* suppress warning */
+ default:
+ value = 0;
+ break;
+ }
+
+ v->len = ngx_sprintf(p, "%uA", value) - p;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = p;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_stub_status_add_variables(ngx_conf_t *cf)
+{
+ ngx_http_variable_t *var, *v;
+
+ for (v = ngx_http_stub_status_vars; v->name.len; v++) {
+ var = ngx_http_add_variable(cf, &v->name, v->flags);
+ if (var == NULL) {
+ return NGX_ERROR;
+ }
+
+ var->get_handler = v->get_handler;
+ var->data = v->data;
+ }
+
+ return NGX_OK;
+}
+
+
static char *ngx_http_set_status(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_core_loc_conf_t *clcf;
diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_sub_filter_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_sub_filter_module.c
index 6959a00e1dc..6ba57dfffc0 100644
--- a/usr.sbin/nginx/src/http/modules/ngx_http_sub_filter_module.c
+++ b/usr.sbin/nginx/src/http/modules/ngx_http_sub_filter_module.c
@@ -168,6 +168,7 @@ ngx_http_sub_header_filter(ngx_http_request_t *r)
if (r == r->main) {
ngx_http_clear_content_length(r);
ngx_http_clear_last_modified(r);
+ ngx_http_clear_etag(r);
}
return ngx_http_next_header_filter(r);
diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_upstream_ip_hash_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_upstream_ip_hash_module.c
index 89ccc2b858f..29c74fd54d0 100644
--- a/usr.sbin/nginx/src/http/modules/ngx_http_upstream_ip_hash_module.c
+++ b/usr.sbin/nginx/src/http/modules/ngx_http_upstream_ip_hash_module.c
@@ -252,6 +252,11 @@ ngx_http_upstream_ip_hash(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);
+ if (uscf->peer.init_upstream) {
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "load balancing method redefined");
+ }
+
uscf->peer.init_upstream = ngx_http_upstream_init_ip_hash;
uscf->flags = NGX_HTTP_UPSTREAM_CREATE
diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_upstream_least_conn_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_upstream_least_conn_module.c
index 6066ed2bb84..87c4d8d613b 100644
--- a/usr.sbin/nginx/src/http/modules/ngx_http_upstream_least_conn_module.c
+++ b/usr.sbin/nginx/src/http/modules/ngx_http_upstream_least_conn_module.c
@@ -387,6 +387,11 @@ ngx_http_upstream_least_conn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);
+ if (uscf->peer.init_upstream) {
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "load balancing method redefined");
+ }
+
uscf->peer.init_upstream = ngx_http_upstream_init_least_conn;
uscf->flags = NGX_HTTP_UPSTREAM_CREATE
diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_uwsgi_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_uwsgi_module.c
index 0c6414c6f82..623ee495771 100644
--- a/usr.sbin/nginx/src/http/modules/ngx_http_uwsgi_module.c
+++ b/usr.sbin/nginx/src/http/modules/ngx_http_uwsgi_module.c
@@ -1018,7 +1018,7 @@ ngx_http_uwsgi_process_header(ngx_http_request_t *r)
u = r->upstream;
if (u->headers_in.status_n) {
- return NGX_OK;
+ goto done;
}
if (u->headers_in.status) {
@@ -1049,6 +1049,14 @@ ngx_http_uwsgi_process_header(ngx_http_request_t *r)
u->state->status = u->headers_in.status_n;
}
+ done:
+
+ if (u->headers_in.status_n == NGX_HTTP_SWITCHING_PROTOCOLS
+ && r->headers_in.upgrade)
+ {
+ u->upgrade = 1;
+ }
+
return NGX_OK;
}
@@ -1104,6 +1112,8 @@ ngx_http_uwsgi_create_loc_conf(ngx_conf_t *cf)
conf->upstream.buffering = NGX_CONF_UNSET;
conf->upstream.ignore_client_abort = NGX_CONF_UNSET;
+ conf->upstream.local = NGX_CONF_UNSET_PTR;
+
conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC;
conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC;
conf->upstream.read_timeout = NGX_CONF_UNSET_MSEC;
@@ -1172,6 +1182,9 @@ ngx_http_uwsgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_conf_merge_value(conf->upstream.ignore_client_abort,
prev->upstream.ignore_client_abort, 0);
+ ngx_conf_merge_ptr_value(conf->upstream.local,
+ prev->upstream.local, NULL);
+
ngx_conf_merge_msec_value(conf->upstream.connect_timeout,
prev->upstream.connect_timeout, 60000);
diff --git a/usr.sbin/nginx/src/http/modules/ngx_http_xslt_filter_module.c b/usr.sbin/nginx/src/http/modules/ngx_http_xslt_filter_module.c
index ddcee6312b5..a6ae1ce02b8 100644
--- a/usr.sbin/nginx/src/http/modules/ngx_http_xslt_filter_module.c
+++ b/usr.sbin/nginx/src/http/modules/ngx_http_xslt_filter_module.c
@@ -328,6 +328,7 @@ ngx_http_xslt_send(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx,
}
ngx_http_clear_last_modified(r);
+ ngx_http_clear_etag(r);
}
rc = ngx_http_next_header_filter(r);
diff --git a/usr.sbin/nginx/src/http/modules/perl/Makefile.PL b/usr.sbin/nginx/src/http/modules/perl/Makefile.PL
index 78a1e516bf7..03348b555fc 100644
--- a/usr.sbin/nginx/src/http/modules/perl/Makefile.PL
+++ b/usr.sbin/nginx/src/http/modules/perl/Makefile.PL
@@ -21,8 +21,10 @@ WriteMakefile(
} (split /\s+/, $ENV{NGX_INCS})),
depend => {
- 'nginx.c' =>
- "../../../../../src/http/modules/perl/ngx_http_perl_module.h"
+ 'nginx.c' => join(" ", map {
+ m#^/# ? $_ : "../../../../../$_"
+ } (split(/\s+/, $ENV{NGX_DEPS}),
+ "src/http/modules/perl/ngx_http_perl_module.h"))
},
PM => {
diff --git a/usr.sbin/nginx/src/http/modules/perl/nginx.pm b/usr.sbin/nginx/src/http/modules/perl/nginx.pm
index 32684feac89..e3f73611025 100644
--- a/usr.sbin/nginx/src/http/modules/perl/nginx.pm
+++ b/usr.sbin/nginx/src/http/modules/perl/nginx.pm
@@ -50,7 +50,7 @@ our @EXPORT = qw(
HTTP_INSUFFICIENT_STORAGE
);
-our $VERSION = '1.2.9';
+our $VERSION = '%%VERSION%%';
require XSLoader;
XSLoader::load('nginx', $VERSION);
diff --git a/usr.sbin/nginx/src/http/modules/perl/nginx.xs b/usr.sbin/nginx/src/http/modules/perl/nginx.xs
index ed974391163..bbfef079c23 100644
--- a/usr.sbin/nginx/src/http/modules/perl/nginx.xs
+++ b/usr.sbin/nginx/src/http/modules/perl/nginx.xs
@@ -357,7 +357,7 @@ has_request_body(r, next)
ngx_http_perl_set_request(r);
- if (r->headers_in.content_length_n <= 0) {
+ if (r->headers_in.content_length_n <= 0 && !r->headers_in.chunked) {
XSRETURN_UNDEF;
}
@@ -386,7 +386,10 @@ request_body(r)
dXSTARG;
ngx_http_request_t *r;
+ u_char *p, *data;
size_t len;
+ ngx_buf_t *buf;
+ ngx_chain_t *cl;
ngx_http_perl_set_request(r);
@@ -397,13 +400,43 @@ request_body(r)
XSRETURN_UNDEF;
}
- len = r->request_body->bufs->buf->last - r->request_body->bufs->buf->pos;
+ cl = r->request_body->bufs;
+ buf = cl->buf;
+
+ if (cl->next == NULL) {
+ len = buf->last - buf->pos;
+ data = buf->pos;
+ goto done;
+ }
+
+ len = buf->last - buf->pos;
+ cl = cl->next;
+
+ for ( /* void */ ; cl; cl = cl->next) {
+ buf = cl->buf;
+ len += buf->last - buf->pos;
+ }
+
+ p = ngx_pnalloc(r->pool, len);
+ if (p == NULL) {
+ return XSRETURN_UNDEF;
+ }
+
+ data = p;
+ cl = r->request_body->bufs;
+
+ for ( /* void */ ; cl; cl = cl->next) {
+ buf = cl->buf;
+ p = ngx_cpymem(p, buf->pos, buf->last - buf->pos);
+ }
+
+ done:
if (len == 0) {
XSRETURN_UNDEF;
}
- ngx_http_perl_set_targ(r->request_body->bufs->buf->pos, len);
+ ngx_http_perl_set_targ(data, len);
ST(0) = TARG;
diff --git a/usr.sbin/nginx/src/http/ngx_http.c b/usr.sbin/nginx/src/http/ngx_http.c
index f1f8a48e08f..987ae54df66 100644
--- a/usr.sbin/nginx/src/http/ngx_http.c
+++ b/usr.sbin/nginx/src/http/ngx_http.c
@@ -1225,6 +1225,9 @@ ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
#if (NGX_HTTP_SSL)
ngx_uint_t ssl;
#endif
+#if (NGX_HTTP_SPDY)
+ ngx_uint_t spdy;
+#endif
/*
* we cannot compare whole sockaddr struct's as kernel
@@ -1277,6 +1280,9 @@ ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
#if (NGX_HTTP_SSL)
ssl = lsopt->ssl || addr[i].opt.ssl;
#endif
+#if (NGX_HTTP_SPDY)
+ spdy = lsopt->spdy || addr[i].opt.spdy;
+#endif
if (lsopt->set) {
@@ -1307,6 +1313,9 @@ ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
#if (NGX_HTTP_SSL)
addr[i].opt.ssl = ssl;
#endif
+#if (NGX_HTTP_SPDY)
+ addr[i].opt.spdy = spdy;
+#endif
return NGX_OK;
}
@@ -1337,6 +1346,14 @@ ngx_http_add_address(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
}
}
+#if (NGX_HTTP_SPDY && NGX_HTTP_SSL && !defined TLSEXT_TYPE_next_proto_neg)
+ if (lsopt->spdy && lsopt->ssl) {
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "nginx was built without OpenSSL NPN support, "
+ "SPDY is not enabled for %s", lsopt->addr);
+ }
+#endif
+
addr = ngx_array_push(&port->addrs);
if (addr == NULL) {
return NGX_ERROR;
@@ -1462,7 +1479,7 @@ ngx_http_server_names(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf,
ngx_memzero(&ha, sizeof(ngx_hash_keys_arrays_t));
- ha.temp_pool = ngx_create_pool(16384, cf->log);
+ ha.temp_pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log);
if (ha.temp_pool == NULL) {
return NGX_ERROR;
}
@@ -1820,6 +1837,9 @@ ngx_http_add_addrs(ngx_conf_t *cf, ngx_http_port_t *hport,
#if (NGX_HTTP_SSL)
addrs[i].conf.ssl = addr[i].opt.ssl;
#endif
+#if (NGX_HTTP_SPDY)
+ addrs[i].conf.spdy = addr[i].opt.spdy;
+#endif
if (addr[i].hash.buckets == NULL
&& (addr[i].wc_head == NULL
@@ -1881,6 +1901,9 @@ ngx_http_add_addrs6(ngx_conf_t *cf, ngx_http_port_t *hport,
#if (NGX_HTTP_SSL)
addrs6[i].conf.ssl = addr[i].opt.ssl;
#endif
+#if (NGX_HTTP_SPDY)
+ addrs6[i].conf.spdy = addr[i].opt.spdy;
+#endif
if (addr[i].hash.buckets == NULL
&& (addr[i].wc_head == NULL
diff --git a/usr.sbin/nginx/src/http/ngx_http.h b/usr.sbin/nginx/src/http/ngx_http.h
index d278d8346e3..3d758bfd963 100644
--- a/usr.sbin/nginx/src/http/ngx_http.h
+++ b/usr.sbin/nginx/src/http/ngx_http.h
@@ -18,6 +18,11 @@ typedef struct ngx_http_upstream_s ngx_http_upstream_t;
typedef struct ngx_http_cache_s ngx_http_cache_t;
typedef struct ngx_http_file_cache_s ngx_http_file_cache_t;
typedef struct ngx_http_log_ctx_s ngx_http_log_ctx_t;
+typedef struct ngx_http_chunked_s ngx_http_chunked_t;
+
+#if (NGX_HTTP_SPDY)
+typedef struct ngx_http_spdy_stream_s ngx_http_spdy_stream_t;
+#endif
typedef ngx_int_t (*ngx_http_header_handler_pt)(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
@@ -26,14 +31,17 @@ typedef u_char *(*ngx_http_log_handler_pt)(ngx_http_request_t *r,
#include <ngx_http_variables.h>
+#include <ngx_http_config.h>
#include <ngx_http_request.h>
+#include <ngx_http_script.h>
#include <ngx_http_upstream.h>
#include <ngx_http_upstream_round_robin.h>
-#include <ngx_http_config.h>
#include <ngx_http_busy_lock.h>
-#include <ngx_http_script.h>
#include <ngx_http_core_module.h>
+#if (NGX_HTTP_SPDY)
+#include <ngx_http_spdy.h>
+#endif
#if (NGX_HTTP_CACHE)
#include <ngx_http_cache.h>
#endif
@@ -52,6 +60,13 @@ struct ngx_http_log_ctx_s {
};
+struct ngx_http_chunked_s {
+ ngx_uint_t state;
+ off_t size;
+ off_t length;
+};
+
+
typedef struct {
ngx_uint_t http_version;
ngx_uint_t code;
@@ -72,12 +87,14 @@ ngx_int_t ngx_http_add_listen(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
void ngx_http_init_connection(ngx_connection_t *c);
+void ngx_http_close_connection(ngx_connection_t *c);
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
int ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg);
#endif
ngx_int_t ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b);
+ngx_int_t ngx_http_parse_uri(ngx_http_request_t *r);
ngx_int_t ngx_http_parse_complex_uri(ngx_http_request_t *r,
ngx_uint_t merge_slashes);
ngx_int_t ngx_http_parse_status_line(ngx_http_request_t *r, ngx_buf_t *b,
@@ -92,14 +109,21 @@ ngx_int_t ngx_http_arg(ngx_http_request_t *r, u_char *name, size_t len,
ngx_str_t *value);
void ngx_http_split_args(ngx_http_request_t *r, ngx_str_t *uri,
ngx_str_t *args);
+ngx_int_t ngx_http_parse_chunked(ngx_http_request_t *r, ngx_buf_t *b,
+ ngx_http_chunked_t *ctx);
+ngx_http_request_t *ngx_http_create_request(ngx_connection_t *c);
+ngx_int_t ngx_http_process_request_uri(ngx_http_request_t *r);
+ngx_int_t ngx_http_process_request_header(ngx_http_request_t *r);
+void ngx_http_process_request(ngx_http_request_t *r);
void ngx_http_update_location_config(ngx_http_request_t *r);
void ngx_http_handler(ngx_http_request_t *r);
void ngx_http_run_posted_requests(ngx_connection_t *c);
ngx_int_t ngx_http_post_request(ngx_http_request_t *r,
ngx_http_posted_request_t *pr);
void ngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc);
+void ngx_http_free_request(ngx_http_request_t *r, ngx_int_t rc);
void ngx_http_empty_handler(ngx_event_t *wev);
void ngx_http_request_empty_handler(ngx_http_request_t *r);
diff --git a/usr.sbin/nginx/src/http/ngx_http_core_module.c b/usr.sbin/nginx/src/http/ngx_http_core_module.c
index e3f0004d5e0..5bdb46debbd 100644
--- a/usr.sbin/nginx/src/http/ngx_http_core_module.c
+++ b/usr.sbin/nginx/src/http/ngx_http_core_module.c
@@ -76,6 +76,9 @@ static ngx_uint_t ngx_http_gzip_quantity(u_char *p, u_char *last);
static char *ngx_http_gzip_disable(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
#endif
+static ngx_int_t ngx_http_get_forwarded_addr_internal(ngx_http_request_t *r,
+ ngx_addr_t *addr, u_char *xff, size_t xfflen, ngx_array_t *proxies,
+ int recursive);
#if (NGX_HAVE_OPENAT)
static char *ngx_http_disable_symlinks(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
@@ -650,6 +653,13 @@ static ngx_command_t ngx_http_core_commands[] = {
offsetof(ngx_http_core_loc_conf_t, chunked_transfer_encoding),
NULL },
+ { ngx_string("etag"),
+ 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, etag),
+ NULL },
+
{ ngx_string("error_page"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
|NGX_CONF_2MORE,
@@ -841,7 +851,8 @@ ngx_http_handler(ngx_http_request_t *r)
break;
}
- r->lingering_close = (r->headers_in.content_length_n > 0);
+ r->lingering_close = (r->headers_in.content_length_n > 0
+ || r->headers_in.chunked);
r->phase_handler = 0;
} else {
@@ -1450,14 +1461,10 @@ ngx_http_update_location_config(ngx_http_request_t *r)
}
if (r == r->main) {
- r->connection->log->file = clcf->error_log->file;
+ ngx_http_set_connection_log(r->connection, clcf->error_log);
#if (NGX_ENABLE_SYSLOG)
r->connection->log->priority = clcf->error_log->priority;
#endif
-
- if (!(r->connection->log->log_level & NGX_LOG_DEBUG_CONNECTION)) {
- r->connection->log->log_level = clcf->error_log->log_level;
- }
}
if ((ngx_io.flags & NGX_IO_SENDFILE) && clcf->sendfile) {
@@ -1813,6 +1820,42 @@ ngx_http_set_exten(ngx_http_request_t *r)
ngx_int_t
+ngx_http_set_etag(ngx_http_request_t *r)
+{
+ ngx_table_elt_t *etag;
+ ngx_http_core_loc_conf_t *clcf;
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (!clcf->etag) {
+ return NGX_OK;
+ }
+
+ etag = ngx_list_push(&r->headers_out.headers);
+ if (etag == NULL) {
+ return NGX_ERROR;
+ }
+
+ etag->hash = 1;
+ ngx_str_set(&etag->key, "ETag");
+
+ etag->value.data = ngx_pnalloc(r->pool, NGX_OFF_T_LEN + NGX_TIME_T_LEN + 3);
+ if (etag->value.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ etag->value.len = ngx_sprintf(etag->value.data, "\"%xT-%xO\"",
+ r->headers_out.last_modified_time,
+ r->headers_out.content_length_n)
+ - etag->value.data;
+
+ r->headers_out.etag = etag;
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
ngx_http_send_response(ngx_http_request_t *r, ngx_uint_t status,
ngx_str_t *ct, ngx_http_complex_value_t *cv)
{
@@ -2090,6 +2133,13 @@ ngx_http_gzip_ok(ngx_http_request_t *r)
return NGX_DECLINED;
}
+#if (NGX_HTTP_SPDY)
+ if (r->spdy_stream) {
+ r->gzip_ok = 1;
+ return NGX_OK;
+ }
+#endif
+
ae = r->headers_in.accept_encoding;
if (ae == NULL) {
return NGX_DECLINED;
@@ -2424,6 +2474,10 @@ ngx_http_subrequest(ngx_http_request_t *r,
sr->request_body = r->request_body;
+#if (NGX_HTTP_SPDY)
+ sr->spdy_stream = r->spdy_stream;
+#endif
+
sr->method = NGX_HTTP_GET;
sr->http_version = r->http_version;
@@ -2706,10 +2760,58 @@ ngx_http_set_disable_symlinks(ngx_http_request_t *r,
ngx_int_t
ngx_http_get_forwarded_addr(ngx_http_request_t *r, ngx_addr_t *addr,
+ ngx_array_t *headers, ngx_str_t *value, ngx_array_t *proxies,
+ int recursive)
+{
+ ngx_int_t rc;
+ ngx_uint_t i, found;
+ ngx_table_elt_t **h;
+
+ if (headers == NULL) {
+ return ngx_http_get_forwarded_addr_internal(r, addr, value->data,
+ value->len, proxies,
+ recursive);
+ }
+
+ i = headers->nelts;
+ h = headers->elts;
+
+ rc = NGX_DECLINED;
+
+ found = 0;
+
+ while (i-- > 0) {
+ rc = ngx_http_get_forwarded_addr_internal(r, addr, h[i]->value.data,
+ h[i]->value.len, proxies,
+ recursive);
+
+ if (!recursive) {
+ break;
+ }
+
+ if (rc == NGX_DECLINED && found) {
+ rc = NGX_DONE;
+ break;
+ }
+
+ if (rc != NGX_OK) {
+ break;
+ }
+
+ found = 1;
+ }
+
+ return rc;
+}
+
+
+static ngx_int_t
+ngx_http_get_forwarded_addr_internal(ngx_http_request_t *r, ngx_addr_t *addr,
u_char *xff, size_t xfflen, ngx_array_t *proxies, int recursive)
{
u_char *p;
in_addr_t inaddr;
+ ngx_int_t rc;
ngx_addr_t paddr;
ngx_cidr_t *cidr;
ngx_uint_t family, i;
@@ -2801,8 +2903,15 @@ ngx_http_get_forwarded_addr(ngx_http_request_t *r, ngx_addr_t *addr,
*addr = paddr;
if (recursive && p > xff) {
- (void) ngx_http_get_forwarded_addr(r, addr, xff, p - 1 - xff,
- proxies, 1);
+ rc = ngx_http_get_forwarded_addr_internal(r, addr, xff, p - 1 - xff,
+ proxies, 1);
+
+ if (rc == NGX_DECLINED) {
+ return NGX_DONE;
+ }
+
+ /* rc == NGX_OK || rc == NGX_DONE */
+ return rc;
}
return NGX_OK;
@@ -3150,7 +3259,7 @@ ngx_http_core_regex_location(ngx_conf_t *cf, ngx_http_core_loc_conf_t *clcf,
#if (NGX_HAVE_CASELESS_FILESYSTEM)
rc.options = NGX_REGEX_CASELESS;
#else
- rc.options = caseless;
+ rc.options = caseless ? NGX_REGEX_CASELESS : 0;
#endif
clcf->regex = ngx_http_regex_compile(cf, &rc);
@@ -3515,6 +3624,7 @@ ngx_http_core_create_loc_conf(ngx_conf_t *cf)
clcf->recursive_error_pages = NGX_CONF_UNSET;
clcf->server_tokens = NGX_CONF_UNSET;
clcf->chunked_transfer_encoding = NGX_CONF_UNSET;
+ clcf->etag = NGX_CONF_UNSET;
clcf->types_hash_max_size = NGX_CONF_UNSET_UINT;
clcf->types_hash_bucket_size = NGX_CONF_UNSET_UINT;
@@ -3776,6 +3886,7 @@ ngx_http_core_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_conf_merge_value(conf->server_tokens, prev->server_tokens, 1);
ngx_conf_merge_value(conf->chunked_transfer_encoding,
prev->chunked_transfer_encoding, 1);
+ ngx_conf_merge_value(conf->etag, prev->etag, 1);
ngx_conf_merge_ptr_value(conf->open_file_cache,
prev->open_file_cache, NULL);
@@ -3872,6 +3983,9 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
lsopt.setfib = -1;
#endif
lsopt.wildcard = u.wildcard;
+#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
+ lsopt.ipv6only = 1;
+#endif
(void) ngx_sock_ntop(&lsopt.u.sockaddr, lsopt.addr,
NGX_SOCKADDR_STRLEN, 1);
@@ -3991,7 +4105,7 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
lsopt.ipv6only = 1;
} else if (ngx_strcmp(&value[n].data[10], "ff") == 0) {
- lsopt.ipv6only = 2;
+ lsopt.ipv6only = 0;
} else {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
@@ -4030,6 +4144,18 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
#endif
}
+ if (ngx_strcmp(value[n].data, "spdy") == 0) {
+#if (NGX_HTTP_SPDY)
+ lsopt.spdy = 1;
+ continue;
+#else
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "the \"spdy\" parameter requires "
+ "ngx_http_spdy_module");
+ return NGX_CONF_ERROR;
+#endif
+ }
+
if (ngx_strncmp(value[n].data, "so_keepalive=", 13) == 0) {
if (ngx_strcmp(&value[n].data[13], "on") == 0) {
diff --git a/usr.sbin/nginx/src/http/ngx_http_core_module.h b/usr.sbin/nginx/src/http/ngx_http_core_module.h
index e95d1e06968..5b38000d8db 100644
--- a/usr.sbin/nginx/src/http/ngx_http_core_module.h
+++ b/usr.sbin/nginx/src/http/ngx_http_core_module.h
@@ -75,8 +75,11 @@ typedef struct {
#if (NGX_HTTP_SSL)
unsigned ssl:1;
#endif
+#if (NGX_HTTP_SPDY)
+ unsigned spdy:1;
+#endif
#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
- unsigned ipv6only:2;
+ unsigned ipv6only:1;
#endif
unsigned so_keepalive:2;
@@ -209,15 +212,35 @@ typedef struct {
typedef struct {
+#if (NGX_PCRE)
+ ngx_http_regex_t *regex;
+#endif
+ ngx_http_core_srv_conf_t *server; /* virtual name server conf */
+ ngx_str_t name;
+} ngx_http_server_name_t;
+
+
+typedef struct {
+ ngx_hash_combined_t names;
+
+ ngx_uint_t nregex;
+ ngx_http_server_name_t *regex;
+} ngx_http_virtual_names_t;
+
+
+struct ngx_http_addr_conf_s {
/* the default server configuration for this address:port */
ngx_http_core_srv_conf_t *default_server;
ngx_http_virtual_names_t *virtual_names;
#if (NGX_HTTP_SSL)
- ngx_uint_t ssl; /* unsigned ssl:1; */
+ unsigned ssl:1;
#endif
-} ngx_http_addr_conf_t;
+#if (NGX_HTTP_SPDY)
+ unsigned spdy:1;
+#endif
+};
typedef struct {
@@ -268,15 +291,6 @@ typedef struct {
} ngx_http_conf_addr_t;
-struct ngx_http_server_name_s {
-#if (NGX_PCRE)
- ngx_http_regex_t *regex;
-#endif
- ngx_http_core_srv_conf_t *server; /* virtual name server conf */
- ngx_str_t name;
-};
-
-
typedef struct {
ngx_int_t status;
ngx_int_t overwrite;
@@ -392,6 +406,7 @@ struct ngx_http_core_loc_conf_s {
ngx_flag_t recursive_error_pages; /* recursive_error_pages */
ngx_flag_t server_tokens; /* server_tokens */
ngx_flag_t chunked_transfer_encoding; /* chunked_transfer_encoding */
+ ngx_flag_t etag; /* etag */
#if (NGX_HTTP_GZIP)
ngx_flag_t gzip_vary; /* gzip_vary */
@@ -480,6 +495,7 @@ ngx_int_t ngx_http_core_content_phase(ngx_http_request_t *r,
void *ngx_http_test_content_type(ngx_http_request_t *r, ngx_hash_t *types_hash);
ngx_int_t ngx_http_set_content_type(ngx_http_request_t *r);
void ngx_http_set_exten(ngx_http_request_t *r);
+ngx_int_t ngx_http_set_etag(ngx_http_request_t *r);
ngx_int_t ngx_http_send_response(ngx_http_request_t *r, ngx_uint_t status,
ngx_str_t *ct, ngx_http_complex_value_t *cv);
u_char *ngx_http_map_uri_to_path(ngx_http_request_t *r, ngx_str_t *name,
@@ -514,7 +530,8 @@ ngx_int_t ngx_http_set_disable_symlinks(ngx_http_request_t *r,
ngx_http_core_loc_conf_t *clcf, ngx_str_t *path, ngx_open_file_info_t *of);
ngx_int_t ngx_http_get_forwarded_addr(ngx_http_request_t *r, ngx_addr_t *addr,
- u_char *xff, size_t xfflen, ngx_array_t *proxies, int recursive);
+ ngx_array_t *headers, ngx_str_t *value, ngx_array_t *proxies,
+ int recursive);
extern ngx_module_t ngx_http_core_module;
@@ -555,5 +572,12 @@ extern ngx_str_t ngx_http_core_get_method;
r->headers_out.location = NULL; \
}
+#define ngx_http_clear_etag(r) \
+ \
+ if (r->headers_out.etag) { \
+ r->headers_out.etag->hash = 0; \
+ r->headers_out.etag = NULL; \
+ }
+
#endif /* _NGX_HTTP_CORE_H_INCLUDED_ */
diff --git a/usr.sbin/nginx/src/http/ngx_http_header_filter_module.c b/usr.sbin/nginx/src/http/ngx_http_header_filter_module.c
index e3efbba5bcf..707a8131353 100644
--- a/usr.sbin/nginx/src/http/ngx_http_header_filter_module.c
+++ b/usr.sbin/nginx/src/http/ngx_http_header_filter_module.c
@@ -379,7 +379,10 @@ ngx_http_header_filter(ngx_http_request_t *r)
len += sizeof("Transfer-Encoding: chunked" CRLF) - 1;
}
- if (r->keepalive) {
+ if (r->headers_out.status == NGX_HTTP_SWITCHING_PROTOCOLS) {
+ len += sizeof("Connection: upgrade" CRLF) - 1;
+
+ } else if (r->keepalive) {
len += sizeof("Connection: keep-alive" CRLF) - 1;
/*
@@ -548,7 +551,11 @@ ngx_http_header_filter(ngx_http_request_t *r)
sizeof("Transfer-Encoding: chunked" CRLF) - 1);
}
- if (r->keepalive) {
+ if (r->headers_out.status == NGX_HTTP_SWITCHING_PROTOCOLS) {
+ b->last = ngx_cpymem(b->last, "Connection: upgrade" CRLF,
+ sizeof("Connection: upgrade" CRLF) - 1);
+
+ } else if (r->keepalive) {
b->last = ngx_cpymem(b->last, "Connection: keep-alive" CRLF,
sizeof("Connection: keep-alive" CRLF) - 1);
diff --git a/usr.sbin/nginx/src/http/ngx_http_parse.c b/usr.sbin/nginx/src/http/ngx_http_parse.c
index be750edd084..3c168aaf25b 100644
--- a/usr.sbin/nginx/src/http/ngx_http_parse.c
+++ b/usr.sbin/nginx/src/http/ngx_http_parse.c
@@ -1075,6 +1075,154 @@ header_done:
ngx_int_t
+ngx_http_parse_uri(ngx_http_request_t *r)
+{
+ u_char *p, ch;
+ enum {
+ sw_start = 0,
+ sw_after_slash_in_uri,
+ sw_check_uri,
+ sw_uri
+ } state;
+
+ state = sw_start;
+
+ for (p = r->uri_start; p != r->uri_end; p++) {
+
+ ch = *p;
+
+ switch (state) {
+
+ case sw_start:
+
+ if (ch != '/') {
+ return NGX_ERROR;
+ }
+
+ state = sw_after_slash_in_uri;
+ break;
+
+ /* check "/.", "//", "%", and "\" (Win32) in URI */
+ case sw_after_slash_in_uri:
+
+ if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
+ state = sw_check_uri;
+ break;
+ }
+
+ switch (ch) {
+ case ' ':
+ r->space_in_uri = 1;
+ state = sw_check_uri;
+ break;
+ case '.':
+ r->complex_uri = 1;
+ state = sw_uri;
+ break;
+ case '%':
+ r->quoted_uri = 1;
+ state = sw_uri;
+ break;
+ case '/':
+ r->complex_uri = 1;
+ state = sw_uri;
+ break;
+#if (NGX_WIN32)
+ case '\\':
+ r->complex_uri = 1;
+ state = sw_uri;
+ break;
+#endif
+ case '?':
+ r->args_start = p + 1;
+ state = sw_uri;
+ break;
+ case '#':
+ r->complex_uri = 1;
+ state = sw_uri;
+ break;
+ case '+':
+ r->plus_in_uri = 1;
+ break;
+ default:
+ state = sw_check_uri;
+ break;
+ }
+ break;
+
+ /* check "/", "%" and "\" (Win32) in URI */
+ case sw_check_uri:
+
+ if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
+ break;
+ }
+
+ switch (ch) {
+ case '/':
+#if (NGX_WIN32)
+ if (r->uri_ext == p) {
+ r->complex_uri = 1;
+ state = sw_uri;
+ break;
+ }
+#endif
+ r->uri_ext = NULL;
+ state = sw_after_slash_in_uri;
+ break;
+ case '.':
+ r->uri_ext = p + 1;
+ break;
+ case ' ':
+ r->space_in_uri = 1;
+ break;
+#if (NGX_WIN32)
+ case '\\':
+ r->complex_uri = 1;
+ state = sw_after_slash_in_uri;
+ break;
+#endif
+ case '%':
+ r->quoted_uri = 1;
+ state = sw_uri;
+ break;
+ case '?':
+ r->args_start = p + 1;
+ state = sw_uri;
+ break;
+ case '#':
+ r->complex_uri = 1;
+ state = sw_uri;
+ break;
+ case '+':
+ r->plus_in_uri = 1;
+ break;
+ }
+ break;
+
+ /* URI */
+ case sw_uri:
+
+ if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
+ break;
+ }
+
+ switch (ch) {
+ case ' ':
+ r->space_in_uri = 1;
+ break;
+ case '#':
+ r->complex_uri = 1;
+ break;
+ }
+ break;
+ }
+ }
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
ngx_http_parse_complex_uri(ngx_http_request_t *r, ngx_uint_t merge_slashes)
{
u_char c, ch, decoded, *p, *u;
@@ -1818,3 +1966,263 @@ ngx_http_split_args(ngx_http_request_t *r, ngx_str_t *uri, ngx_str_t *args)
args->len = 0;
}
}
+
+
+ngx_int_t
+ngx_http_parse_chunked(ngx_http_request_t *r, ngx_buf_t *b,
+ ngx_http_chunked_t *ctx)
+{
+ u_char *pos, ch, c;
+ ngx_int_t rc;
+ 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;
+
+ state = ctx->state;
+
+ if (state == sw_chunk_data && ctx->size == 0) {
+ state = sw_after_data;
+ }
+
+ rc = NGX_AGAIN;
+
+ for (pos = b->pos; pos < b->last; pos++) {
+
+ ch = *pos;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http 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 ';':
+ case ' ':
+ case '\t':
+ 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 ';':
+ case ' ':
+ case '\t':
+ 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;
+ b->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;
+
+ }
+
+ if (ctx->size < 0 || ctx->length < 0) {
+ goto invalid;
+ }
+
+ return rc;
+
+done:
+
+ ctx->state = 0;
+ b->pos = pos + 1;
+
+ return NGX_DONE;
+
+invalid:
+
+ return NGX_ERROR;
+}
diff --git a/usr.sbin/nginx/src/http/ngx_http_request.c b/usr.sbin/nginx/src/http/ngx_http_request.c
index 268cc37f354..6afca1140e6 100644
--- a/usr.sbin/nginx/src/http/ngx_http_request.c
+++ b/usr.sbin/nginx/src/http/ngx_http_request.c
@@ -10,7 +10,7 @@
#include <ngx_http.h>
-static void ngx_http_init_request(ngx_event_t *ev);
+static void ngx_http_wait_request_handler(ngx_event_t *ev);
static void ngx_http_process_request_line(ngx_event_t *rev);
static void ngx_http_process_request_headers(ngx_event_t *rev);
static ssize_t ngx_http_read_request_header(ngx_http_request_t *r);
@@ -21,21 +21,22 @@ static ngx_int_t ngx_http_process_header_line(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t ngx_http_process_unique_header_line(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_process_multi_header_lines(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t ngx_http_process_host(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t ngx_http_process_connection(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t ngx_http_process_user_agent(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
-static ngx_int_t ngx_http_process_cookie(ngx_http_request_t *r,
- ngx_table_elt_t *h, ngx_uint_t offset);
-static ngx_int_t ngx_http_process_request_header(ngx_http_request_t *r);
-static void ngx_http_process_request(ngx_http_request_t *r);
-static ssize_t ngx_http_validate_host(ngx_http_request_t *r, u_char **host,
- size_t len, ngx_uint_t alloc);
-static ngx_int_t ngx_http_find_virtual_server(ngx_http_request_t *r,
- u_char *host, size_t len);
+static ngx_int_t ngx_http_validate_host(ngx_str_t *host, ngx_pool_t *pool,
+ ngx_uint_t alloc);
+static ngx_int_t ngx_http_set_virtual_server(ngx_http_request_t *r,
+ ngx_str_t *host);
+static ngx_int_t ngx_http_find_virtual_server(ngx_connection_t *c,
+ ngx_http_virtual_names_t *virtual_names, ngx_str_t *host,
+ ngx_http_request_t *r, ngx_http_core_srv_conf_t **cscfp);
static void ngx_http_request_handler(ngx_event_t *ev);
static void ngx_http_terminate_request(ngx_http_request_t *r, ngx_int_t rc);
@@ -51,9 +52,7 @@ static void ngx_http_set_lingering_close(ngx_http_request_t *r);
static void ngx_http_lingering_close_handler(ngx_event_t *ev);
static ngx_int_t ngx_http_post_action(ngx_http_request_t *r);
static void ngx_http_close_request(ngx_http_request_t *r, ngx_int_t error);
-static void ngx_http_free_request(ngx_http_request_t *r, ngx_int_t error);
static void ngx_http_log_request(ngx_http_request_t *r);
-static void ngx_http_close_connection(ngx_connection_t *c);
static u_char *ngx_http_log_error(ngx_log_t *log, u_char *buf, size_t len);
static u_char *ngx_http_log_error_handler(ngx_http_request_t *r,
@@ -93,6 +92,14 @@ ngx_http_header_t ngx_http_headers_in[] = {
offsetof(ngx_http_headers_in_t, if_unmodified_since),
ngx_http_process_unique_header_line },
+ { ngx_string("If-Match"),
+ offsetof(ngx_http_headers_in_t, if_match),
+ ngx_http_process_unique_header_line },
+
+ { ngx_string("If-None-Match"),
+ offsetof(ngx_http_headers_in_t, if_none_match),
+ ngx_http_process_unique_header_line },
+
{ ngx_string("User-Agent"), offsetof(ngx_http_headers_in_t, user_agent),
ngx_http_process_user_agent },
@@ -122,6 +129,10 @@ ngx_http_header_t ngx_http_headers_in[] = {
offsetof(ngx_http_headers_in_t, expect),
ngx_http_process_unique_header_line },
+ { ngx_string("Upgrade"),
+ offsetof(ngx_http_headers_in_t, upgrade),
+ ngx_http_process_header_line },
+
#if (NGX_HTTP_GZIP)
{ ngx_string("Accept-Encoding"),
offsetof(ngx_http_headers_in_t, accept_encoding),
@@ -141,7 +152,7 @@ ngx_http_header_t ngx_http_headers_in[] = {
#if (NGX_HTTP_X_FORWARDED_FOR)
{ ngx_string("X-Forwarded-For"),
offsetof(ngx_http_headers_in_t, x_forwarded_for),
- ngx_http_process_header_line },
+ ngx_http_process_multi_header_lines },
#endif
#if (NGX_HTTP_REALIP)
@@ -173,7 +184,8 @@ ngx_http_header_t ngx_http_headers_in[] = {
ngx_http_process_header_line },
#endif
- { ngx_string("Cookie"), 0, ngx_http_process_cookie },
+ { ngx_string("Cookie"), offsetof(ngx_http_headers_in_t, cookies),
+ ngx_http_process_multi_header_lines },
{ ngx_null_string, 0, NULL }
};
@@ -182,137 +194,30 @@ ngx_http_header_t ngx_http_headers_in[] = {
void
ngx_http_init_connection(ngx_connection_t *c)
{
- ngx_event_t *rev;
- ngx_http_log_ctx_t *ctx;
-
- ctx = ngx_palloc(c->pool, sizeof(ngx_http_log_ctx_t));
- if (ctx == NULL) {
- ngx_http_close_connection(c);
- return;
- }
-
- ctx->connection = c;
- ctx->request = NULL;
- ctx->current_request = NULL;
-
- c->log->connection = c->number;
- c->log->handler = ngx_http_log_error;
- c->log->data = ctx;
- c->log->action = "reading client request line";
-
- c->log_error = NGX_ERROR_INFO;
-
- rev = c->read;
- rev->handler = ngx_http_init_request;
- c->write->handler = ngx_http_empty_handler;
-
-#if (NGX_STAT_STUB)
- (void) ngx_atomic_fetch_add(ngx_stat_reading, 1);
-#endif
-
- if (rev->ready) {
- /* the deferred accept(), rtsig, aio, iocp */
-
- if (ngx_use_accept_mutex) {
- ngx_post_event(rev, &ngx_posted_events);
- return;
- }
-
- ngx_http_init_request(rev);
- return;
- }
-
- ngx_add_timer(rev, c->listening->post_accept_timeout);
-
- if (ngx_handle_read_event(rev, 0) != NGX_OK) {
-#if (NGX_STAT_STUB)
- (void) ngx_atomic_fetch_add(ngx_stat_reading, -1);
-#endif
- ngx_http_close_connection(c);
- return;
- }
-}
-
-
-static void
-ngx_http_init_request(ngx_event_t *rev)
-{
- ngx_time_t *tp;
- ngx_uint_t i;
- ngx_connection_t *c;
- ngx_http_request_t *r;
- struct sockaddr_in *sin;
- ngx_http_port_t *port;
- ngx_http_in_addr_t *addr;
- ngx_http_log_ctx_t *ctx;
- ngx_http_addr_conf_t *addr_conf;
- ngx_http_connection_t *hc;
- ngx_http_core_srv_conf_t *cscf;
- ngx_http_core_loc_conf_t *clcf;
- ngx_http_core_main_conf_t *cmcf;
+ ngx_uint_t i;
+ ngx_event_t *rev;
+ struct sockaddr_in *sin;
+ ngx_http_port_t *port;
+ ngx_http_in_addr_t *addr;
+ ngx_http_log_ctx_t *ctx;
+ ngx_http_connection_t *hc;
#if (NGX_HAVE_INET6)
- struct sockaddr_in6 *sin6;
- ngx_http_in6_addr_t *addr6;
-#endif
-
-#if (NGX_STAT_STUB)
- (void) ngx_atomic_fetch_add(ngx_stat_reading, -1);
+ struct sockaddr_in6 *sin6;
+ ngx_http_in6_addr_t *addr6;
#endif
- c = rev->data;
-
- if (rev->timedout) {
- ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
-
+ hc = ngx_pcalloc(c->pool, sizeof(ngx_http_connection_t));
+ if (hc == NULL) {
ngx_http_close_connection(c);
return;
}
- c->requests++;
-
- hc = c->data;
-
- if (hc == NULL) {
- hc = ngx_pcalloc(c->pool, sizeof(ngx_http_connection_t));
- if (hc == NULL) {
- ngx_http_close_connection(c);
- return;
- }
- }
-
- r = hc->request;
-
- if (r) {
- ngx_memzero(r, sizeof(ngx_http_request_t));
-
- r->pipeline = hc->pipeline;
-
- if (hc->nbusy) {
- r->header_in = hc->busy[0];
- }
-
- } else {
- r = ngx_pcalloc(c->pool, sizeof(ngx_http_request_t));
- if (r == NULL) {
- ngx_http_close_connection(c);
- return;
- }
-
- hc->request = r;
- }
-
- c->data = r;
- r->http_connection = hc;
-
- c->sent = 0;
- r->signature = NGX_HTTP_MODULE;
+ c->data = hc;
/* find the server configuration for the address:port */
port = c->listening->servers;
- r->connection = c;
-
if (port->naddrs > 1) {
/*
@@ -342,7 +247,7 @@ ngx_http_init_request(ngx_event_t *rev)
}
}
- addr_conf = &addr6[i].conf;
+ hc->addr_conf = &addr6[i].conf;
break;
#endif
@@ -360,7 +265,7 @@ ngx_http_init_request(ngx_event_t *rev)
}
}
- addr_conf = &addr[i].conf;
+ hc->addr_conf = &addr[i].conf;
break;
}
@@ -372,108 +277,278 @@ ngx_http_init_request(ngx_event_t *rev)
#if (NGX_HAVE_INET6)
case AF_INET6:
addr6 = port->addrs;
- addr_conf = &addr6[0].conf;
+ hc->addr_conf = &addr6[0].conf;
break;
#endif
default: /* AF_INET */
addr = port->addrs;
- addr_conf = &addr[0].conf;
+ hc->addr_conf = &addr[0].conf;
break;
}
}
- r->virtual_names = addr_conf->virtual_names;
-
/* the default server configuration for the address:port */
- cscf = addr_conf->default_server;
+ hc->conf_ctx = hc->addr_conf->default_server->ctx;
- r->main_conf = cscf->ctx->main_conf;
- r->srv_conf = cscf->ctx->srv_conf;
- r->loc_conf = cscf->ctx->loc_conf;
+ ctx = ngx_palloc(c->pool, sizeof(ngx_http_log_ctx_t));
+ if (ctx == NULL) {
+ ngx_http_close_connection(c);
+ return;
+ }
- rev->handler = ngx_http_process_request_line;
- r->read_event_handler = ngx_http_block_reading;
+ ctx->connection = c;
+ ctx->request = NULL;
+ ctx->current_request = NULL;
-#if (NGX_HTTP_SSL)
+ c->log->connection = c->number;
+ c->log->handler = ngx_http_log_error;
+ c->log->data = ctx;
+ c->log->action = "waiting for request";
+
+ c->log_error = NGX_ERROR_INFO;
+
+ rev = c->read;
+ rev->handler = ngx_http_wait_request_handler;
+ c->write->handler = ngx_http_empty_handler;
+
+#if (NGX_HTTP_SPDY)
+ if (hc->addr_conf->spdy) {
+ rev->handler = ngx_http_spdy_init;
+ }
+#endif
+#if (NGX_HTTP_SSL)
{
ngx_http_ssl_srv_conf_t *sscf;
- sscf = ngx_http_get_module_srv_conf(r, ngx_http_ssl_module);
- if (sscf->enable || addr_conf->ssl) {
+ sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module);
- if (c->ssl == NULL) {
+ if (sscf->enable || hc->addr_conf->ssl) {
- c->log->action = "SSL handshaking";
+ c->log->action = "SSL handshaking";
- if (addr_conf->ssl && sscf->ssl.ctx == NULL) {
- ngx_log_error(NGX_LOG_ERR, c->log, 0,
- "no \"ssl_certificate\" is defined "
- "in server listening on SSL port");
- ngx_http_close_connection(c);
- return;
- }
+ if (hc->addr_conf->ssl && sscf->ssl.ctx == NULL) {
+ ngx_log_error(NGX_LOG_ERR, c->log, 0,
+ "no \"ssl_certificate\" is defined "
+ "in server listening on SSL port");
+ ngx_http_close_connection(c);
+ return;
+ }
- if (ngx_ssl_create_connection(&sscf->ssl, c, NGX_SSL_BUFFER)
- != NGX_OK)
- {
- ngx_http_close_connection(c);
- return;
- }
+ hc->ssl = 1;
+
+ rev->handler = ngx_http_ssl_handshake;
+ }
+ }
+#endif
+
+ if (rev->ready) {
+ /* the deferred accept(), rtsig, aio, iocp */
- rev->handler = ngx_http_ssl_handshake;
+ if (ngx_use_accept_mutex) {
+ ngx_post_event(rev, &ngx_posted_events);
+ return;
}
- r->main_filter_need_in_memory = 1;
+ rev->handler(rev);
+ return;
}
+
+ ngx_add_timer(rev, c->listening->post_accept_timeout);
+ ngx_reusable_connection(c, 1);
+
+ if (ngx_handle_read_event(rev, 0) != NGX_OK) {
+ ngx_http_close_connection(c);
+ return;
}
+}
-#endif
- clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
- c->log->file = clcf->error_log->file;
-#if (NGX_ENABLE_SYSLOG)
- c->log->priority = clcf->error_log->priority;
-#endif
- if (!(c->log->log_level & NGX_LOG_DEBUG_CONNECTION)) {
- c->log->log_level = clcf->error_log->log_level;
+static void
+ngx_http_wait_request_handler(ngx_event_t *rev)
+{
+ size_t size;
+ ssize_t n;
+ ngx_buf_t *b;
+ ngx_connection_t *c;
+ ngx_http_connection_t *hc;
+ ngx_http_core_srv_conf_t *cscf;
+
+ c = rev->data;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http wait request handler");
+
+ if (rev->timedout) {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ if (c->close) {
+ ngx_http_close_connection(c);
+ return;
}
- if (c->buffer == NULL) {
- c->buffer = ngx_create_temp_buf(c->pool,
- cscf->client_header_buffer_size);
- if (c->buffer == NULL) {
+ hc = c->data;
+ cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module);
+
+ size = cscf->client_header_buffer_size;
+
+ b = c->buffer;
+
+ if (b == NULL) {
+ b = ngx_create_temp_buf(c->pool, size);
+ if (b == NULL) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ c->buffer = b;
+
+ } else if (b->start == NULL) {
+
+ b->start = ngx_palloc(c->pool, size);
+ if (b->start == NULL) {
ngx_http_close_connection(c);
return;
}
+
+ b->pos = b->start;
+ b->last = b->start;
+ b->end = b->last + size;
}
- if (r->header_in == NULL) {
- r->header_in = c->buffer;
+ n = c->recv(c, b->last, size);
+
+ if (n == NGX_AGAIN) {
+
+#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)
+ if (c->listening->deferred_accept
+#if (NGX_HTTP_SSL)
+ && c->ssl == NULL
+#endif
+ )
+ {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
+ "client timed out in deferred accept");
+ ngx_http_close_connection(c);
+ return;
+ }
+#endif
+
+ if (!rev->timer_set) {
+ ngx_add_timer(rev, c->listening->post_accept_timeout);
+ ngx_reusable_connection(c, 1);
+ }
+
+ if (ngx_handle_read_event(rev, 0) != NGX_OK) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ /*
+ * We are trying to not hold c->buffer's memory for an idle connection.
+ */
+
+ if (ngx_pfree(c->pool, b->start) == NGX_OK) {
+ b->start = NULL;
+ }
+
+ return;
}
- r->pool = ngx_create_pool(cscf->request_pool_size, c->log);
- if (r->pool == NULL) {
+ if (n == NGX_ERROR) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ if (n == 0) {
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "client closed connection");
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ b->last += n;
+
+ c->log->action = "reading client request line";
+
+ ngx_reusable_connection(c, 0);
+
+ c->data = ngx_http_create_request(c);
+ if (c->data == NULL) {
ngx_http_close_connection(c);
return;
}
+ rev->handler = ngx_http_process_request_line;
+ ngx_http_process_request_line(rev);
+}
+
+
+ngx_http_request_t *
+ngx_http_create_request(ngx_connection_t *c)
+{
+ ngx_pool_t *pool;
+ ngx_time_t *tp;
+ ngx_http_request_t *r;
+ ngx_http_log_ctx_t *ctx;
+ ngx_http_connection_t *hc;
+ ngx_http_core_srv_conf_t *cscf;
+ ngx_http_core_loc_conf_t *clcf;
+ ngx_http_core_main_conf_t *cmcf;
+
+ c->requests++;
+
+ hc = c->data;
+
+ cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module);
+
+ pool = ngx_create_pool(cscf->request_pool_size, c->log);
+ if (pool == NULL) {
+ return NULL;
+ }
+
+ r = ngx_pcalloc(pool, sizeof(ngx_http_request_t));
+ if (r == NULL) {
+ ngx_destroy_pool(pool);
+ return NULL;
+ }
+
+ r->pool = pool;
+
+ r->http_connection = hc;
+ r->signature = NGX_HTTP_MODULE;
+ r->connection = c;
+
+ r->main_conf = hc->conf_ctx->main_conf;
+ r->srv_conf = hc->conf_ctx->srv_conf;
+ r->loc_conf = hc->conf_ctx->loc_conf;
+
+ r->read_event_handler = ngx_http_block_reading;
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ ngx_http_set_connection_log(r->connection, clcf->error_log);
+#if (NGX_ENABLE_SYSLOG)
+ c->log->priority = clcf->error_log->priority;
+#endif
+
+ r->header_in = hc->nbusy ? hc->busy[0] : c->buffer;
if (ngx_list_init(&r->headers_out.headers, r->pool, 20,
sizeof(ngx_table_elt_t))
!= NGX_OK)
{
ngx_destroy_pool(r->pool);
- ngx_http_close_connection(c);
- return;
+ return NULL;
}
r->ctx = ngx_pcalloc(r->pool, sizeof(void *) * ngx_http_max_module);
if (r->ctx == NULL) {
ngx_destroy_pool(r->pool);
- ngx_http_close_connection(c);
- return;
+ return NULL;
}
cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
@@ -482,12 +557,14 @@ ngx_http_init_request(ngx_event_t *rev)
* sizeof(ngx_http_variable_value_t));
if (r->variables == NULL) {
ngx_destroy_pool(r->pool);
- ngx_http_close_connection(c);
- return;
+ return NULL;
}
- c->single_connection = 1;
- c->destroyed = 0;
+#if (NGX_HTTP_SSL)
+ if (c->ssl) {
+ r->main_filter_need_in_memory = 1;
+ }
+#endif
r->main = r;
r->count = 1;
@@ -519,7 +596,7 @@ ngx_http_init_request(ngx_event_t *rev)
(void) ngx_atomic_fetch_add(ngx_stat_requests, 1);
#endif
- rev->handler(rev);
+ return r;
}
@@ -528,37 +605,63 @@ ngx_http_init_request(ngx_event_t *rev)
static void
ngx_http_ssl_handshake(ngx_event_t *rev)
{
- u_char buf[1];
- ssize_t n;
- ngx_int_t rc;
- ngx_connection_t *c;
- ngx_http_request_t *r;
+ u_char buf[1];
+ ssize_t n;
+ ngx_err_t err;
+ ngx_int_t rc;
+ ngx_connection_t *c;
+ ngx_http_connection_t *hc;
+ ngx_http_ssl_srv_conf_t *sscf;
c = rev->data;
- r = c->data;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0,
"http check ssl handshake");
if (rev->timedout) {
ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
- c->timedout = 1;
- ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT);
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ if (c->close) {
+ ngx_http_close_connection(c);
return;
}
n = recv(c->fd, (char *) buf, 1, MSG_PEEK);
- if (n == -1 && ngx_socket_errno == NGX_EAGAIN) {
+ err = ngx_socket_errno;
- if (!rev->timer_set) {
- ngx_add_timer(rev, c->listening->post_accept_timeout);
- }
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http recv(): %d", n);
- if (ngx_handle_read_event(rev, 0) != NGX_OK) {
- ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ if (n == -1) {
+ if (err == NGX_EAGAIN) {
+
+#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)
+ if (c->listening->deferred_accept) {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
+ "client timed out in deferred accept");
+ ngx_http_close_connection(c);
+ return;
+ }
+#endif
+
+ if (!rev->timer_set) {
+ ngx_add_timer(rev, c->listening->post_accept_timeout);
+ ngx_reusable_connection(c, 1);
+ }
+
+ if (ngx_handle_read_event(rev, 0) != NGX_OK) {
+ ngx_http_close_connection(c);
+ }
+
+ return;
}
+ ngx_connection_error(c, err, "recv() failed");
+ ngx_http_close_connection(c);
+
return;
}
@@ -567,6 +670,17 @@ ngx_http_ssl_handshake(ngx_event_t *rev)
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, rev->log, 0,
"https ssl handshake: 0x%02Xd", buf[0]);
+ hc = c->data;
+ sscf = ngx_http_get_module_srv_conf(hc->conf_ctx,
+ ngx_http_ssl_module);
+
+ if (ngx_ssl_create_connection(&sscf->ssl, c, NGX_SSL_BUFFER)
+ != NGX_OK)
+ {
+ ngx_http_close_connection(c);
+ return;
+ }
+
rc = ngx_ssl_handshake(c);
if (rc == NGX_AGAIN) {
@@ -575,6 +689,8 @@ ngx_http_ssl_handshake(ngx_event_t *rev)
ngx_add_timer(rev, c->listening->post_accept_timeout);
}
+ ngx_reusable_connection(c, 0);
+
c->ssl->handler = ngx_http_ssl_handshake_handler;
return;
}
@@ -582,27 +698,26 @@ ngx_http_ssl_handshake(ngx_event_t *rev)
ngx_http_ssl_handshake_handler(c);
return;
+ }
- } else {
- ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0,
- "plain http");
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "plain http");
- r->plain_http = 1;
- }
- }
+ c->log->action = "waiting for request";
- c->log->action = "reading client request line";
+ rev->handler = ngx_http_wait_request_handler;
+ ngx_http_wait_request_handler(rev);
- rev->handler = ngx_http_process_request_line;
- ngx_http_process_request_line(rev);
+ return;
+ }
+
+ ngx_log_error(NGX_LOG_INFO, c->log, 0, "client closed connection");
+ ngx_http_close_connection(c);
}
static void
ngx_http_ssl_handshake_handler(ngx_connection_t *c)
{
- ngx_http_request_t *r;
-
if (c->ssl->handshaked) {
/*
@@ -615,21 +730,38 @@ ngx_http_ssl_handshake_handler(ngx_connection_t *c)
c->ssl->no_wait_shutdown = 1;
- c->log->action = "reading client request line";
+#if (NGX_HTTP_SPDY && defined TLSEXT_TYPE_next_proto_neg)
+ {
+ unsigned int len;
+ const unsigned char *data;
+ static const ngx_str_t spdy = ngx_string(NGX_SPDY_NPN_NEGOTIATED);
+
+ SSL_get0_next_proto_negotiated(c->ssl->connection, &data, &len);
- c->read->handler = ngx_http_process_request_line;
+ if (len == spdy.len && ngx_strncmp(data, spdy.data, spdy.len) == 0) {
+ ngx_http_spdy_init(c->read);
+ return;
+ }
+ }
+#endif
+
+ c->log->action = "waiting for request";
+
+ c->read->handler = ngx_http_wait_request_handler;
/* STUB: epoll edge */ c->write->handler = ngx_http_empty_handler;
- ngx_http_process_request_line(c->read);
+ ngx_reusable_connection(c, 1);
+
+ ngx_http_wait_request_handler(c->read);
return;
}
- r = c->data;
-
- ngx_http_close_request(r, NGX_HTTP_BAD_REQUEST);
+ if (c->read->timedout) {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
+ }
- return;
+ ngx_http_close_connection(c);
}
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
@@ -637,12 +769,13 @@ ngx_http_ssl_handshake_handler(ngx_connection_t *c)
int
ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg)
{
- size_t len;
- u_char *host;
- const char *servername;
- ngx_connection_t *c;
- ngx_http_request_t *r;
- ngx_http_ssl_srv_conf_t *sscf;
+ ngx_str_t host;
+ const char *servername;
+ ngx_connection_t *c;
+ ngx_http_connection_t *hc;
+ ngx_http_ssl_srv_conf_t *sscf;
+ ngx_http_core_loc_conf_t *clcf;
+ ngx_http_core_srv_conf_t *cscf;
servername = SSL_get_servername(ssl_conn, TLSEXT_NAMETYPE_host_name);
@@ -655,27 +788,44 @@ ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg)
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
"SSL server name: \"%s\"", servername);
- len = ngx_strlen(servername);
+ host.len = ngx_strlen(servername);
- if (len == 0) {
+ if (host.len == 0) {
return SSL_TLSEXT_ERR_NOACK;
}
- r = c->data;
+ host.data = (u_char *) servername;
- host = (u_char *) servername;
+ if (ngx_http_validate_host(&host, c->pool, 1) != NGX_OK) {
+ return SSL_TLSEXT_ERR_NOACK;
+ }
- len = ngx_http_validate_host(r, &host, len, 1);
+ hc = c->data;
- if (len <= 0) {
+ if (ngx_http_find_virtual_server(c, hc->addr_conf->virtual_names, &host,
+ NULL, &cscf)
+ != NGX_OK)
+ {
return SSL_TLSEXT_ERR_NOACK;
}
- if (ngx_http_find_virtual_server(r, host, len) != NGX_OK) {
+ hc->ssl_servername = ngx_palloc(c->pool, sizeof(ngx_str_t));
+ if (hc->ssl_servername == NULL) {
return SSL_TLSEXT_ERR_NOACK;
}
- sscf = ngx_http_get_module_srv_conf(r, ngx_http_ssl_module);
+ *hc->ssl_servername = host;
+
+ hc->conf_ctx = cscf->ctx;
+
+ clcf = ngx_http_get_module_loc_conf(hc->conf_ctx, ngx_http_core_module);
+
+ ngx_http_set_connection_log(c, clcf->error_log);
+#if (NGX_ENABLE_SYSLOG)
+ c->log->priority = clcf->error_log->priority;
+#endif
+
+ sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module);
if (sscf->ssl.ctx) {
SSL_set_SSL_CTX(ssl_conn, sscf->ssl.ctx);
@@ -710,12 +860,11 @@ ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg)
static void
ngx_http_process_request_line(ngx_event_t *rev)
{
- u_char *host;
- ssize_t n;
- ngx_int_t rc, rv;
- ngx_connection_t *c;
- ngx_http_request_t *r;
- ngx_http_core_srv_conf_t *cscf;
+ ssize_t n;
+ ngx_int_t rc, rv;
+ ngx_str_t host;
+ ngx_connection_t *c;
+ ngx_http_request_t *r;
c = rev->data;
r = c->data;
@@ -752,159 +901,52 @@ ngx_http_process_request_line(ngx_event_t *rev)
r->request_line.data = r->request_start;
r->request_length = r->header_in->pos - r->request_start;
-
- if (r->args_start) {
- r->uri.len = r->args_start - 1 - r->uri_start;
- } else {
- r->uri.len = r->uri_end - r->uri_start;
- }
-
-
- if (r->complex_uri || r->quoted_uri) {
-
- r->uri.data = ngx_pnalloc(r->pool, r->uri.len + 1);
- if (r->uri.data == NULL) {
- ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
- return;
- }
-
- cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
-
- rc = ngx_http_parse_complex_uri(r, cscf->merge_slashes);
-
- if (rc == NGX_HTTP_PARSE_INVALID_REQUEST) {
- ngx_log_error(NGX_LOG_INFO, c->log, 0,
- "client sent invalid request");
- ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
- return;
- }
-
- } else {
- r->uri.data = r->uri_start;
- }
-
-
- r->unparsed_uri.len = r->uri_end - r->uri_start;
- r->unparsed_uri.data = r->uri_start;
-
- r->valid_unparsed_uri = r->space_in_uri ? 0 : 1;
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http request line: \"%V\"", &r->request_line);
r->method_name.len = r->method_end - r->request_start + 1;
r->method_name.data = r->request_line.data;
-
if (r->http_protocol.data) {
r->http_protocol.len = r->request_end - r->http_protocol.data;
}
-
- if (r->uri_ext) {
- if (r->args_start) {
- r->exten.len = r->args_start - 1 - r->uri_ext;
- } else {
- r->exten.len = r->uri_end - r->uri_ext;
- }
-
- r->exten.data = r->uri_ext;
- }
-
-
- if (r->args_start && r->uri_end > r->args_start) {
- r->args.len = r->uri_end - r->args_start;
- r->args.data = r->args_start;
- }
-
-#if (NGX_WIN32)
- {
- u_char *p, *last;
-
- p = r->uri.data;
- last = r->uri.data + r->uri.len;
-
- while (p < last) {
-
- if (*p++ == ':') {
-
- /*
- * this check covers "::$data", "::$index_allocation" and
- * ":$i30:$index_allocation"
- */
-
- if (p < last && *p == '$') {
- ngx_log_error(NGX_LOG_INFO, c->log, 0,
- "client sent unsafe win32 URI");
- ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
- return;
- }
- }
- }
-
- p = r->uri.data + r->uri.len - 1;
-
- while (p > r->uri.data) {
-
- if (*p == ' ') {
- p--;
- continue;
- }
-
- if (*p == '.') {
- p--;
- continue;
- }
-
- break;
- }
-
- if (p != r->uri.data + r->uri.len - 1) {
- r->uri.len = p + 1 - r->uri.data;
- ngx_http_set_exten(r);
- }
-
+ if (ngx_http_process_request_uri(r) != NGX_OK) {
+ return;
}
-#endif
-
- ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
- "http request line: \"%V\"", &r->request_line);
-
- ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
- "http uri: \"%V\"", &r->uri);
-
- ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
- "http args: \"%V\"", &r->args);
-
- ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
- "http exten: \"%V\"", &r->exten);
if (r->host_start && r->host_end) {
- host = r->host_start;
- n = ngx_http_validate_host(r, &host,
- r->host_end - r->host_start, 0);
+ host.len = r->host_end - r->host_start;
+ host.data = r->host_start;
+
+ rc = ngx_http_validate_host(&host, r->pool, 0);
- if (n == 0) {
+ if (rc == NGX_DECLINED) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"client sent invalid host in request line");
ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
return;
}
- if (n < 0) {
+ if (rc == NGX_ERROR) {
ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
- r->headers_in.server.len = n;
- r->headers_in.server.data = host;
+ if (ngx_http_set_virtual_server(r, &host) == NGX_ERROR) {
+ return;
+ }
+
+ r->headers_in.server = host;
}
if (r->http_version < NGX_HTTP_VERSION_10) {
- if (ngx_http_find_virtual_server(r, r->headers_in.server.data,
- r->headers_in.server.len)
- == NGX_ERROR)
+ if (r->headers_in.server.len == 0
+ && ngx_http_set_virtual_server(r, &r->headers_in.server)
+ == NGX_ERROR)
{
- ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
@@ -921,15 +963,6 @@ ngx_http_process_request_line(ngx_event_t *rev)
return;
}
-
- if (ngx_array_init(&r->headers_in.cookies, r->pool, 2,
- sizeof(ngx_table_elt_t *))
- != NGX_OK)
- {
- ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
- return;
- }
-
c->log->action = "reading client request headers";
rev->handler = ngx_http_process_request_headers;
@@ -973,6 +1006,121 @@ ngx_http_process_request_line(ngx_event_t *rev)
}
+ngx_int_t
+ngx_http_process_request_uri(ngx_http_request_t *r)
+{
+ ngx_http_core_srv_conf_t *cscf;
+
+ if (r->args_start) {
+ r->uri.len = r->args_start - 1 - r->uri_start;
+ } else {
+ r->uri.len = r->uri_end - r->uri_start;
+ }
+
+ if (r->complex_uri || r->quoted_uri) {
+
+ r->uri.data = ngx_pnalloc(r->pool, r->uri.len + 1);
+ if (r->uri.data == NULL) {
+ ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return NGX_ERROR;
+ }
+
+ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
+
+ if (ngx_http_parse_complex_uri(r, cscf->merge_slashes) != NGX_OK) {
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client sent invalid request");
+ ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
+ return NGX_ERROR;
+ }
+
+ } else {
+ r->uri.data = r->uri_start;
+ }
+
+ r->unparsed_uri.len = r->uri_end - r->uri_start;
+ r->unparsed_uri.data = r->uri_start;
+
+ r->valid_unparsed_uri = r->space_in_uri ? 0 : 1;
+
+ if (r->uri_ext) {
+ if (r->args_start) {
+ r->exten.len = r->args_start - 1 - r->uri_ext;
+ } else {
+ r->exten.len = r->uri_end - r->uri_ext;
+ }
+
+ r->exten.data = r->uri_ext;
+ }
+
+ if (r->args_start && r->uri_end > r->args_start) {
+ r->args.len = r->uri_end - r->args_start;
+ r->args.data = r->args_start;
+ }
+
+#if (NGX_WIN32)
+ {
+ u_char *p, *last;
+
+ p = r->uri.data;
+ last = r->uri.data + r->uri.len;
+
+ while (p < last) {
+
+ if (*p++ == ':') {
+
+ /*
+ * this check covers "::$data", "::$index_allocation" and
+ * ":$i30:$index_allocation"
+ */
+
+ if (p < last && *p == '$') {
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client sent unsafe win32 URI");
+ ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
+ return NGX_ERROR;
+ }
+ }
+ }
+
+ p = r->uri.data + r->uri.len - 1;
+
+ while (p > r->uri.data) {
+
+ if (*p == ' ') {
+ p--;
+ continue;
+ }
+
+ if (*p == '.') {
+ p--;
+ continue;
+ }
+
+ break;
+ }
+
+ if (p != r->uri.data + r->uri.len - 1) {
+ r->uri.len = p + 1 - r->uri.data;
+ ngx_http_set_exten(r);
+ }
+
+ }
+#endif
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http uri: \"%V\"", &r->uri);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http args: \"%V\"", &r->args);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http exten: \"%V\"", &r->exten);
+
+ return NGX_OK;
+}
+
+
static void
ngx_http_process_request_headers(ngx_event_t *rev)
{
@@ -1001,7 +1149,6 @@ ngx_http_process_request_headers(ngx_event_t *rev)
}
cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
- cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
rc = NGX_AGAIN;
@@ -1055,6 +1202,9 @@ ngx_http_process_request_headers(ngx_event_t *rev)
}
}
+ /* the host header could change the server configuration context */
+ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
+
rc = ngx_http_parse_header_line(r, r->header_in,
cscf->underscores_in_headers);
@@ -1404,24 +1554,25 @@ static ngx_int_t
ngx_http_process_host(ngx_http_request_t *r, ngx_table_elt_t *h,
ngx_uint_t offset)
{
- u_char *host;
- ssize_t len;
+ ngx_int_t rc;
+ ngx_str_t host;
if (r->headers_in.host == NULL) {
r->headers_in.host = h;
}
- host = h->value.data;
- len = ngx_http_validate_host(r, &host, h->value.len, 0);
+ host = h->value;
+
+ rc = ngx_http_validate_host(&host, r->pool, 0);
- if (len == 0) {
+ if (rc == NGX_DECLINED) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent invalid host header");
ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
return NGX_ERROR;
}
- if (len < 0) {
+ if (rc == NGX_ERROR) {
ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return NGX_ERROR;
}
@@ -1430,8 +1581,11 @@ ngx_http_process_host(ngx_http_request_t *r, ngx_table_elt_t *h,
return NGX_OK;
}
- r->headers_in.server.len = len;
- r->headers_in.server.data = host;
+ if (ngx_http_set_virtual_server(r, &host) == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ r->headers_in.server = host;
return NGX_OK;
}
@@ -1526,31 +1680,41 @@ ngx_http_process_user_agent(ngx_http_request_t *r, ngx_table_elt_t *h,
static ngx_int_t
-ngx_http_process_cookie(ngx_http_request_t *r, ngx_table_elt_t *h,
+ngx_http_process_multi_header_lines(ngx_http_request_t *r, ngx_table_elt_t *h,
ngx_uint_t offset)
{
- ngx_table_elt_t **cookie;
+ ngx_array_t *headers;
+ ngx_table_elt_t **ph;
- cookie = ngx_array_push(&r->headers_in.cookies);
- if (cookie) {
- *cookie = h;
- return NGX_OK;
+ headers = (ngx_array_t *) ((char *) &r->headers_in + offset);
+
+ if (headers->elts == NULL) {
+ if (ngx_array_init(headers, r->pool, 1, sizeof(ngx_table_elt_t *))
+ != NGX_OK)
+ {
+ ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return NGX_ERROR;
+ }
}
- ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ ph = ngx_array_push(headers);
+ if (ph == NULL) {
+ ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return NGX_ERROR;
+ }
- return NGX_ERROR;
+ *ph = h;
+ return NGX_OK;
}
-static ngx_int_t
+ngx_int_t
ngx_http_process_request_header(ngx_http_request_t *r)
{
- if (ngx_http_find_virtual_server(r, r->headers_in.server.data,
- r->headers_in.server.len)
- == NGX_ERROR)
+ if (r->headers_in.server.len == 0
+ && ngx_http_set_virtual_server(r, &r->headers_in.server)
+ == NGX_ERROR)
{
- ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return NGX_ERROR;
}
@@ -1569,19 +1733,11 @@ ngx_http_process_request_header(ngx_http_request_t *r)
if (r->headers_in.content_length_n == NGX_ERROR) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent invalid \"Content-Length\" header");
- ngx_http_finalize_request(r, NGX_HTTP_LENGTH_REQUIRED);
+ ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
return NGX_ERROR;
}
}
- if (r->method & NGX_HTTP_PUT && r->headers_in.content_length_n == -1) {
- ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
- "client sent %V method without \"Content-Length\" header",
- &r->method_name);
- ngx_http_finalize_request(r, NGX_HTTP_LENGTH_REQUIRED);
- return NGX_ERROR;
- }
-
if (r->method & NGX_HTTP_TRACE) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent TRACE method");
@@ -1589,14 +1745,25 @@ ngx_http_process_request_header(ngx_http_request_t *r)
return NGX_ERROR;
}
- if (r->headers_in.transfer_encoding
- && ngx_strcasestrn(r->headers_in.transfer_encoding->value.data,
- "chunked", 7 - 1))
- {
- ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
- "client sent \"Transfer-Encoding: chunked\" header");
- ngx_http_finalize_request(r, NGX_HTTP_LENGTH_REQUIRED);
- return NGX_ERROR;
+ if (r->headers_in.transfer_encoding) {
+ if (r->headers_in.transfer_encoding->value.len == 7
+ && ngx_strncasecmp(r->headers_in.transfer_encoding->value.data,
+ (u_char *) "chunked", 7) == 0)
+ {
+ r->headers_in.content_length = NULL;
+ r->headers_in.content_length_n = -1;
+ r->headers_in.chunked = 1;
+
+ } else if (r->headers_in.transfer_encoding->value.len != 8
+ || ngx_strncasecmp(r->headers_in.transfer_encoding->value.data,
+ (u_char *) "identity", 8) != 0)
+ {
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client sent unknown \"Transfer-Encoding\": \"%V\"",
+ &r->headers_in.transfer_encoding->value);
+ ngx_http_finalize_request(r, NGX_HTTP_NOT_IMPLEMENTED);
+ return NGX_ERROR;
+ }
}
if (r->headers_in.connection_type == NGX_HTTP_CONNECTION_KEEP_ALIVE) {
@@ -1611,27 +1778,27 @@ ngx_http_process_request_header(ngx_http_request_t *r)
}
-static void
+void
ngx_http_process_request(ngx_http_request_t *r)
{
ngx_connection_t *c;
c = r->connection;
- if (r->plain_http) {
- ngx_log_error(NGX_LOG_INFO, c->log, 0,
- "client sent plain HTTP request to HTTPS port");
- ngx_http_finalize_request(r, NGX_HTTP_TO_HTTPS);
- return;
- }
-
#if (NGX_HTTP_SSL)
- if (c->ssl) {
+ if (r->http_connection->ssl) {
long rc;
X509 *cert;
ngx_http_ssl_srv_conf_t *sscf;
+ if (c->ssl == NULL) {
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "client sent plain HTTP request to HTTPS port");
+ ngx_http_finalize_request(r, NGX_HTTP_TO_HTTPS);
+ return;
+ }
+
sscf = ngx_http_get_module_srv_conf(r, ngx_http_ssl_module);
if (sscf->verify) {
@@ -1693,9 +1860,8 @@ ngx_http_process_request(ngx_http_request_t *r)
}
-static ssize_t
-ngx_http_validate_host(ngx_http_request_t *r, u_char **host, size_t len,
- ngx_uint_t alloc)
+static ngx_int_t
+ngx_http_validate_host(ngx_str_t *host, ngx_pool_t *pool, ngx_uint_t alloc)
{
u_char *h, ch;
size_t i, dot_pos, host_len;
@@ -1706,21 +1872,21 @@ ngx_http_validate_host(ngx_http_request_t *r, u_char **host, size_t len,
sw_rest
} state;
- dot_pos = len;
- host_len = len;
+ dot_pos = host->len;
+ host_len = host->len;
- h = *host;
+ h = host->data;
state = sw_usual;
- for (i = 0; i < len; i++) {
+ for (i = 0; i < host->len; i++) {
ch = h[i];
switch (ch) {
case '.':
if (dot_pos == i - 1) {
- return 0;
+ return NGX_DECLINED;
}
dot_pos = i;
break;
@@ -1746,12 +1912,12 @@ ngx_http_validate_host(ngx_http_request_t *r, u_char **host, size_t len,
break;
case '\0':
- return 0;
+ return NGX_DECLINED;
default:
if (ngx_path_separator(ch)) {
- return 0;
+ return NGX_DECLINED;
}
if (ch >= 'A' && ch <= 'Z') {
@@ -1766,86 +1932,190 @@ ngx_http_validate_host(ngx_http_request_t *r, u_char **host, size_t len,
host_len--;
}
+ if (host_len == 0) {
+ return NGX_DECLINED;
+ }
+
if (alloc) {
- *host = ngx_pnalloc(r->pool, host_len);
- if (*host == NULL) {
- return -1;
+ host->data = ngx_pnalloc(pool, host_len);
+ if (host->data == NULL) {
+ return NGX_ERROR;
}
- ngx_strlow(*host, h, host_len);
+ ngx_strlow(host->data, h, host_len);
}
- return host_len;
+ host->len = host_len;
+
+ return NGX_OK;
}
static ngx_int_t
-ngx_http_find_virtual_server(ngx_http_request_t *r, u_char *host, size_t len)
+ngx_http_set_virtual_server(ngx_http_request_t *r, ngx_str_t *host)
{
+ ngx_int_t rc;
+ ngx_http_connection_t *hc;
ngx_http_core_loc_conf_t *clcf;
ngx_http_core_srv_conf_t *cscf;
- if (r->virtual_names == NULL) {
+ hc = r->http_connection;
+
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+
+ if (hc->ssl_servername) {
+ if (hc->ssl_servername->len == host->len
+ && ngx_strncmp(hc->ssl_servername->data,
+ host->data, host->len) == 0)
+ {
+#if (NGX_PCRE)
+ if (hc->ssl_servername_regex
+ && ngx_http_regex_exec(r, hc->ssl_servername_regex,
+ hc->ssl_servername) != NGX_OK)
+ {
+ ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return NGX_ERROR;
+ }
+#endif
+ return NGX_OK;
+ }
+ }
+
+#endif
+
+ rc = ngx_http_find_virtual_server(r->connection,
+ hc->addr_conf->virtual_names,
+ host, r, &cscf);
+
+ if (rc == NGX_ERROR) {
+ ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return NGX_ERROR;
+ }
+
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+
+ if (hc->ssl_servername) {
+ ngx_http_ssl_srv_conf_t *sscf;
+
+ if (rc == NGX_DECLINED) {
+ cscf = hc->addr_conf->default_server;
+ rc = NGX_OK;
+ }
+
+ sscf = ngx_http_get_module_srv_conf(cscf->ctx, ngx_http_ssl_module);
+
+ if (sscf->verify) {
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client attempted to request the server name "
+ "different from that one was negotiated");
+ ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
+ return NGX_ERROR;
+ }
+ }
+
+#endif
+
+ if (rc == NGX_DECLINED) {
+ return NGX_OK;
+ }
+
+ r->srv_conf = cscf->ctx->srv_conf;
+ r->loc_conf = cscf->ctx->loc_conf;
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ ngx_http_set_connection_log(r->connection, clcf->error_log);
+#if (NGX_ENABLE_SYSLOG)
+ r->connection->log->priority = clcf->error_log->priority;
+#endif
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_find_virtual_server(ngx_connection_t *c,
+ ngx_http_virtual_names_t *virtual_names, ngx_str_t *host,
+ ngx_http_request_t *r, ngx_http_core_srv_conf_t **cscfp)
+{
+ ngx_http_core_srv_conf_t *cscf;
+
+ if (virtual_names == NULL) {
return NGX_DECLINED;
}
- cscf = ngx_hash_find_combined(&r->virtual_names->names,
- ngx_hash_key(host, len), host, len);
+ cscf = ngx_hash_find_combined(&virtual_names->names,
+ ngx_hash_key(host->data, host->len),
+ host->data, host->len);
if (cscf) {
- goto found;
+ *cscfp = cscf;
+ return NGX_OK;
}
#if (NGX_PCRE)
- if (len && r->virtual_names->nregex) {
+ if (host->len && virtual_names->nregex) {
ngx_int_t n;
ngx_uint_t i;
- ngx_str_t name;
ngx_http_server_name_t *sn;
- name.len = len;
- name.data = host;
+ sn = virtual_names->regex;
- sn = r->virtual_names->regex;
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
- for (i = 0; i < r->virtual_names->nregex; i++) {
+ if (r == NULL) {
+ ngx_http_connection_t *hc;
- n = ngx_http_regex_exec(r, sn[i].regex, &name);
+ for (i = 0; i < virtual_names->nregex; i++) {
- if (n == NGX_OK) {
- cscf = sn[i].server;
- goto found;
- }
+ n = ngx_regex_exec(sn[i].regex->regex, host, NULL, 0);
- if (n == NGX_DECLINED) {
- continue;
+ if (n == NGX_REGEX_NO_MATCHED) {
+ continue;
+ }
+
+ if (n >= 0) {
+ hc = c->data;
+ hc->ssl_servername_regex = sn[i].regex;
+
+ *cscfp = sn[i].server;
+ return NGX_OK;
+ }
+
+ ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+ ngx_regex_exec_n " failed: %i "
+ "on \"%V\" using \"%V\"",
+ n, host, &sn[i].regex->name);
+
+ return NGX_ERROR;
}
- return NGX_ERROR;
+ return NGX_DECLINED;
}
- }
-#endif
+#endif /* SSL_CTRL_SET_TLSEXT_HOSTNAME */
- return NGX_DECLINED;
+ for (i = 0; i < virtual_names->nregex; i++) {
-found:
+ n = ngx_http_regex_exec(r, sn[i].regex, host);
- r->srv_conf = cscf->ctx->srv_conf;
- r->loc_conf = cscf->ctx->loc_conf;
+ if (n == NGX_DECLINED) {
+ continue;
+ }
- clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
- r->connection->log->file = clcf->error_log->file;
-#if (NGX_ENABLE_SYSLOG)
- r->connection->log->priority = clcf->error_log->priority;
-#endif
+ if (n == NGX_OK) {
+ *cscfp = sn[i].server;
+ return NGX_OK;
+ }
- if (!(r->connection->log->log_level & NGX_LOG_DEBUG_CONNECTION)) {
- r->connection->log->log_level = clcf->error_log->log_level;
+ return NGX_ERROR;
+ }
}
- return NGX_OK;
+#endif /* NGX_PCRE */
+
+ return NGX_DECLINED;
}
@@ -2188,6 +2458,13 @@ ngx_http_finalize_connection(ngx_http_request_t *r)
{
ngx_http_core_loc_conf_t *clcf;
+#if (NGX_HTTP_SPDY)
+ if (r->spdy_stream) {
+ ngx_http_close_request(r, 0);
+ return;
+ }
+#endif
+
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
if (r->main->count != 1) {
@@ -2242,6 +2519,12 @@ ngx_http_set_write_handler(ngx_http_request_t *r)
ngx_http_test_reading;
r->write_event_handler = ngx_http_writer;
+#if (NGX_HTTP_SPDY)
+ if (r->spdy_stream) {
+ return NGX_OK;
+ }
+#endif
+
wev = r->connection->write;
if (wev->ready && wev->delayed) {
@@ -2389,6 +2672,19 @@ ngx_http_test_reading(ngx_http_request_t *r)
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http test reading");
+#if (NGX_HTTP_SPDY)
+
+ if (r->spdy_stream) {
+ if (c->error) {
+ err = 0;
+ goto closed;
+ }
+
+ return;
+ }
+
+#endif
+
#if (NGX_HAVE_KQUEUE)
if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
@@ -2490,7 +2786,7 @@ ngx_http_set_keepalive(ngx_http_request_t *r)
/*
* If the large header buffers were allocated while the previous
* request processing then we do not use c->buffer for
- * the pipelined request (see ngx_http_init_request()).
+ * the pipelined request (see ngx_http_create_request()).
*
* Now we would move the large header buffers to the free list.
*/
@@ -2519,14 +2815,13 @@ ngx_http_set_keepalive(ngx_http_request_t *r)
}
}
+ /* guard against recursive call from ngx_http_finalize_connection() */
r->keepalive = 0;
ngx_http_free_request(r, 0);
c->data = hc;
- ngx_add_timer(rev, clcf->keepalive_timeout);
-
if (ngx_handle_read_event(rev, 0) != NGX_OK) {
ngx_http_close_connection(c);
return;
@@ -2539,32 +2834,37 @@ ngx_http_set_keepalive(ngx_http_request_t *r)
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "pipelined request");
-#if (NGX_STAT_STUB)
- (void) ngx_atomic_fetch_add(ngx_stat_reading, 1);
-#endif
-
- hc->pipeline = 1;
c->log->action = "reading client pipelined request line";
- rev->handler = ngx_http_init_request;
+ r = ngx_http_create_request(c);
+ if (r == NULL) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ r->pipeline = 1;
+
+ c->data = r;
+
+ c->sent = 0;
+ c->destroyed = 0;
+
+ if (rev->timer_set) {
+ ngx_del_timer(rev);
+ }
+
+ rev->handler = ngx_http_process_request_line;
ngx_post_event(rev, &ngx_posted_events);
return;
}
- hc->pipeline = 0;
-
/*
- * To keep a memory footprint as small as possible for an idle
- * keepalive connection we try to free the ngx_http_request_t and
- * c->buffer's memory if they were allocated outside the c->pool.
- * The large header buffers are always allocated outside the c->pool and
- * are freed too.
+ * To keep a memory footprint as small as possible for an idle keepalive
+ * connection we try to free c->buffer's memory if it was allocated outside
+ * the c->pool. The large header buffers are always allocated outside the
+ * c->pool and are freed too.
*/
- if (ngx_pfree(c->pool, r) == NGX_OK) {
- hc->request = NULL;
- }
-
b = c->buffer;
if (ngx_pfree(c->pool, b->start) == NGX_OK) {
@@ -2670,6 +2970,8 @@ ngx_http_set_keepalive(ngx_http_request_t *r)
c->idle = 1;
ngx_reusable_connection(c, 1);
+ ngx_add_timer(rev, clcf->keepalive_timeout);
+
if (rev->ready) {
ngx_post_event(rev, &ngx_posted_events);
}
@@ -2785,17 +3087,25 @@ ngx_http_keepalive_handler(ngx_event_t *rev)
b->last += n;
-#if (NGX_STAT_STUB)
- (void) ngx_atomic_fetch_add(ngx_stat_reading, 1);
-#endif
-
c->log->handler = ngx_http_log_error;
c->log->action = "reading client request line";
c->idle = 0;
ngx_reusable_connection(c, 0);
- ngx_http_init_request(rev);
+ c->data = ngx_http_create_request(c);
+ if (c->data == NULL) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ c->sent = 0;
+ c->destroyed = 0;
+
+ ngx_del_timer(rev);
+
+ rev->handler = ngx_http_process_request_line;
+ ngx_http_process_request_line(rev);
}
@@ -3010,15 +3320,23 @@ ngx_http_close_request(ngx_http_request_t *r, ngx_int_t rc)
return;
}
+#if (NGX_HTTP_SPDY)
+ if (r->spdy_stream) {
+ ngx_http_spdy_close_stream(r->spdy_stream, rc);
+ return;
+ }
+#endif
+
ngx_http_free_request(r, rc);
ngx_http_close_connection(c);
}
-static void
+void
ngx_http_free_request(ngx_http_request_t *r, ngx_int_t rc)
{
ngx_log_t *log;
+ ngx_pool_t *pool;
struct linger linger;
ngx_http_cleanup_t *cln;
ngx_http_log_ctx_t *ctx;
@@ -3085,7 +3403,15 @@ ngx_http_free_request(ngx_http_request_t *r, ngx_int_t rc)
r->connection->destroyed = 1;
- ngx_destroy_pool(r->pool);
+ /*
+ * Setting r->pool to NULL will increase probability to catch double close
+ * of request since the request object is allocated from its own pool.
+ */
+
+ pool = r->pool;
+ r->pool = NULL;
+
+ ngx_destroy_pool(pool);
}
@@ -3107,7 +3433,7 @@ ngx_http_log_request(ngx_http_request_t *r)
}
-static void
+void
ngx_http_close_connection(ngx_connection_t *c)
{
ngx_pool_t *pool;
diff --git a/usr.sbin/nginx/src/http/ngx_http_request.h b/usr.sbin/nginx/src/http/ngx_http_request.h
index c2651a86fd1..5c62785e2a8 100644
--- a/usr.sbin/nginx/src/http/ngx_http_request.h
+++ b/usr.sbin/nginx/src/http/ngx_http_request.h
@@ -64,6 +64,10 @@
#define NGX_HTTP_LOG_UNSAFE 8
+#define NGX_HTTP_CONTINUE 100
+#define NGX_HTTP_SWITCHING_PROTOCOLS 101
+#define NGX_HTTP_PROCESSING 102
+
#define NGX_HTTP_OK 200
#define NGX_HTTP_CREATED 201
#define NGX_HTTP_ACCEPTED 202
@@ -137,13 +141,6 @@
#define NGX_HTTP_COPY_BUFFERED 0x04
-#if (NGX_HTTP_PROXY || NGX_HTTP_REALIP || NGX_HTTP_GEO)
-#ifndef NGX_HTTP_X_FORWARDED_FOR
-#define NGX_HTTP_X_FORWARDED_FOR 1
-#endif
-#endif
-
-
typedef enum {
NGX_HTTP_INITING_REQUEST_STATE = 0,
NGX_HTTP_READING_REQUEST_STATE,
@@ -179,6 +176,8 @@ typedef struct {
ngx_table_elt_t *connection;
ngx_table_elt_t *if_modified_since;
ngx_table_elt_t *if_unmodified_since;
+ ngx_table_elt_t *if_match;
+ ngx_table_elt_t *if_none_match;
ngx_table_elt_t *user_agent;
ngx_table_elt_t *referer;
ngx_table_elt_t *content_length;
@@ -189,6 +188,7 @@ typedef struct {
ngx_table_elt_t *transfer_encoding;
ngx_table_elt_t *expect;
+ ngx_table_elt_t *upgrade;
#if (NGX_HTTP_GZIP)
ngx_table_elt_t *accept_encoding;
@@ -200,7 +200,7 @@ typedef struct {
ngx_table_elt_t *keep_alive;
#if (NGX_HTTP_X_FORWARDED_FOR)
- ngx_table_elt_t *x_forwarded_for;
+ ngx_array_t x_forwarded_for;
#endif
#if (NGX_HTTP_REALIP)
@@ -229,6 +229,7 @@ typedef struct {
time_t keep_alive_n;
unsigned connection_type:2;
+ unsigned chunked:1;
unsigned msie:1;
unsigned msie6:1;
unsigned opera:1;
@@ -281,13 +282,25 @@ typedef struct {
ngx_chain_t *bufs;
ngx_buf_t *buf;
off_t rest;
- ngx_chain_t *to_write;
+ ngx_chain_t *free;
+ ngx_chain_t *busy;
+ ngx_http_chunked_t *chunked;
ngx_http_client_body_handler_pt post_handler;
} ngx_http_request_body_t;
+typedef struct ngx_http_addr_conf_s ngx_http_addr_conf_t;
+
typedef struct {
- ngx_http_request_t *request;
+ ngx_http_addr_conf_t *addr_conf;
+ ngx_http_conf_ctx_t *conf_ctx;
+
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+ ngx_str_t *ssl_servername;
+#if (NGX_PCRE)
+ ngx_http_regex_t *ssl_servername_regex;
+#endif
+#endif
ngx_buf_t **busy;
ngx_int_t nbusy;
@@ -295,21 +308,12 @@ typedef struct {
ngx_buf_t **free;
ngx_int_t nfree;
- ngx_uint_t pipeline; /* unsigned pipeline:1; */
+#if (NGX_HTTP_SSL)
+ ngx_uint_t ssl; /* unsigned ssl:1; */
+#endif
} ngx_http_connection_t;
-typedef struct ngx_http_server_name_s ngx_http_server_name_t;
-
-
-typedef struct {
- ngx_hash_combined_t names;
-
- ngx_uint_t nregex;
- ngx_http_server_name_t *regex;
-} ngx_http_virtual_names_t;
-
-
typedef void (*ngx_http_cleanup_pt)(void *data);
typedef struct ngx_http_cleanup_s ngx_http_cleanup_t;
@@ -403,8 +407,6 @@ struct ngx_http_request_s {
ngx_http_post_subrequest_t *post_subrequest;
ngx_http_posted_request_t *posted_requests;
- ngx_http_virtual_names_t *virtual_names;
-
ngx_int_t phase_handler;
ngx_http_handler_pt content_handler;
ngx_uint_t access_code;
@@ -427,6 +429,9 @@ struct ngx_http_request_s {
ngx_uint_t err_status;
ngx_http_connection_t *http_connection;
+#if (NGX_HTTP_SPDY)
+ ngx_http_spdy_stream_t *spdy_stream;
+#endif
ngx_http_log_handler_pt log_handler;
@@ -497,7 +502,6 @@ struct ngx_http_request_s {
#endif
unsigned pipeline:1;
- unsigned plain_http:1;
unsigned chunked:1;
unsigned header_only:1;
unsigned keepalive:1;
@@ -577,4 +581,12 @@ extern ngx_http_header_t ngx_http_headers_in[];
extern ngx_http_header_out_t ngx_http_headers_out[];
+#define ngx_http_set_connection_log(c, l) \
+ \
+ c->log->file = l->file; \
+ if (!(c->log->log_level & NGX_LOG_DEBUG_CONNECTION)) { \
+ c->log->log_level = l->log_level; \
+ }
+
+
#endif /* _NGX_HTTP_REQUEST_H_INCLUDED_ */
diff --git a/usr.sbin/nginx/src/http/ngx_http_request_body.c b/usr.sbin/nginx/src/http/ngx_http_request_body.c
index 3c69d052c7a..2c612311d0a 100644
--- a/usr.sbin/nginx/src/http/ngx_http_request_body.c
+++ b/usr.sbin/nginx/src/http/ngx_http_request_body.c
@@ -12,18 +12,21 @@
static void ngx_http_read_client_request_body_handler(ngx_http_request_t *r);
static ngx_int_t ngx_http_do_read_client_request_body(ngx_http_request_t *r);
-static ngx_int_t ngx_http_write_request_body(ngx_http_request_t *r,
- ngx_chain_t *body);
+static ngx_int_t ngx_http_write_request_body(ngx_http_request_t *r);
static ngx_int_t ngx_http_read_discarded_request_body(ngx_http_request_t *r);
+static ngx_int_t ngx_http_discard_request_body_filter(ngx_http_request_t *r,
+ ngx_buf_t *b);
static ngx_int_t ngx_http_test_expect(ngx_http_request_t *r);
+static ngx_int_t ngx_http_request_body_filter(ngx_http_request_t *r,
+ ngx_chain_t *in);
+static ngx_int_t ngx_http_request_body_length_filter(ngx_http_request_t *r,
+ ngx_chain_t *in);
+static ngx_int_t ngx_http_request_body_chunked_filter(ngx_http_request_t *r,
+ ngx_chain_t *in);
+static ngx_int_t ngx_http_request_body_save_filter(ngx_http_request_t *r,
+ ngx_chain_t *in);
-/*
- * on completion ngx_http_read_client_request_body() adds to
- * r->request_body->bufs one or two bufs:
- * *) one memory buf that was preread in r->header_in;
- * *) one memory or file buf that contains the rest of the body
- */
ngx_int_t
ngx_http_read_client_request_body(ngx_http_request_t *r,
@@ -33,13 +36,20 @@ ngx_http_read_client_request_body(ngx_http_request_t *r,
ssize_t size;
ngx_int_t rc;
ngx_buf_t *b;
- ngx_chain_t *cl, **next;
+ ngx_chain_t out, *cl;
ngx_http_request_body_t *rb;
ngx_http_core_loc_conf_t *clcf;
r->main->count++;
- if (r->request_body || r->discard_body) {
+#if (NGX_HTTP_SPDY)
+ if (r->spdy_stream) {
+ rc = ngx_http_spdy_read_request_body(r, post_handler);
+ goto done;
+ }
+#endif
+
+ if (r != r->main || r->request_body || r->discard_body) {
post_handler(r);
return NGX_OK;
}
@@ -55,39 +65,26 @@ ngx_http_read_client_request_body(ngx_http_request_t *r,
goto done;
}
- r->request_body = rb;
-
- if (r->headers_in.content_length_n < 0) {
- post_handler(r);
- return NGX_OK;
- }
-
- clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
-
- if (r->headers_in.content_length_n == 0) {
-
- if (r->request_body_in_file_only) {
- if (ngx_http_write_request_body(r, NULL) != NGX_OK) {
- rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
- goto done;
- }
- }
-
- post_handler(r);
-
- return NGX_OK;
- }
-
- rb->post_handler = post_handler;
-
/*
* set by ngx_pcalloc():
*
* rb->bufs = NULL;
* rb->buf = NULL;
- * rb->rest = 0;
+ * rb->free = NULL;
+ * rb->busy = NULL;
+ * rb->chunked = NULL;
*/
+ rb->rest = -1;
+ rb->post_handler = post_handler;
+
+ r->request_body = rb;
+
+ if (r->headers_in.content_length_n < 0 && !r->headers_in.chunked) {
+ post_handler(r);
+ return NGX_OK;
+ }
+
preread = r->header_in->last - r->header_in->pos;
if (preread) {
@@ -97,83 +94,98 @@ ngx_http_read_client_request_body(ngx_http_request_t *r,
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http client request body preread %uz", preread);
- b = ngx_calloc_buf(r->pool);
- if (b == NULL) {
- rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
- goto done;
- }
+ out.buf = r->header_in;
+ out.next = NULL;
- b->temporary = 1;
- b->start = r->header_in->pos;
- b->pos = r->header_in->pos;
- b->last = r->header_in->last;
- b->end = r->header_in->end;
+ rc = ngx_http_request_body_filter(r, &out);
- rb->bufs = ngx_alloc_chain_link(r->pool);
- if (rb->bufs == NULL) {
- rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
+ if (rc != NGX_OK) {
goto done;
}
- rb->bufs->buf = b;
- rb->bufs->next = NULL;
-
- rb->buf = b;
+ r->request_length += preread - (r->header_in->last - r->header_in->pos);
- if ((off_t) preread >= r->headers_in.content_length_n) {
+ if (!r->headers_in.chunked
+ && rb->rest > 0
+ && rb->rest <= (off_t) (r->header_in->end - r->header_in->last))
+ {
+ /* the whole request body may be placed in r->header_in */
- /* the whole request body was pre-read */
+ b = ngx_calloc_buf(r->pool);
+ if (b == NULL) {
+ rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
+ goto done;
+ }
- r->header_in->pos += (size_t) r->headers_in.content_length_n;
- r->request_length += r->headers_in.content_length_n;
- b->last = r->header_in->pos;
+ b->temporary = 1;
+ b->start = r->header_in->pos;
+ b->pos = r->header_in->pos;
+ b->last = r->header_in->last;
+ b->end = r->header_in->end;
- if (r->request_body_in_file_only) {
- if (ngx_http_write_request_body(r, rb->bufs) != NGX_OK) {
- rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
- goto done;
- }
- }
+ rb->buf = b;
- post_handler(r);
+ r->read_event_handler = ngx_http_read_client_request_body_handler;
+ r->write_event_handler = ngx_http_request_empty_handler;
- return NGX_OK;
+ rc = ngx_http_do_read_client_request_body(r);
+ goto done;
}
- /*
- * to not consider the body as pipelined request in
- * ngx_http_set_keepalive()
- */
- r->header_in->pos = r->header_in->last;
+ } else {
+ /* set rb->rest */
- r->request_length += preread;
+ if (ngx_http_request_body_filter(r, NULL) != NGX_OK) {
+ rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
+ goto done;
+ }
+ }
- rb->rest = r->headers_in.content_length_n - preread;
+ if (rb->rest == 0) {
+ /* the whole request body was pre-read */
- if (rb->rest <= (off_t) (b->end - b->last)) {
+ if (r->request_body_in_file_only) {
+ if (ngx_http_write_request_body(r) != NGX_OK) {
+ rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
+ goto done;
+ }
- /* the whole request body may be placed in r->header_in */
+ cl = ngx_chain_get_free_buf(r->pool, &rb->free);
+ if (cl == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
- rb->to_write = rb->bufs;
+ b = cl->buf;
- r->read_event_handler = ngx_http_read_client_request_body_handler;
+ ngx_memzero(b, sizeof(ngx_buf_t));
- rc = ngx_http_do_read_client_request_body(r);
- goto done;
+ b->in_file = 1;
+ b->file_last = rb->temp_file->file.offset;
+ b->file = &rb->temp_file->file;
+
+ rb->bufs = cl;
}
- next = &rb->bufs->next;
+ post_handler(r);
- } else {
- b = NULL;
- rb->rest = r->headers_in.content_length_n;
- next = &rb->bufs;
+ return NGX_OK;
}
+ if (rb->rest < 0) {
+ ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+ "negative request body rest");
+ rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
+ goto done;
+ }
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
size = clcf->client_body_buffer_size;
size += size >> 2;
- if (rb->rest < size) {
+ /* TODO: honor r->request_body_in_single_buf */
+
+ if (!r->headers_in.chunked && rb->rest < size) {
size = (ssize_t) rb->rest;
if (r->request_body_in_single_buf) {
@@ -182,9 +194,6 @@ ngx_http_read_client_request_body(ngx_http_request_t *r,
} else {
size = clcf->client_body_buffer_size;
-
- /* disable copying buffer for r->request_body_in_single_buf */
- b = NULL;
}
rb->buf = ngx_create_temp_buf(r->pool, size);
@@ -193,33 +202,8 @@ ngx_http_read_client_request_body(ngx_http_request_t *r,
goto done;
}
- cl = ngx_alloc_chain_link(r->pool);
- if (cl == NULL) {
- rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
- goto done;
- }
-
- cl->buf = rb->buf;
- cl->next = NULL;
-
- if (b && r->request_body_in_single_buf) {
- size = b->last - b->pos;
- ngx_memcpy(rb->buf->pos, b->pos, size);
- rb->buf->last += size;
-
- next = &rb->bufs;
- }
-
- *next = cl;
-
- if (r->request_body_in_file_only || r->request_body_in_single_buf) {
- rb->to_write = rb->bufs;
-
- } else {
- rb->to_write = rb->bufs->next ? rb->bufs->next : rb->bufs;
- }
-
r->read_event_handler = ngx_http_read_client_request_body_handler;
+ r->write_event_handler = ngx_http_request_empty_handler;
rc = ngx_http_do_read_client_request_body(r);
@@ -255,9 +239,12 @@ ngx_http_read_client_request_body_handler(ngx_http_request_t *r)
static ngx_int_t
ngx_http_do_read_client_request_body(ngx_http_request_t *r)
{
+ off_t rest;
size_t size;
ssize_t n;
+ ngx_int_t rc;
ngx_buf_t *b;
+ ngx_chain_t *cl, out;
ngx_connection_t *c;
ngx_http_request_body_t *rb;
ngx_http_core_loc_conf_t *clcf;
@@ -272,18 +259,44 @@ ngx_http_do_read_client_request_body(ngx_http_request_t *r)
for ( ;; ) {
if (rb->buf->last == rb->buf->end) {
- if (ngx_http_write_request_body(r, rb->to_write) != NGX_OK) {
+ /* pass buffer to request body filter chain */
+
+ out.buf = rb->buf;
+ out.next = NULL;
+
+ rc = ngx_http_request_body_filter(r, &out);
+
+ if (rc != NGX_OK) {
+ return rc;
+ }
+
+ /* write to file */
+
+ if (ngx_http_write_request_body(r) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
- rb->to_write = rb->bufs->next ? rb->bufs->next : rb->bufs;
+ /* update chains */
+
+ rc = ngx_http_request_body_filter(r, NULL);
+
+ if (rc != NGX_OK) {
+ return rc;
+ }
+
+ if (rb->busy != NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ rb->buf->pos = rb->buf->start;
rb->buf->last = rb->buf->start;
}
size = rb->buf->end - rb->buf->last;
+ rest = rb->rest - (rb->buf->last - rb->buf->pos);
- if ((off_t) size > rb->rest) {
- size = (size_t) rb->rest;
+ if ((off_t) size > rest) {
+ size = (size_t) rest;
}
n = c->recv(c, rb->buf->last, size);
@@ -306,9 +319,21 @@ ngx_http_do_read_client_request_body(ngx_http_request_t *r)
}
rb->buf->last += n;
- rb->rest -= n;
r->request_length += n;
+ if (n == rest) {
+ /* pass buffer to request body filter chain */
+
+ out.buf = rb->buf;
+ out.next = NULL;
+
+ rc = ngx_http_request_body_filter(r, &out);
+
+ if (rc != NGX_OK) {
+ return rc;
+ }
+ }
+
if (rb->rest == 0) {
break;
}
@@ -345,32 +370,24 @@ ngx_http_do_read_client_request_body(ngx_http_request_t *r)
/* save the last part */
- if (ngx_http_write_request_body(r, rb->to_write) != NGX_OK) {
+ if (ngx_http_write_request_body(r) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
- b = ngx_calloc_buf(r->pool);
- if (b == NULL) {
+ cl = ngx_chain_get_free_buf(r->pool, &rb->free);
+ if (cl == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
+ b = cl->buf;
+
+ ngx_memzero(b, sizeof(ngx_buf_t));
+
b->in_file = 1;
- b->file_pos = 0;
b->file_last = rb->temp_file->file.offset;
b->file = &rb->temp_file->file;
- if (rb->bufs->next) {
- rb->bufs->next->buf = b;
-
- } else {
- rb->bufs->buf = b;
- }
- }
-
- if (rb->bufs->next
- && (r->request_body_in_file_only || r->request_body_in_single_buf))
- {
- rb->bufs = rb->bufs->next;
+ rb->bufs = cl;
}
r->read_event_handler = ngx_http_block_reading;
@@ -382,15 +399,19 @@ ngx_http_do_read_client_request_body(ngx_http_request_t *r)
static ngx_int_t
-ngx_http_write_request_body(ngx_http_request_t *r, ngx_chain_t *body)
+ngx_http_write_request_body(ngx_http_request_t *r)
{
ssize_t n;
+ ngx_chain_t *cl;
ngx_temp_file_t *tf;
ngx_http_request_body_t *rb;
ngx_http_core_loc_conf_t *clcf;
rb = r->request_body;
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http write client request body, bufs %p", rb->bufs);
+
if (rb->temp_file == NULL) {
tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t));
if (tf == NULL) {
@@ -414,7 +435,7 @@ ngx_http_write_request_body(ngx_http_request_t *r, ngx_chain_t *body)
rb->temp_file = tf;
- if (body == NULL) {
+ if (rb->bufs == NULL) {
/* empty body with r->request_body_in_file_only */
if (ngx_create_temp_file(&tf->file, tf->path, tf->pool,
@@ -428,7 +449,11 @@ ngx_http_write_request_body(ngx_http_request_t *r, ngx_chain_t *body)
}
}
- n = ngx_write_chain_to_temp_file(rb->temp_file, body);
+ if (rb->bufs == NULL) {
+ return NGX_OK;
+ }
+
+ n = ngx_write_chain_to_temp_file(rb->temp_file, rb->bufs);
/* TODO: n == 0 or not complete and level event */
@@ -438,6 +463,14 @@ ngx_http_write_request_body(ngx_http_request_t *r, ngx_chain_t *body)
rb->temp_file->offset += n;
+ /* mark all buffers as written */
+
+ for (cl = rb->bufs; cl; cl = cl->next) {
+ cl->buf->pos = cl->buf->last;
+ }
+
+ rb->bufs = NULL;
+
return NGX_OK;
}
@@ -446,9 +479,17 @@ ngx_int_t
ngx_http_discard_request_body(ngx_http_request_t *r)
{
ssize_t size;
+ ngx_int_t rc;
ngx_event_t *rev;
- if (r != r->main || r->discard_body) {
+#if (NGX_HTTP_SPDY)
+ if (r->spdy_stream && r == r->main) {
+ r->spdy_stream->skip_data = NGX_SPDY_DATA_DISCARD;
+ return NGX_OK;
+ }
+#endif
+
+ if (r != r->main || r->discard_body || r->request_body) {
return NGX_OK;
}
@@ -464,30 +505,36 @@ ngx_http_discard_request_body(ngx_http_request_t *r)
ngx_del_timer(rev);
}
- if (r->headers_in.content_length_n <= 0 || r->request_body) {
+ if (r->headers_in.content_length_n <= 0 && !r->headers_in.chunked) {
return NGX_OK;
}
size = r->header_in->last - r->header_in->pos;
- if (size) {
- if (r->headers_in.content_length_n > size) {
- r->header_in->pos += size;
- r->headers_in.content_length_n -= size;
+ if (size || r->headers_in.chunked) {
+ rc = ngx_http_discard_request_body_filter(r, r->header_in);
- } else {
- r->header_in->pos += (size_t) r->headers_in.content_length_n;
- r->headers_in.content_length_n = 0;
+ if (rc != NGX_OK) {
+ return rc;
+ }
+
+ if (r->headers_in.content_length_n == 0) {
return NGX_OK;
}
}
- if (ngx_http_read_discarded_request_body(r) == NGX_OK) {
+ rc = ngx_http_read_discarded_request_body(r);
+
+ if (rc == NGX_OK) {
r->lingering_close = 0;
return NGX_OK;
}
- /* == NGX_AGAIN */
+ if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
+ return rc;
+ }
+
+ /* rc == NGX_AGAIN */
r->read_event_handler = ngx_http_discarded_request_body_handler;
@@ -544,6 +591,12 @@ ngx_http_discarded_request_body_handler(ngx_http_request_t *r)
return;
}
+ if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
+ c->error = 1;
+ ngx_http_finalize_request(r, NGX_ERROR);
+ return;
+ }
+
/* rc == NGX_AGAIN */
if (ngx_handle_read_event(rev, 0) != NGX_OK) {
@@ -570,13 +623,19 @@ ngx_http_discarded_request_body_handler(ngx_http_request_t *r)
static ngx_int_t
ngx_http_read_discarded_request_body(ngx_http_request_t *r)
{
- size_t size;
- ssize_t n;
- u_char buffer[NGX_HTTP_DISCARD_BUFFER_SIZE];
+ size_t size;
+ ssize_t n;
+ ngx_int_t rc;
+ ngx_buf_t b;
+ u_char buffer[NGX_HTTP_DISCARD_BUFFER_SIZE];
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http read discarded body");
+ ngx_memzero(&b, sizeof(ngx_buf_t));
+
+ b.temporary = 1;
+
for ( ;; ) {
if (r->headers_in.content_length_n == 0) {
r->read_event_handler = ngx_http_block_reading;
@@ -587,9 +646,8 @@ ngx_http_read_discarded_request_body(ngx_http_request_t *r)
return NGX_AGAIN;
}
- size = (r->headers_in.content_length_n > NGX_HTTP_DISCARD_BUFFER_SIZE) ?
- NGX_HTTP_DISCARD_BUFFER_SIZE:
- (size_t) r->headers_in.content_length_n;
+ size = (size_t) ngx_min(r->headers_in.content_length_n,
+ NGX_HTTP_DISCARD_BUFFER_SIZE);
n = r->connection->recv(r->connection, buffer, size);
@@ -606,8 +664,104 @@ ngx_http_read_discarded_request_body(ngx_http_request_t *r)
return NGX_OK;
}
- r->headers_in.content_length_n -= n;
+ b.pos = buffer;
+ b.last = buffer + n;
+
+ rc = ngx_http_discard_request_body_filter(r, &b);
+
+ if (rc != NGX_OK) {
+ return rc;
+ }
+ }
+}
+
+
+static ngx_int_t
+ngx_http_discard_request_body_filter(ngx_http_request_t *r, ngx_buf_t *b)
+{
+ size_t size;
+ ngx_int_t rc;
+ ngx_http_request_body_t *rb;
+
+ if (r->headers_in.chunked) {
+
+ rb = r->request_body;
+
+ if (rb == NULL) {
+
+ rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));
+ if (rb == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ rb->chunked = ngx_pcalloc(r->pool, sizeof(ngx_http_chunked_t));
+ if (rb->chunked == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ r->request_body = rb;
+ }
+
+ for ( ;; ) {
+
+ rc = ngx_http_parse_chunked(r, b, rb->chunked);
+
+ if (rc == NGX_OK) {
+
+ /* a chunk has been parsed successfully */
+
+ size = b->last - b->pos;
+
+ if ((off_t) size > rb->chunked->size) {
+ b->pos += rb->chunked->size;
+ rb->chunked->size = 0;
+
+ } else {
+ rb->chunked->size -= size;
+ b->pos = b->last;
+ }
+
+ continue;
+ }
+
+ if (rc == NGX_DONE) {
+
+ /* a whole response has been parsed successfully */
+
+ r->headers_in.content_length_n = 0;
+ break;
+ }
+
+ if (rc == NGX_AGAIN) {
+
+ /* set amount of data we want to see next time */
+
+ r->headers_in.content_length_n = rb->chunked->length;
+ break;
+ }
+
+ /* invalid */
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "client sent invalid chunked body");
+
+ return NGX_HTTP_BAD_REQUEST;
+ }
+
+ } else {
+ size = b->last - b->pos;
+
+ if ((off_t) size > r->headers_in.content_length_n) {
+ b->pos += r->headers_in.content_length_n;
+ r->headers_in.content_length_n = 0;
+
+ } else {
+ b->pos = b->last;
+ r->headers_in.content_length_n -= size;
+ }
}
+
+ return NGX_OK;
}
@@ -651,3 +805,278 @@ ngx_http_test_expect(ngx_http_request_t *r)
return NGX_ERROR;
}
+
+
+static ngx_int_t
+ngx_http_request_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
+{
+ if (r->headers_in.chunked) {
+ return ngx_http_request_body_chunked_filter(r, in);
+
+ } else {
+ return ngx_http_request_body_length_filter(r, in);
+ }
+}
+
+
+static ngx_int_t
+ngx_http_request_body_length_filter(ngx_http_request_t *r, ngx_chain_t *in)
+{
+ size_t size;
+ ngx_int_t rc;
+ ngx_buf_t *b;
+ ngx_chain_t *cl, *tl, *out, **ll;
+ ngx_http_request_body_t *rb;
+
+ rb = r->request_body;
+
+ if (rb->rest == -1) {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http request body content length filter");
+
+ rb->rest = r->headers_in.content_length_n;
+ }
+
+ out = NULL;
+ ll = &out;
+
+ for (cl = in; cl; cl = cl->next) {
+
+ tl = ngx_chain_get_free_buf(r->pool, &rb->free);
+ if (tl == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ b = tl->buf;
+
+ ngx_memzero(b, sizeof(ngx_buf_t));
+
+ b->temporary = 1;
+ b->tag = (ngx_buf_tag_t) &ngx_http_read_client_request_body;
+ b->start = cl->buf->pos;
+ b->pos = cl->buf->pos;
+ b->last = cl->buf->last;
+ b->end = cl->buf->end;
+
+ size = cl->buf->last - cl->buf->pos;
+
+ if ((off_t) size < rb->rest) {
+ cl->buf->pos = cl->buf->last;
+ rb->rest -= size;
+
+ } else {
+ cl->buf->pos += rb->rest;
+ rb->rest = 0;
+ b->last = cl->buf->pos;
+ b->last_buf = 1;
+ }
+
+ *ll = tl;
+ ll = &tl->next;
+ }
+
+ rc = ngx_http_request_body_save_filter(r, out);
+
+ ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &out,
+ (ngx_buf_tag_t) &ngx_http_read_client_request_body);
+
+ return rc;
+}
+
+
+static ngx_int_t
+ngx_http_request_body_chunked_filter(ngx_http_request_t *r, ngx_chain_t *in)
+{
+ size_t size;
+ ngx_int_t rc;
+ ngx_buf_t *b;
+ ngx_chain_t *cl, *out, *tl, **ll;
+ ngx_http_request_body_t *rb;
+ ngx_http_core_loc_conf_t *clcf;
+
+ rb = r->request_body;
+
+ if (rb->rest == -1) {
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http request body chunked filter");
+
+ rb->chunked = ngx_pcalloc(r->pool, sizeof(ngx_http_chunked_t));
+ if (rb->chunked == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ r->headers_in.content_length_n = 0;
+ rb->rest = 3;
+ }
+
+ out = NULL;
+ ll = &out;
+
+ for (cl = in; cl; cl = cl->next) {
+
+ for ( ;; ) {
+
+ ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
+ "http body chunked buf "
+ "t:%d f:%d %p, pos %p, size: %z file: %O, size: %z",
+ cl->buf->temporary, cl->buf->in_file,
+ cl->buf->start, cl->buf->pos,
+ cl->buf->last - cl->buf->pos,
+ cl->buf->file_pos,
+ cl->buf->file_last - cl->buf->file_pos);
+
+ rc = ngx_http_parse_chunked(r, cl->buf, rb->chunked);
+
+ if (rc == NGX_OK) {
+
+ /* a chunk has been parsed successfully */
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (clcf->client_max_body_size
+ && clcf->client_max_body_size
+ < r->headers_in.content_length_n + rb->chunked->size)
+ {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "client intended to send too large chunked "
+ "body: %O bytes",
+ r->headers_in.content_length_n
+ + rb->chunked->size);
+
+ r->lingering_close = 1;
+
+ return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE;
+ }
+
+ tl = ngx_chain_get_free_buf(r->pool, &rb->free);
+ if (tl == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ b = tl->buf;
+
+ ngx_memzero(b, sizeof(ngx_buf_t));
+
+ b->temporary = 1;
+ b->tag = (ngx_buf_tag_t) &ngx_http_read_client_request_body;
+ b->start = cl->buf->pos;
+ b->pos = cl->buf->pos;
+ b->last = cl->buf->last;
+ b->end = cl->buf->end;
+
+ *ll = tl;
+ ll = &tl->next;
+
+ size = cl->buf->last - cl->buf->pos;
+
+ if ((off_t) size > rb->chunked->size) {
+ cl->buf->pos += rb->chunked->size;
+ r->headers_in.content_length_n += rb->chunked->size;
+ rb->chunked->size = 0;
+
+ } else {
+ rb->chunked->size -= size;
+ r->headers_in.content_length_n += size;
+ cl->buf->pos = cl->buf->last;
+ }
+
+ b->last = cl->buf->pos;
+
+ continue;
+ }
+
+ if (rc == NGX_DONE) {
+
+ /* a whole response has been parsed successfully */
+
+ rb->rest = 0;
+
+ tl = ngx_chain_get_free_buf(r->pool, &rb->free);
+ if (tl == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ b = tl->buf;
+
+ ngx_memzero(b, sizeof(ngx_buf_t));
+
+ b->last_buf = 1;
+
+ *ll = tl;
+ ll = &tl->next;
+
+ break;
+ }
+
+ if (rc == NGX_AGAIN) {
+
+ /* set rb->rest, amount of data we want to see next time */
+
+ rb->rest = rb->chunked->length;
+
+ break;
+ }
+
+ /* invalid */
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "client sent invalid chunked body");
+
+ return NGX_HTTP_BAD_REQUEST;
+ }
+ }
+
+ rc = ngx_http_request_body_save_filter(r, out);
+
+ ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &out,
+ (ngx_buf_tag_t) &ngx_http_read_client_request_body);
+
+ return rc;
+}
+
+
+static ngx_int_t
+ngx_http_request_body_save_filter(ngx_http_request_t *r, ngx_chain_t *in)
+{
+#if (NGX_DEBUG)
+ ngx_chain_t *cl;
+#endif
+ ngx_http_request_body_t *rb;
+
+ rb = r->request_body;
+
+#if (NGX_DEBUG)
+
+ for (cl = rb->bufs; cl; cl = cl->next) {
+ ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
+ "http body old buf t:%d f:%d %p, pos %p, size: %z "
+ "file: %O, size: %z",
+ cl->buf->temporary, cl->buf->in_file,
+ cl->buf->start, cl->buf->pos,
+ cl->buf->last - cl->buf->pos,
+ cl->buf->file_pos,
+ cl->buf->file_last - cl->buf->file_pos);
+ }
+
+ for (cl = in; cl; cl = cl->next) {
+ ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
+ "http body new buf t:%d f:%d %p, pos %p, size: %z "
+ "file: %O, size: %z",
+ cl->buf->temporary, cl->buf->in_file,
+ cl->buf->start, cl->buf->pos,
+ cl->buf->last - cl->buf->pos,
+ cl->buf->file_pos,
+ cl->buf->file_last - cl->buf->file_pos);
+ }
+
+#endif
+
+ /* TODO: coalesce neighbouring buffers */
+
+ if (ngx_chain_add_copy(r->pool, &rb->bufs, in) != NGX_OK) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ return NGX_OK;
+}
diff --git a/usr.sbin/nginx/src/http/ngx_http_spdy.c b/usr.sbin/nginx/src/http/ngx_http_spdy.c
new file mode 100644
index 00000000000..3febc23e972
--- /dev/null
+++ b/usr.sbin/nginx/src/http/ngx_http_spdy.c
@@ -0,0 +1,2882 @@
+
+/*
+ * Copyright (C) Nginx, Inc.
+ * Copyright (C) Valentin V. Bartenev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+#include <ngx_http_spdy_module.h>
+
+#include <zlib.h>
+
+
+#if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)
+
+#define ngx_str5cmp(m, c0, c1, c2, c3, c4) \
+ *(uint32_t *) m == (c3 << 24 | c2 << 16 | c1 << 8 | c0) \
+ && m[4] == c4
+
+#else
+
+#define ngx_str5cmp(m, c0, c1, c2, c3, c4) \
+ m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 && m[4] == c4
+
+#endif
+
+
+#if (NGX_HAVE_NONALIGNED)
+
+#define ngx_spdy_frame_parse_uint16(p) ntohs(*(uint16_t *) (p))
+#define ngx_spdy_frame_parse_uint32(p) ntohl(*(uint32_t *) (p))
+
+#else
+
+#define ngx_spdy_frame_parse_uint16(p) ((p)[0] << 8 | (p)[1])
+#define ngx_spdy_frame_parse_uint32(p) \
+ ((p)[0] << 24 | (p)[1] << 16 | (p)[2] << 8 | (p)[3])
+
+#endif
+
+#define ngx_spdy_frame_parse_sid(p) \
+ (ngx_spdy_frame_parse_uint32(p) & 0x7fffffff)
+
+
+#define ngx_spdy_ctl_frame_check(h) \
+ (((h) & 0xffffff00) == ngx_spdy_ctl_frame_head(0))
+#define ngx_spdy_data_frame_check(h) \
+ (!((h) & (uint32_t) NGX_SPDY_CTL_BIT << 31))
+
+#define ngx_spdy_ctl_frame_type(h) ((h) & 0x000000ff)
+#define ngx_spdy_frame_flags(p) ((p) >> 24)
+#define ngx_spdy_frame_length(p) ((p) & 0x00ffffff)
+
+
+#define NGX_SPDY_SKIP_HEADERS_BUFFER_SIZE 4096
+#define NGX_SPDY_CTL_FRAME_BUFFER_SIZE 16
+
+#define NGX_SPDY_PROTOCOL_ERROR 1
+#define NGX_SPDY_INVALID_STREAM 2
+#define NGX_SPDY_REFUSED_STREAM 3
+#define NGX_SPDY_UNSUPPORTED_VERSION 4
+#define NGX_SPDY_CANCEL 5
+#define NGX_SPDY_INTERNAL_ERROR 6
+#define NGX_SPDY_FLOW_CONTROL_ERROR 7
+
+#define NGX_SPDY_SETTINGS_MAX_STREAMS 4
+
+#define NGX_SPDY_SETTINGS_FLAG_PERSIST 0x01
+
+typedef struct {
+ ngx_uint_t hash;
+ u_char len;
+ u_char header[7];
+ ngx_int_t (*handler)(ngx_http_request_t *r);
+} ngx_http_spdy_request_header_t;
+
+
+static void ngx_http_spdy_read_handler(ngx_event_t *rev);
+static void ngx_http_spdy_write_handler(ngx_event_t *wev);
+static void ngx_http_spdy_handle_connection(ngx_http_spdy_connection_t *sc);
+
+static u_char *ngx_http_spdy_state_detect_settings(
+ ngx_http_spdy_connection_t *sc, u_char *pos, u_char *end);
+static u_char *ngx_http_spdy_state_head(ngx_http_spdy_connection_t *sc,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_spdy_state_syn_stream(ngx_http_spdy_connection_t *sc,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_spdy_state_headers(ngx_http_spdy_connection_t *sc,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_spdy_state_headers_error(ngx_http_spdy_connection_t *sc,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_spdy_state_headers_skip(ngx_http_spdy_connection_t *sc,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_spdy_state_data(ngx_http_spdy_connection_t *sc,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_spdy_state_rst_stream(ngx_http_spdy_connection_t *sc,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_spdy_state_ping(ngx_http_spdy_connection_t *sc,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_spdy_state_skip(ngx_http_spdy_connection_t *sc,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_spdy_state_settings(ngx_http_spdy_connection_t *sc,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_spdy_state_noop(ngx_http_spdy_connection_t *sc,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_spdy_state_complete(ngx_http_spdy_connection_t *sc,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_spdy_state_save(ngx_http_spdy_connection_t *sc,
+ u_char *pos, u_char *end, ngx_http_spdy_handler_pt handler);
+static u_char *ngx_http_spdy_state_protocol_error(
+ ngx_http_spdy_connection_t *sc);
+static u_char *ngx_http_spdy_state_internal_error(
+ ngx_http_spdy_connection_t *sc);
+
+static ngx_int_t ngx_http_spdy_send_rst_stream(ngx_http_spdy_connection_t *sc,
+ ngx_uint_t sid, ngx_uint_t status, ngx_uint_t priority);
+static ngx_int_t ngx_http_spdy_send_settings(ngx_http_spdy_connection_t *sc);
+static ngx_int_t ngx_http_spdy_settings_frame_handler(
+ ngx_http_spdy_connection_t *sc, ngx_http_spdy_out_frame_t *frame);
+static ngx_http_spdy_out_frame_t *ngx_http_spdy_get_ctl_frame(
+ ngx_http_spdy_connection_t *sc, size_t size, ngx_uint_t priority);
+static ngx_int_t ngx_http_spdy_ctl_frame_handler(
+ ngx_http_spdy_connection_t *sc, ngx_http_spdy_out_frame_t *frame);
+
+static ngx_http_spdy_stream_t *ngx_http_spdy_create_stream(
+ ngx_http_spdy_connection_t *sc, ngx_uint_t id, ngx_uint_t priority);
+static ngx_http_spdy_stream_t *ngx_http_spdy_get_stream_by_id(
+ ngx_http_spdy_connection_t *sc, ngx_uint_t sid);
+#define ngx_http_spdy_streams_index_size(sscf) (sscf->streams_index_mask + 1)
+#define ngx_http_spdy_stream_index(sscf, sid) \
+ ((sid >> 1) & sscf->streams_index_mask)
+
+static ngx_int_t ngx_http_spdy_parse_header(ngx_http_request_t *r);
+static ngx_int_t ngx_http_spdy_alloc_large_header_buffer(ngx_http_request_t *r);
+
+static ngx_int_t ngx_http_spdy_handle_request_header(ngx_http_request_t *r);
+static ngx_int_t ngx_http_spdy_parse_method(ngx_http_request_t *r);
+static ngx_int_t ngx_http_spdy_parse_scheme(ngx_http_request_t *r);
+static ngx_int_t ngx_http_spdy_parse_url(ngx_http_request_t *r);
+static ngx_int_t ngx_http_spdy_parse_version(ngx_http_request_t *r);
+
+static ngx_int_t ngx_http_spdy_construct_request_line(ngx_http_request_t *r);
+static void ngx_http_spdy_run_request(ngx_http_request_t *r);
+static ngx_int_t ngx_http_spdy_init_request_body(ngx_http_request_t *r);
+
+static void ngx_http_spdy_handle_connection_handler(ngx_event_t *rev);
+static void ngx_http_spdy_keepalive_handler(ngx_event_t *rev);
+static void ngx_http_spdy_finalize_connection(ngx_http_spdy_connection_t *sc,
+ ngx_int_t rc);
+
+static void ngx_http_spdy_pool_cleanup(void *data);
+
+static void *ngx_http_spdy_zalloc(void *opaque, u_int items, u_int size);
+static void ngx_http_spdy_zfree(void *opaque, void *address);
+
+
+static const u_char ngx_http_spdy_dict[] =
+ "options" "get" "head" "post" "put" "delete" "trace"
+ "accept" "accept-charset" "accept-encoding" "accept-language"
+ "authorization" "expect" "from" "host"
+ "if-modified-since" "if-match" "if-none-match" "if-range"
+ "if-unmodifiedsince" "max-forwards" "proxy-authorization"
+ "range" "referer" "te" "user-agent"
+ "100" "101" "200" "201" "202" "203" "204" "205" "206"
+ "300" "301" "302" "303" "304" "305" "306" "307"
+ "400" "401" "402" "403" "404" "405" "406" "407" "408" "409" "410"
+ "411" "412" "413" "414" "415" "416" "417"
+ "500" "501" "502" "503" "504" "505"
+ "accept-ranges" "age" "etag" "location" "proxy-authenticate" "public"
+ "retry-after" "server" "vary" "warning" "www-authenticate" "allow"
+ "content-base" "content-encoding" "cache-control" "connection" "date"
+ "trailer" "transfer-encoding" "upgrade" "via" "warning"
+ "content-language" "content-length" "content-location"
+ "content-md5" "content-range" "content-type" "etag" "expires"
+ "last-modified" "set-cookie"
+ "Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday" "Sunday"
+ "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec"
+ "chunked" "text/html" "image/png" "image/jpg" "image/gif"
+ "application/xml" "application/xhtml" "text/plain" "public" "max-age"
+ "charset=iso-8859-1" "utf-8" "gzip" "deflate" "HTTP/1.1" "status"
+ "version" "url";
+
+
+static ngx_http_spdy_request_header_t ngx_http_spdy_request_headers[] = {
+ { 0, 6, "method", ngx_http_spdy_parse_method },
+ { 0, 6, "scheme", ngx_http_spdy_parse_scheme },
+ { 0, 3, "url", ngx_http_spdy_parse_url },
+ { 0, 7, "version", ngx_http_spdy_parse_version },
+};
+
+#define NGX_SPDY_REQUEST_HEADERS \
+ (sizeof(ngx_http_spdy_request_headers) \
+ / sizeof(ngx_http_spdy_request_header_t))
+
+
+void
+ngx_http_spdy_init(ngx_event_t *rev)
+{
+ int rc;
+ ngx_connection_t *c;
+ ngx_pool_cleanup_t *cln;
+ ngx_http_connection_t *hc;
+ ngx_http_spdy_srv_conf_t *sscf;
+ ngx_http_spdy_main_conf_t *smcf;
+ ngx_http_spdy_connection_t *sc;
+
+ c = rev->data;
+ hc = c->data;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "init spdy request");
+
+ c->log->action = "processing SPDY";
+
+ smcf = ngx_http_get_module_main_conf(hc->conf_ctx, ngx_http_spdy_module);
+
+ if (smcf->recv_buffer == NULL) {
+ smcf->recv_buffer = ngx_palloc(ngx_cycle->pool, smcf->recv_buffer_size);
+ if (smcf->recv_buffer == NULL) {
+ ngx_http_close_connection(c);
+ return;
+ }
+ }
+
+ sc = ngx_pcalloc(c->pool, sizeof(ngx_http_spdy_connection_t));
+ if (sc == NULL) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ sc->connection = c;
+ sc->http_connection = hc;
+
+ sc->handler = ngx_http_spdy_state_detect_settings;
+
+ sc->zstream_in.zalloc = ngx_http_spdy_zalloc;
+ sc->zstream_in.zfree = ngx_http_spdy_zfree;
+ sc->zstream_in.opaque = sc;
+
+ rc = inflateInit(&sc->zstream_in);
+ if (rc != Z_OK) {
+ ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+ "inflateInit() failed: %d", rc);
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ sc->zstream_out.zalloc = ngx_http_spdy_zalloc;
+ sc->zstream_out.zfree = ngx_http_spdy_zfree;
+ sc->zstream_out.opaque = sc;
+
+ sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_spdy_module);
+
+ rc = deflateInit2(&sc->zstream_out, (int) sscf->headers_comp,
+ Z_DEFLATED, 11, 4, Z_DEFAULT_STRATEGY);
+
+ if (rc != Z_OK) {
+ ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+ "deflateInit2() failed: %d", rc);
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ rc = deflateSetDictionary(&sc->zstream_out, ngx_http_spdy_dict,
+ sizeof(ngx_http_spdy_dict));
+ if (rc != Z_OK) {
+ ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+ "deflateSetDictionary() failed: %d", rc);
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ sc->pool = ngx_create_pool(sscf->pool_size, sc->connection->log);
+ if (sc->pool == NULL) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ cln = ngx_pool_cleanup_add(c->pool, sizeof(ngx_pool_cleanup_file_t));
+ if (cln == NULL) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ cln->handler = ngx_http_spdy_pool_cleanup;
+ cln->data = sc;
+
+ sc->streams_index = ngx_pcalloc(sc->pool,
+ ngx_http_spdy_streams_index_size(sscf)
+ * sizeof(ngx_http_spdy_stream_t *));
+ if (sc->streams_index == NULL) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ c->data = sc;
+
+ rev->handler = ngx_http_spdy_read_handler;
+ c->write->handler = ngx_http_spdy_write_handler;
+
+ ngx_http_spdy_read_handler(rev);
+}
+
+
+static void
+ngx_http_spdy_read_handler(ngx_event_t *rev)
+{
+ u_char *p, *end;
+ size_t available;
+ ssize_t n;
+ ngx_connection_t *c;
+ ngx_http_spdy_main_conf_t *smcf;
+ ngx_http_spdy_connection_t *sc;
+
+ c = rev->data;
+ sc = c->data;
+
+ if (rev->timedout) {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
+ ngx_http_spdy_finalize_connection(sc, NGX_HTTP_REQUEST_TIME_OUT);
+ return;
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "spdy read handler");
+
+ sc->blocked = 1;
+
+ smcf = ngx_http_get_module_main_conf(sc->http_connection->conf_ctx,
+ ngx_http_spdy_module);
+
+ available = smcf->recv_buffer_size - 2 * NGX_SPDY_STATE_BUFFER_SIZE;
+
+ do {
+ p = smcf->recv_buffer;
+
+ ngx_memcpy(p, sc->buffer, NGX_SPDY_STATE_BUFFER_SIZE);
+ end = p + sc->buffer_used;
+
+ n = c->recv(c, end, available);
+
+ if (n == NGX_AGAIN) {
+ break;
+ }
+
+ if (n == 0 && (sc->waiting || sc->processing)) {
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "client closed prematurely connection");
+ }
+
+ if (n == 0 || n == NGX_ERROR) {
+ ngx_http_spdy_finalize_connection(sc,
+ NGX_HTTP_CLIENT_CLOSED_REQUEST);
+ return;
+ }
+
+ end += n;
+
+ sc->buffer_used = 0;
+ sc->waiting = 0;
+
+ do {
+ p = sc->handler(sc, p, end);
+
+ if (p == NULL) {
+ return;
+ }
+
+ } while (p != end);
+
+ } while (rev->ready);
+
+ if (ngx_handle_read_event(rev, 0) != NGX_OK) {
+ ngx_http_spdy_finalize_connection(sc, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ sc->blocked = 0;
+
+ if (sc->processing) {
+ if (rev->timer_set) {
+ ngx_del_timer(rev);
+ }
+ return;
+ }
+
+ ngx_http_spdy_handle_connection(sc);
+}
+
+
+static void
+ngx_http_spdy_write_handler(ngx_event_t *wev)
+{
+ ngx_int_t rc;
+ ngx_connection_t *c;
+ ngx_http_spdy_stream_t *stream, *s, *sn;
+ ngx_http_spdy_connection_t *sc;
+
+ c = wev->data;
+ sc = c->data;
+
+ if (wev->timedout) {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "spdy write event timed out");
+ ngx_http_spdy_finalize_connection(sc, NGX_HTTP_CLIENT_CLOSED_REQUEST);
+ return;
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "spdy write handler");
+
+ sc->blocked = 2;
+
+ rc = ngx_http_spdy_send_output_queue(sc);
+
+ if (rc == NGX_ERROR) {
+ ngx_http_spdy_finalize_connection(sc, NGX_HTTP_CLIENT_CLOSED_REQUEST);
+ return;
+ }
+
+ stream = NULL;
+
+ for (s = sc->last_stream; s; s = sn) {
+ sn = s->next;
+ s->next = stream;
+ stream = s;
+ }
+
+ sc->last_stream = NULL;
+
+ sc->blocked = 1;
+
+ for ( /* void */ ; stream; stream = sn) {
+ sn = stream->next;
+ stream->handled = 0;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "spdy run stream %ui", stream->id);
+
+ wev = stream->request->connection->write;
+ wev->handler(wev);
+ }
+
+ sc->blocked = 0;
+
+ if (rc == NGX_AGAIN) {
+ return;
+ }
+
+ ngx_http_spdy_handle_connection(sc);
+}
+
+
+ngx_int_t
+ngx_http_spdy_send_output_queue(ngx_http_spdy_connection_t *sc)
+{
+ ngx_chain_t *cl;
+ ngx_event_t *wev;
+ ngx_connection_t *c;
+ ngx_http_core_loc_conf_t *clcf;
+ ngx_http_spdy_out_frame_t *out, *frame, *fn;
+
+ c = sc->connection;
+
+ if (c->error) {
+ return NGX_ERROR;
+ }
+
+ wev = c->write;
+
+ if (!wev->ready) {
+ return NGX_OK;
+ }
+
+ cl = NULL;
+ out = NULL;
+
+ for (frame = sc->last_out; frame; frame = fn) {
+ frame->last->next = cl;
+ cl = frame->first;
+
+ fn = frame->next;
+ frame->next = out;
+ out = frame;
+
+ ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "spdy frame out: %p sid:%ui prio:%ui bl:%ui size:%uz",
+ out, out->stream ? out->stream->id : 0, out->priority,
+ out->blocked, out->size);
+ }
+
+ cl = c->send_chain(c, cl, 0);
+
+ if (cl == NGX_CHAIN_ERROR) {
+ c->error = 1;
+
+ if (!sc->blocked) {
+ ngx_post_event(wev, &ngx_posted_events);
+ }
+
+ return NGX_ERROR;
+ }
+
+ clcf = ngx_http_get_module_loc_conf(sc->http_connection->conf_ctx,
+ ngx_http_core_module);
+
+ if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) {
+ return NGX_ERROR; /* FIXME */
+ }
+
+ if (cl) {
+ ngx_add_timer(wev, clcf->send_timeout);
+
+ } else {
+ if (wev->timer_set) {
+ ngx_del_timer(wev);
+ }
+ }
+
+ for ( /* void */ ; out; out = out->next) {
+ if (out->handler(sc, out) != NGX_OK) {
+ out->blocked = 1;
+ out->priority = NGX_SPDY_HIGHEST_PRIORITY;
+ break;
+ }
+
+ ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "spdy frame sent: %p sid:%ui bl:%ui size:%uz",
+ out, out->stream ? out->stream->id : 0,
+ out->blocked, out->size);
+ }
+
+ frame = NULL;
+
+ for ( /* void */ ; out; out = fn) {
+ fn = out->next;
+ out->next = frame;
+ frame = out;
+ }
+
+ sc->last_out = frame;
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_http_spdy_handle_connection(ngx_http_spdy_connection_t *sc)
+{
+ ngx_connection_t *c;
+ ngx_http_spdy_srv_conf_t *sscf;
+
+ if (sc->last_out || sc->processing) {
+ return;
+ }
+
+ c = sc->connection;
+
+ if (c->error) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ if (c->buffered) {
+ return;
+ }
+
+ sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx,
+ ngx_http_spdy_module);
+ if (sc->waiting) {
+ ngx_add_timer(c->read, sscf->recv_timeout);
+ return;
+ }
+
+ if (ngx_terminate || ngx_exiting) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ ngx_destroy_pool(sc->pool);
+
+ sc->pool = NULL;
+ sc->free_ctl_frames = NULL;
+ sc->free_fake_connections = NULL;
+
+#if (NGX_HTTP_SSL)
+ if (c->ssl) {
+ ngx_ssl_free_buffer(c);
+ }
+#endif
+
+ c->destroyed = 1;
+ c->idle = 1;
+ ngx_reusable_connection(c, 1);
+
+ c->write->handler = ngx_http_empty_handler;
+ c->read->handler = ngx_http_spdy_keepalive_handler;
+
+ if (c->write->timer_set) {
+ ngx_del_timer(c->write);
+ }
+
+ ngx_add_timer(c->read, sscf->keepalive_timeout);
+}
+
+
+static u_char *
+ngx_http_spdy_state_detect_settings(ngx_http_spdy_connection_t *sc,
+ u_char *pos, u_char *end)
+{
+ if (end - pos < NGX_SPDY_FRAME_HEADER_SIZE) {
+ return ngx_http_spdy_state_save(sc, pos, end,
+ ngx_http_spdy_state_detect_settings);
+ }
+
+ /*
+ * Since this is the first frame in a buffer,
+ * then it is properly aligned
+ */
+
+ if (*(uint32_t *) pos == htonl(ngx_spdy_ctl_frame_head(NGX_SPDY_SETTINGS)))
+ {
+ sc->length = ngx_spdy_frame_length(htonl(((uint32_t *) pos)[1]));
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
+ "spdy SETTINGS frame received, size: %uz", sc->length);
+
+ pos += NGX_SPDY_FRAME_HEADER_SIZE;
+
+ return ngx_http_spdy_state_settings(sc, pos, end);
+ }
+
+ ngx_http_spdy_send_settings(sc);
+
+ return ngx_http_spdy_state_head(sc, pos, end);
+}
+
+
+static u_char *
+ngx_http_spdy_state_head(ngx_http_spdy_connection_t *sc, u_char *pos,
+ u_char *end)
+{
+ uint32_t head, flen;
+
+ if (end - pos < NGX_SPDY_FRAME_HEADER_SIZE) {
+ return ngx_http_spdy_state_save(sc, pos, end,
+ ngx_http_spdy_state_head);
+ }
+
+ head = ngx_spdy_frame_parse_uint32(pos);
+
+ pos += sizeof(uint32_t);
+
+ flen = ngx_spdy_frame_parse_uint32(pos);
+
+ sc->flags = ngx_spdy_frame_flags(flen);
+ sc->length = ngx_spdy_frame_length(flen);
+
+ pos += sizeof(uint32_t);
+
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
+ "spdy process frame head:%08Xd f:%ui l:%ui",
+ head, sc->flags, sc->length);
+
+ if (ngx_spdy_ctl_frame_check(head)) {
+ switch (ngx_spdy_ctl_frame_type(head)) {
+
+ case NGX_SPDY_SYN_STREAM:
+ return ngx_http_spdy_state_syn_stream(sc, pos, end);
+
+ case NGX_SPDY_SYN_REPLY:
+ return ngx_http_spdy_state_protocol_error(sc);
+
+ case NGX_SPDY_RST_STREAM:
+ return ngx_http_spdy_state_rst_stream(sc, pos, end);
+
+ case NGX_SPDY_SETTINGS:
+ return ngx_http_spdy_state_skip(sc, pos, end);
+
+ case NGX_SPDY_NOOP:
+ return ngx_http_spdy_state_noop(sc, pos, end);
+
+ case NGX_SPDY_PING:
+ return ngx_http_spdy_state_ping(sc, pos, end);
+
+ case NGX_SPDY_GOAWAY:
+ return ngx_http_spdy_state_skip(sc, pos, end); /* TODO */
+
+ case NGX_SPDY_HEADERS:
+ return ngx_http_spdy_state_protocol_error(sc);
+
+ default: /* TODO logging */
+ return ngx_http_spdy_state_skip(sc, pos, end);
+ }
+ }
+
+ if (ngx_spdy_data_frame_check(head)) {
+ sc->stream = ngx_http_spdy_get_stream_by_id(sc, head);
+ return ngx_http_spdy_state_data(sc, pos, end);
+ }
+
+
+ /* TODO version & type check */
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
+ "spdy unknown frame");
+
+ return ngx_http_spdy_state_protocol_error(sc);
+}
+
+
+static u_char *
+ngx_http_spdy_state_syn_stream(ngx_http_spdy_connection_t *sc, u_char *pos,
+ u_char *end)
+{
+ ngx_uint_t sid, prio;
+ ngx_http_spdy_stream_t *stream;
+ ngx_http_spdy_srv_conf_t *sscf;
+
+ if (end - pos < NGX_SPDY_SYN_STREAM_SIZE) {
+ return ngx_http_spdy_state_save(sc, pos, end,
+ ngx_http_spdy_state_syn_stream);
+ }
+
+ if (sc->length <= NGX_SPDY_SYN_STREAM_SIZE) {
+ /* TODO logging */
+ return ngx_http_spdy_state_protocol_error(sc);
+ }
+
+ sc->length -= NGX_SPDY_SYN_STREAM_SIZE;
+
+ sid = ngx_spdy_frame_parse_sid(pos);
+ prio = pos[8] >> 6;
+
+ pos += NGX_SPDY_SYN_STREAM_SIZE;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
+ "spdy SYN_STREAM frame sid:%ui prio:%ui", sid, prio);
+
+ sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx,
+ ngx_http_spdy_module);
+
+ if (sc->processing >= sscf->concurrent_streams) {
+
+ ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,
+ "spdy concurrent streams excessed %ui", sc->processing);
+
+ if (ngx_http_spdy_send_rst_stream(sc, sid, NGX_SPDY_REFUSED_STREAM,
+ prio)
+ != NGX_OK)
+ {
+ return ngx_http_spdy_state_internal_error(sc);
+ }
+
+ return ngx_http_spdy_state_headers_skip(sc, pos, end);
+ }
+
+ stream = ngx_http_spdy_create_stream(sc, sid, prio);
+ if (stream == NULL) {
+ return ngx_http_spdy_state_internal_error(sc);
+ }
+
+ stream->in_closed = (sc->flags & NGX_SPDY_FLAG_FIN) ? 1 : 0;
+
+ stream->request->request_length = NGX_SPDY_FRAME_HEADER_SIZE
+ + NGX_SPDY_SYN_STREAM_SIZE
+ + sc->length;
+
+ sc->stream = stream;
+
+ sc->last_sid = sid;
+
+ return ngx_http_spdy_state_headers(sc, pos, end);
+}
+
+
+static u_char *
+ngx_http_spdy_state_headers(ngx_http_spdy_connection_t *sc, u_char *pos,
+ u_char *end)
+{
+ int z;
+ size_t size;
+ ngx_buf_t *buf;
+ ngx_int_t rc;
+ ngx_uint_t complete;
+ ngx_http_request_t *r;
+
+ size = end - pos;
+
+ if (size == 0) {
+ return ngx_http_spdy_state_save(sc, pos, end,
+ ngx_http_spdy_state_headers);
+ }
+
+ if (size >= sc->length) {
+ size = sc->length;
+ complete = 1;
+
+ } else {
+ complete = 0;
+ }
+
+ r = sc->stream->request;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "spdy process HEADERS %uz of %uz", size, sc->length);
+
+ buf = r->header_in;
+
+ sc->zstream_in.next_in = pos;
+ sc->zstream_in.avail_in = size;
+ sc->zstream_in.next_out = buf->last;
+ sc->zstream_in.avail_out = buf->end - buf->last - 1;
+
+ z = inflate(&sc->zstream_in, Z_NO_FLUSH);
+
+ if (z == Z_NEED_DICT) {
+ z = inflateSetDictionary(&sc->zstream_in, ngx_http_spdy_dict,
+ sizeof(ngx_http_spdy_dict));
+ if (z != Z_OK) {
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "spdy inflateSetDictionary() failed: %d", z);
+ ngx_http_spdy_close_stream(sc->stream, 0);
+ return ngx_http_spdy_state_protocol_error(sc);
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "spdy inflateSetDictionary(): %d", z);
+
+ z = sc->zstream_in.avail_in ? inflate(&sc->zstream_in, Z_NO_FLUSH)
+ : Z_OK;
+ }
+
+ if (z != Z_OK) {
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "spdy inflate() failed: %d", z);
+ ngx_http_spdy_close_stream(sc->stream, 0);
+ return ngx_http_spdy_state_protocol_error(sc);
+ }
+
+ ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "spdy inflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d",
+ sc->zstream_in.next_in, sc->zstream_in.next_out,
+ sc->zstream_in.avail_in, sc->zstream_in.avail_out,
+ z);
+
+ sc->length -= sc->zstream_in.next_in - pos;
+ pos = sc->zstream_in.next_in;
+
+ buf->last = sc->zstream_in.next_out;
+
+ if (r->headers_in.headers.part.elts == NULL) {
+
+ if (buf->last - buf->pos < NGX_SPDY_NV_NUM_SIZE) {
+ return ngx_http_spdy_state_save(sc, pos, end,
+ ngx_http_spdy_state_headers);
+ }
+
+ sc->headers = ngx_spdy_frame_parse_uint16(buf->pos);
+
+ buf->pos += NGX_SPDY_NV_NUM_SIZE;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "spdy headers count: %ui", sc->headers);
+
+ if (ngx_list_init(&r->headers_in.headers, r->pool, sc->headers + 3,
+ sizeof(ngx_table_elt_t))
+ != NGX_OK)
+ {
+ ngx_http_spdy_close_stream(sc->stream,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return ngx_http_spdy_state_headers_error(sc, pos, end);
+ }
+
+ if (ngx_array_init(&r->headers_in.cookies, r->pool, 2,
+ sizeof(ngx_table_elt_t *))
+ != NGX_OK)
+ {
+ ngx_http_spdy_close_stream(sc->stream,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return ngx_http_spdy_state_headers_error(sc, pos, end);
+ }
+ }
+
+ while (sc->headers) {
+
+ rc = ngx_http_spdy_parse_header(r);
+
+ switch (rc) {
+
+ case NGX_DONE:
+ sc->headers--;
+
+ case NGX_OK:
+ break;
+
+ case NGX_AGAIN:
+
+ if (sc->zstream_in.avail_in) {
+
+ rc = ngx_http_spdy_alloc_large_header_buffer(r);
+
+ if (rc == NGX_DECLINED) {
+ /* TODO logging */
+ ngx_http_finalize_request(r,
+ NGX_HTTP_REQUEST_HEADER_TOO_LARGE);
+ return ngx_http_spdy_state_headers_error(sc, pos, end);
+ }
+
+ if (rc != NGX_OK) {
+ ngx_http_spdy_close_stream(sc->stream,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return ngx_http_spdy_state_headers_error(sc, pos, end);
+ }
+
+ buf = r->header_in;
+
+ sc->zstream_in.next_out = buf->last;
+ sc->zstream_in.avail_out = buf->end - buf->last - 1;
+
+ z = inflate(&sc->zstream_in, Z_NO_FLUSH);
+
+ if (z != Z_OK) {
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "spdy inflate() failed: %d", z);
+ ngx_http_spdy_close_stream(sc->stream, 0);
+ return ngx_http_spdy_state_protocol_error(sc);
+ }
+
+ sc->length -= sc->zstream_in.next_in - pos;
+ pos = sc->zstream_in.next_in;
+
+ buf->last = sc->zstream_in.next_out;
+
+ continue;
+ }
+
+ if (complete) {
+ /* TODO: improve error message */
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "spdy again while last chunk");
+ ngx_http_spdy_close_stream(sc->stream, 0);
+ return ngx_http_spdy_state_protocol_error(sc);
+ }
+
+ return ngx_http_spdy_state_save(sc, pos, end,
+ ngx_http_spdy_state_headers);
+
+ case NGX_HTTP_PARSE_INVALID_REQUEST:
+
+ /* TODO: improve error message */
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client sent invalid header line");
+
+ ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
+
+ return ngx_http_spdy_state_headers_error(sc, pos, end);
+
+ default: /* NGX_HTTP_PARSE_INVALID_HEADER */
+
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client sent invalid HEADERS spdy frame");
+ ngx_http_spdy_close_stream(sc->stream, NGX_HTTP_BAD_REQUEST);
+ return ngx_http_spdy_state_protocol_error(sc);
+ }
+
+ /* a header line has been parsed successfully */
+
+ rc = ngx_http_spdy_handle_request_header(r);
+
+ if (rc != NGX_OK) {
+ if (rc == NGX_HTTP_PARSE_INVALID_HEADER) {
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client sent invalid HEADERS spdy frame");
+ ngx_http_spdy_close_stream(sc->stream, NGX_HTTP_BAD_REQUEST);
+ return ngx_http_spdy_state_protocol_error(sc);
+ }
+
+ if (rc == NGX_HTTP_PARSE_INVALID_REQUEST) {
+ ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
+ }
+
+ return ngx_http_spdy_state_headers_error(sc, pos, end);
+ }
+ }
+
+ if (buf->pos != buf->last) {
+ /* TODO: improve error message */
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "end %ui %p %p", complete, buf->pos, buf->last);
+ ngx_http_spdy_close_stream(sc->stream, NGX_HTTP_BAD_REQUEST);
+ return ngx_http_spdy_state_protocol_error(sc);
+ }
+
+ if (!complete) {
+ return ngx_http_spdy_state_save(sc, pos, end,
+ ngx_http_spdy_state_headers);
+ }
+
+ ngx_http_spdy_run_request(r);
+
+ return ngx_http_spdy_state_complete(sc, pos, end);
+}
+
+
+static u_char *
+ngx_http_spdy_state_headers_error(ngx_http_spdy_connection_t *sc, u_char *pos,
+ u_char *end)
+{
+ if (sc->connection->error) {
+ return ngx_http_spdy_state_internal_error(sc);
+ }
+
+ return ngx_http_spdy_state_headers_skip(sc, pos, end);
+}
+
+
+static u_char *
+ngx_http_spdy_state_headers_skip(ngx_http_spdy_connection_t *sc, u_char *pos,
+ u_char *end)
+{
+ int n;
+ size_t size;
+ u_char buffer[NGX_SPDY_SKIP_HEADERS_BUFFER_SIZE];
+
+ if (sc->length == 0) {
+ return ngx_http_spdy_state_complete(sc, pos, end);
+ }
+
+ size = end - pos;
+
+ if (size == 0) {
+ return ngx_http_spdy_state_save(sc, pos, end,
+ ngx_http_spdy_state_headers_skip);
+ }
+
+ sc->zstream_in.next_in = pos;
+ sc->zstream_in.avail_in = (size < sc->length) ? size : sc->length;
+
+ while (sc->zstream_in.avail_in) {
+ sc->zstream_in.next_out = buffer;
+ sc->zstream_in.avail_out = NGX_SPDY_SKIP_HEADERS_BUFFER_SIZE;
+
+ n = inflate(&sc->zstream_in, Z_NO_FLUSH);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
+ "spdy inflate(): %d", n);
+
+ if (n != Z_OK) {
+ /* TODO: logging */
+ return ngx_http_spdy_state_protocol_error(sc);
+ }
+ }
+
+ pos = sc->zstream_in.next_in;
+
+ if (size < sc->length) {
+ sc->length -= size;
+ return ngx_http_spdy_state_save(sc, pos, end,
+ ngx_http_spdy_state_headers_skip);
+ }
+
+ return ngx_http_spdy_state_complete(sc, pos, end);
+}
+
+
+static u_char *
+ngx_http_spdy_state_data(ngx_http_spdy_connection_t *sc, u_char *pos,
+ u_char *end)
+{
+ size_t size;
+ ssize_t n;
+ ngx_buf_t *buf;
+ ngx_int_t rc;
+ ngx_uint_t complete;
+ ngx_temp_file_t *tf;
+ ngx_http_request_t *r;
+ ngx_http_spdy_stream_t *stream;
+ ngx_http_request_body_t *rb;
+ ngx_http_core_loc_conf_t *clcf;
+
+ stream = sc->stream;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
+ "spdy DATA frame");
+
+ if (stream == NULL) {
+ return ngx_http_spdy_state_skip(sc, pos, end);
+ }
+
+ if (stream->in_closed) {
+ /* TODO log */
+ return ngx_http_spdy_state_protocol_error(sc);
+ }
+
+ if (stream->skip_data) {
+
+ if (sc->flags & NGX_SPDY_FLAG_FIN) {
+ stream->in_closed = 1;
+ }
+
+ /* TODO log and accounting */
+ return ngx_http_spdy_state_skip(sc, pos, end);
+ }
+
+ size = end - pos;
+
+ if (size >= sc->length) {
+ size = sc->length;
+ complete = 1;
+
+ } else {
+ sc->length -= size;
+ complete = 0;
+ }
+
+ r = stream->request;
+
+ if (r->request_body == NULL
+ && ngx_http_spdy_init_request_body(r) != NGX_OK)
+ {
+ stream->skip_data = NGX_SPDY_DATA_INTERNAL_ERROR;
+ return ngx_http_spdy_state_skip(sc, pos, end);
+ }
+
+ rb = r->request_body;
+ tf = rb->temp_file;
+ buf = rb->buf;
+
+ if (size) {
+ rb->rest += size;
+
+ if (r->headers_in.content_length_n != -1
+ && r->headers_in.content_length_n < rb->rest)
+ {
+ /* TODO logging */
+ stream->skip_data = NGX_SPDY_DATA_ERROR;
+ goto error;
+
+ } else {
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (clcf->client_max_body_size
+ && clcf->client_max_body_size < rb->rest)
+ {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "client intended to send too large chunked "
+ "body: %O bytes",
+ rb->rest);
+
+ stream->skip_data = NGX_SPDY_DATA_ERROR;
+ goto error;
+ }
+ }
+
+ if (tf) {
+ buf->start = pos;
+ buf->pos = pos;
+
+ pos += size;
+
+ buf->end = pos;
+ buf->last = pos;
+
+ n = ngx_write_chain_to_temp_file(tf, rb->bufs);
+
+ /* TODO: n == 0 or not complete and level event */
+
+ if (n == NGX_ERROR) {
+ stream->skip_data = NGX_SPDY_DATA_INTERNAL_ERROR;
+ goto error;
+ }
+
+ tf->offset += n;
+
+ } else {
+ buf->last = ngx_cpymem(buf->last, pos, size);
+ pos += size;
+ }
+
+ r->request_length += size;
+ }
+
+ if (!complete) {
+ return ngx_http_spdy_state_save(sc, pos, end,
+ ngx_http_spdy_state_data);
+ }
+
+ if (sc->flags & NGX_SPDY_FLAG_FIN) {
+
+ stream->in_closed = 1;
+
+ if (tf) {
+ ngx_memzero(buf, sizeof(ngx_buf_t));
+
+ buf->in_file = 1;
+ buf->file_last = tf->file.offset;
+ buf->file = &tf->file;
+
+ rb->buf = NULL;
+ }
+
+ if (r->headers_in.content_length_n < 0) {
+ r->headers_in.content_length_n = rb->rest;
+ }
+
+ if (rb->post_handler) {
+ rb->post_handler(r);
+ }
+ }
+
+ return ngx_http_spdy_state_complete(sc, pos, end);
+
+error:
+
+ if (rb->post_handler) {
+
+ if (stream->skip_data == NGX_SPDY_DATA_ERROR) {
+ rc = (r->headers_in.content_length_n == -1)
+ ? NGX_HTTP_REQUEST_ENTITY_TOO_LARGE
+ : NGX_HTTP_BAD_REQUEST;
+
+ } else {
+ rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ ngx_http_finalize_request(r, rc);
+ }
+
+ return ngx_http_spdy_state_skip(sc, pos, end);
+}
+
+
+static u_char *
+ngx_http_spdy_state_rst_stream(ngx_http_spdy_connection_t *sc, u_char *pos,
+ u_char *end)
+{
+ ngx_uint_t sid, status;
+ ngx_event_t *ev;
+ ngx_connection_t *fc;
+ ngx_http_request_t *r;
+ ngx_http_spdy_stream_t *stream;
+
+ if (end - pos < NGX_SPDY_RST_STREAM_SIZE) {
+ return ngx_http_spdy_state_save(sc, pos, end,
+ ngx_http_spdy_state_rst_stream);
+ }
+
+ if (sc->length != NGX_SPDY_RST_STREAM_SIZE) {
+ /* TODO logging */
+ return ngx_http_spdy_state_protocol_error(sc);
+ }
+
+ sid = ngx_spdy_frame_parse_sid(pos);
+
+ pos += NGX_SPDY_SID_SIZE;
+
+ status = ngx_spdy_frame_parse_uint32(pos);
+
+ pos += sizeof(uint32_t);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
+ "spdy RST_STREAM sid:%ui st:%ui", sid, status);
+
+
+ switch (status) {
+
+ case NGX_SPDY_PROTOCOL_ERROR:
+ /* TODO logging */
+ return ngx_http_spdy_state_protocol_error(sc);
+
+ case NGX_SPDY_INVALID_STREAM:
+ /* TODO */
+ break;
+
+ case NGX_SPDY_REFUSED_STREAM:
+ /* TODO */
+ break;
+
+ case NGX_SPDY_UNSUPPORTED_VERSION:
+ /* TODO logging */
+ return ngx_http_spdy_state_protocol_error(sc);
+
+ case NGX_SPDY_CANCEL:
+ case NGX_SPDY_INTERNAL_ERROR:
+ stream = ngx_http_spdy_get_stream_by_id(sc, sid);
+ if (stream == NULL) {
+ /* TODO false cancel */
+ break;
+ }
+
+ stream->in_closed = 1;
+ stream->out_closed = 1;
+
+ r = stream->request;
+
+ fc = r->connection;
+ fc->error = 1;
+
+ ev = fc->read;
+ ev->handler(ev);
+
+ break;
+
+ case NGX_SPDY_FLOW_CONTROL_ERROR:
+ /* TODO logging */
+ return ngx_http_spdy_state_protocol_error(sc);
+
+ default:
+ /* TODO */
+ return ngx_http_spdy_state_protocol_error(sc);
+ }
+
+ return ngx_http_spdy_state_complete(sc, pos, end);
+}
+
+
+static u_char *
+ngx_http_spdy_state_ping(ngx_http_spdy_connection_t *sc, u_char *pos,
+ u_char *end)
+{
+ u_char *p;
+ ngx_buf_t *buf;
+ ngx_http_spdy_out_frame_t *frame;
+
+ if (end - pos < NGX_SPDY_PING_SIZE) {
+ return ngx_http_spdy_state_save(sc, pos, end,
+ ngx_http_spdy_state_ping);
+ }
+
+ if (sc->length != NGX_SPDY_PING_SIZE) {
+ /* TODO logging */
+ return ngx_http_spdy_state_protocol_error(sc);
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
+ "spdy PING frame");
+
+ frame = ngx_http_spdy_get_ctl_frame(sc, NGX_SPDY_PING_SIZE,
+ NGX_SPDY_HIGHEST_PRIORITY);
+ if (frame == NULL) {
+ return ngx_http_spdy_state_internal_error(sc);
+ }
+
+ buf = frame->first->buf;
+
+ p = buf->pos;
+
+ p = ngx_spdy_frame_write_head(p, NGX_SPDY_PING);
+ p = ngx_spdy_frame_write_flags_and_len(p, 0, NGX_SPDY_PING_SIZE);
+
+ p = ngx_cpymem(p, pos, NGX_SPDY_PING_SIZE);
+
+ buf->last = p;
+
+ ngx_http_spdy_queue_frame(sc, frame);
+
+ pos += NGX_SPDY_PING_SIZE;
+
+ return ngx_http_spdy_state_complete(sc, pos, end);
+}
+
+
+static u_char *
+ngx_http_spdy_state_skip(ngx_http_spdy_connection_t *sc, u_char *pos,
+ u_char *end)
+{
+ size_t size;
+
+ size = end - pos;
+
+ if (size < sc->length) {
+ sc->length -= size;
+ return ngx_http_spdy_state_save(sc, end, end,
+ ngx_http_spdy_state_skip);
+ }
+
+ return ngx_http_spdy_state_complete(sc, pos + sc->length, end);
+}
+
+
+static u_char *
+ngx_http_spdy_state_settings(ngx_http_spdy_connection_t *sc, u_char *pos,
+ u_char *end)
+{
+ ngx_uint_t v;
+ ngx_http_spdy_srv_conf_t *sscf;
+
+ if (sc->headers == 0) {
+
+ if (end - pos < NGX_SPDY_SETTINGS_NUM_SIZE) {
+ return ngx_http_spdy_state_save(sc, pos, end,
+ ngx_http_spdy_state_settings);
+ }
+
+ sc->headers = ngx_spdy_frame_parse_uint32(pos);
+
+ pos += NGX_SPDY_SETTINGS_NUM_SIZE;
+ sc->length -= NGX_SPDY_SETTINGS_NUM_SIZE;
+
+ if (sc->length < sc->headers * NGX_SPDY_SETTINGS_PAIR_SIZE) {
+ /* TODO logging */
+ return ngx_http_spdy_state_protocol_error(sc);
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
+ "spdy SETTINGS frame consists of %ui entries",
+ sc->headers);
+ }
+
+ while (sc->headers) {
+ if (end - pos < NGX_SPDY_SETTINGS_PAIR_SIZE) {
+ return ngx_http_spdy_state_save(sc, pos, end,
+ ngx_http_spdy_state_settings);
+ }
+
+ sc->headers--;
+
+ if (pos[0] != NGX_SPDY_SETTINGS_MAX_STREAMS) {
+ pos += NGX_SPDY_SETTINGS_PAIR_SIZE;
+ sc->length -= NGX_SPDY_SETTINGS_PAIR_SIZE;
+ continue;
+ }
+
+ v = ngx_spdy_frame_parse_uint32(pos + NGX_SPDY_SETTINGS_IDF_SIZE);
+
+ sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx,
+ ngx_http_spdy_module);
+
+ if (v != sscf->concurrent_streams) {
+ ngx_http_spdy_send_settings(sc);
+ }
+
+ return ngx_http_spdy_state_skip(sc, pos, end);
+ }
+
+ ngx_http_spdy_send_settings(sc);
+
+ return ngx_http_spdy_state_complete(sc, pos, end);
+}
+
+
+static u_char *
+ngx_http_spdy_state_noop(ngx_http_spdy_connection_t *sc, u_char *pos,
+ u_char *end)
+{
+ if (sc->length) {
+ /* TODO logging */
+ return ngx_http_spdy_state_protocol_error(sc);
+ }
+
+ return ngx_http_spdy_state_complete(sc, pos, end);
+}
+
+
+static u_char *
+ngx_http_spdy_state_complete(ngx_http_spdy_connection_t *sc, u_char *pos,
+ u_char *end)
+{
+ sc->handler = ngx_http_spdy_state_head;
+ return pos;
+}
+
+
+static u_char *
+ngx_http_spdy_state_save(ngx_http_spdy_connection_t *sc,
+ u_char *pos, u_char *end, ngx_http_spdy_handler_pt handler)
+{
+#if (NGX_DEBUG)
+ if (end - pos > NGX_SPDY_STATE_BUFFER_SIZE) {
+ ngx_log_error(NGX_LOG_ALERT, sc->connection->log, 0,
+ "spdy state buffer overflow: "
+ "%i bytes required", end - pos);
+ return ngx_http_spdy_state_internal_error(sc);
+ }
+#endif
+
+ ngx_memcpy(sc->buffer, pos, NGX_SPDY_STATE_BUFFER_SIZE);
+
+ sc->buffer_used = end - pos;
+ sc->handler = handler;
+ sc->waiting = 1;
+
+ return end;
+}
+
+
+static u_char *
+ngx_http_spdy_state_protocol_error(ngx_http_spdy_connection_t *sc)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
+ "spdy state protocol error");
+
+ /* TODO */
+ ngx_http_spdy_finalize_connection(sc, NGX_HTTP_CLIENT_CLOSED_REQUEST);
+ return NULL;
+}
+
+
+static u_char *
+ngx_http_spdy_state_internal_error(ngx_http_spdy_connection_t *sc)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
+ "spdy state internal error");
+
+ /* TODO */
+ ngx_http_spdy_finalize_connection(sc, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return NULL;
+}
+
+
+static ngx_int_t
+ngx_http_spdy_send_rst_stream(ngx_http_spdy_connection_t *sc, ngx_uint_t sid,
+ ngx_uint_t status, ngx_uint_t priority)
+{
+ u_char *p;
+ ngx_buf_t *buf;
+ ngx_http_spdy_out_frame_t *frame;
+
+ if (sc->connection->error) {
+ return NGX_OK;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
+ "spdy write RST_STREAM sid:%ui st:%ui", sid, status);
+
+ frame = ngx_http_spdy_get_ctl_frame(sc, NGX_SPDY_RST_STREAM_SIZE,
+ priority);
+ if (frame == NULL) {
+ return NGX_ERROR;
+ }
+
+ buf = frame->first->buf;
+
+ p = buf->pos;
+
+ p = ngx_spdy_frame_write_head(p, NGX_SPDY_RST_STREAM);
+ p = ngx_spdy_frame_write_flags_and_len(p, 0, NGX_SPDY_RST_STREAM_SIZE);
+
+ p = ngx_spdy_frame_write_sid(p, sid);
+ p = ngx_spdy_frame_aligned_write_uint32(p, status);
+
+ buf->last = p;
+
+ ngx_http_spdy_queue_frame(sc, frame);
+
+ return NGX_OK;
+}
+
+
+#if 0
+static ngx_int_t
+ngx_http_spdy_send_goaway(ngx_http_spdy_connection_t *sc)
+{
+ u_char *p;
+ ngx_buf_t *buf;
+ ngx_http_spdy_out_frame_t *frame;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
+ "spdy create GOAWAY sid:%ui", sc->last_sid);
+
+ frame = ngx_http_spdy_get_ctl_frame(sc, NGX_SPDY_GOAWAY_SIZE,
+ NGX_SPDY_HIGHEST_PRIORITY);
+ if (frame == NULL) {
+ return NGX_ERROR;
+ }
+
+ buf = frame->first->buf;
+
+ p = buf->pos;
+
+ p = ngx_spdy_frame_write_head(p, NGX_SPDY_GOAWAY);
+ p = ngx_spdy_frame_write_flags_and_len(p, 0, NGX_SPDY_GOAWAY_SIZE);
+
+ p = ngx_spdy_frame_write_sid(p, sc->last_sid);
+
+ buf->last = p;
+
+ ngx_http_spdy_queue_frame(sc, frame);
+
+ return NGX_OK;
+}
+#endif
+
+
+static ngx_int_t
+ngx_http_spdy_send_settings(ngx_http_spdy_connection_t *sc)
+{
+ u_char *p;
+ ngx_buf_t *buf;
+ ngx_pool_t *pool;
+ ngx_chain_t *cl;
+ ngx_http_spdy_srv_conf_t *sscf;
+ ngx_http_spdy_out_frame_t *frame;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
+ "spdy create SETTINGS frame");
+
+ pool = sc->connection->pool;
+
+ frame = ngx_palloc(pool, sizeof(ngx_http_spdy_out_frame_t));
+ if (frame == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl = ngx_alloc_chain_link(pool);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ buf = ngx_create_temp_buf(pool, NGX_SPDY_FRAME_HEADER_SIZE
+ + NGX_SPDY_SETTINGS_NUM_SIZE
+ + NGX_SPDY_SETTINGS_PAIR_SIZE);
+ if (buf == NULL) {
+ return NGX_ERROR;
+ }
+
+ buf->last_buf = 1;
+
+ cl->buf = buf;
+ cl->next = NULL;
+
+ frame->first = cl;
+ frame->last = cl;
+ frame->handler = ngx_http_spdy_settings_frame_handler;
+#if (NGX_DEBUG)
+ frame->stream = NULL;
+ frame->size = NGX_SPDY_FRAME_HEADER_SIZE
+ + NGX_SPDY_SETTINGS_NUM_SIZE
+ + NGX_SPDY_SETTINGS_PAIR_SIZE;
+#endif
+ frame->priority = NGX_SPDY_HIGHEST_PRIORITY;
+ frame->blocked = 0;
+
+ p = buf->pos;
+
+ p = ngx_spdy_frame_write_head(p, NGX_SPDY_SETTINGS);
+ p = ngx_spdy_frame_write_flags_and_len(p, NGX_SPDY_FLAG_CLEAR_SETTINGS,
+ NGX_SPDY_SETTINGS_NUM_SIZE
+ + NGX_SPDY_SETTINGS_PAIR_SIZE);
+
+ p = ngx_spdy_frame_aligned_write_uint32(p, 1);
+ p = ngx_spdy_frame_aligned_write_uint32(p,
+ NGX_SPDY_SETTINGS_MAX_STREAMS << 24
+ | NGX_SPDY_SETTINGS_FLAG_PERSIST);
+
+ sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx,
+ ngx_http_spdy_module);
+
+ p = ngx_spdy_frame_aligned_write_uint32(p, sscf->concurrent_streams);
+
+ buf->last = p;
+
+ ngx_http_spdy_queue_frame(sc, frame);
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_http_spdy_settings_frame_handler(ngx_http_spdy_connection_t *sc,
+ ngx_http_spdy_out_frame_t *frame)
+{
+ ngx_buf_t *buf;
+
+ buf = frame->first->buf;
+
+ if (buf->pos != buf->last) {
+ return NGX_AGAIN;
+ }
+
+ ngx_free_chain(sc->pool, frame->first);
+
+ return NGX_OK;
+}
+
+
+static ngx_http_spdy_out_frame_t *
+ngx_http_spdy_get_ctl_frame(ngx_http_spdy_connection_t *sc, size_t size,
+ ngx_uint_t priority)
+{
+ ngx_chain_t *cl;
+ ngx_http_spdy_out_frame_t *frame;
+
+ frame = sc->free_ctl_frames;
+
+ if (frame) {
+ sc->free_ctl_frames = frame->free;
+
+ cl = frame->first;
+ cl->buf->pos = cl->buf->start;
+
+ } else {
+ frame = ngx_palloc(sc->pool, sizeof(ngx_http_spdy_out_frame_t));
+ if (frame == NULL) {
+ return NULL;
+ }
+
+ cl = ngx_alloc_chain_link(sc->pool);
+ if (cl == NULL) {
+ return NULL;
+ }
+
+ cl->buf = ngx_create_temp_buf(sc->pool,
+ NGX_SPDY_CTL_FRAME_BUFFER_SIZE);
+ if (cl->buf == NULL) {
+ return NULL;
+ }
+
+ cl->buf->last_buf = 1;
+
+ frame->first = cl;
+ frame->last = cl;
+ frame->handler = ngx_http_spdy_ctl_frame_handler;
+ }
+
+ frame->free = NULL;
+
+#if (NGX_DEBUG)
+ if (size > NGX_SPDY_CTL_FRAME_BUFFER_SIZE - NGX_SPDY_FRAME_HEADER_SIZE) {
+ ngx_log_error(NGX_LOG_ALERT, sc->pool->log, 0,
+ "requested control frame is too big: %z", size);
+ return NULL;
+ }
+
+ frame->stream = NULL;
+ frame->size = size;
+#endif
+
+ frame->priority = priority;
+ frame->blocked = 0;
+
+ return frame;
+}
+
+
+static ngx_int_t
+ngx_http_spdy_ctl_frame_handler(ngx_http_spdy_connection_t *sc,
+ ngx_http_spdy_out_frame_t *frame)
+{
+ ngx_buf_t *buf;
+
+ buf = frame->first->buf;
+
+ if (buf->pos != buf->last) {
+ return NGX_AGAIN;
+ }
+
+ frame->free = sc->free_ctl_frames;
+ sc->free_ctl_frames = frame;
+
+ return NGX_OK;
+}
+
+
+static ngx_http_spdy_stream_t *
+ngx_http_spdy_create_stream(ngx_http_spdy_connection_t *sc, ngx_uint_t id,
+ ngx_uint_t priority)
+{
+ ngx_log_t *log;
+ ngx_uint_t index;
+ ngx_event_t *rev, *wev;
+ ngx_connection_t *fc;
+ ngx_http_log_ctx_t *ctx;
+ ngx_http_request_t *r;
+ ngx_http_spdy_stream_t *stream;
+ ngx_http_core_srv_conf_t *cscf;
+ ngx_http_spdy_srv_conf_t *sscf;
+
+ fc = sc->free_fake_connections;
+
+ if (fc) {
+ sc->free_fake_connections = fc->data;
+
+ rev = fc->read;
+ wev = fc->write;
+ log = fc->log;
+ ctx = log->data;
+
+ } else {
+ fc = ngx_palloc(sc->pool, sizeof(ngx_connection_t));
+ if (fc == NULL) {
+ return NULL;
+ }
+
+ rev = ngx_palloc(sc->pool, sizeof(ngx_event_t));
+ if (rev == NULL) {
+ return NULL;
+ }
+
+ wev = ngx_palloc(sc->pool, sizeof(ngx_event_t));
+ if (wev == NULL) {
+ return NULL;
+ }
+
+ log = ngx_palloc(sc->pool, sizeof(ngx_log_t));
+ if (log == NULL) {
+ return NULL;
+ }
+
+ ctx = ngx_palloc(sc->pool, sizeof(ngx_http_log_ctx_t));
+ if (ctx == NULL) {
+ return NULL;
+ }
+
+ ctx->connection = fc;
+ ctx->request = NULL;
+ }
+
+ ngx_memcpy(log, sc->connection->log, sizeof(ngx_log_t));
+
+ log->data = ctx;
+
+ ngx_memzero(rev, sizeof(ngx_event_t));
+
+ rev->data = fc;
+ rev->ready = 1;
+ rev->handler = ngx_http_empty_handler;
+ rev->log = log;
+
+ ngx_memcpy(wev, rev, sizeof(ngx_event_t));
+
+ wev->write = 1;
+
+ ngx_memcpy(fc, sc->connection, sizeof(ngx_connection_t));
+
+ fc->data = sc->http_connection;
+ fc->read = rev;
+ fc->write = wev;
+ fc->sent = 0;
+ fc->log = log;
+ fc->buffered = 0;
+ fc->sndlowat = 1;
+ fc->tcp_nodelay = NGX_TCP_NODELAY_DISABLED;
+
+ r = ngx_http_create_request(fc);
+ if (r == NULL) {
+ return NULL;
+ }
+
+ r->valid_location = 1;
+
+ fc->data = r;
+ sc->connection->requests++;
+
+ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
+
+ r->header_in = ngx_create_temp_buf(r->pool,
+ cscf->client_header_buffer_size);
+ if (r->header_in == NULL) {
+ ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return NULL;
+ }
+
+ r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE;
+
+ stream = ngx_pcalloc(r->pool, sizeof(ngx_http_spdy_stream_t));
+ if (stream == NULL) {
+ ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return NULL;
+ }
+
+ r->spdy_stream = stream;
+
+ stream->id = id;
+ stream->request = r;
+ stream->connection = sc;
+ stream->priority = priority;
+
+ sscf = ngx_http_get_module_srv_conf(r, ngx_http_spdy_module);
+
+ index = ngx_http_spdy_stream_index(sscf, id);
+
+ stream->index = sc->streams_index[index];
+ sc->streams_index[index] = stream;
+
+ sc->processing++;
+
+ return stream;
+}
+
+
+static ngx_http_spdy_stream_t *
+ngx_http_spdy_get_stream_by_id(ngx_http_spdy_connection_t *sc,
+ ngx_uint_t sid)
+{
+ ngx_http_spdy_stream_t *stream;
+ ngx_http_spdy_srv_conf_t *sscf;
+
+ sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx,
+ ngx_http_spdy_module);
+
+ stream = sc->streams_index[ngx_http_spdy_stream_index(sscf, sid)];
+
+ while (stream) {
+ if (stream->id == sid) {
+ return stream;
+ }
+
+ stream = stream->index;
+ }
+
+ return NULL;
+}
+
+
+static ngx_int_t
+ngx_http_spdy_parse_header(ngx_http_request_t *r)
+{
+ u_char *p, *end, ch;
+ ngx_uint_t len, hash;
+ ngx_http_core_srv_conf_t *cscf;
+
+ enum {
+ sw_name_len = 0,
+ sw_name,
+ sw_value_len,
+ sw_value
+ } state;
+
+ state = r->state;
+
+ p = r->header_in->pos;
+ end = r->header_in->last;
+
+ switch (state) {
+
+ case sw_name_len:
+
+ if (end - p < NGX_SPDY_NV_NLEN_SIZE) {
+ return NGX_AGAIN;
+ }
+
+ len = ngx_spdy_frame_parse_uint16(p);
+
+ if (!len) {
+ return NGX_HTTP_PARSE_INVALID_HEADER;
+ }
+
+ p += NGX_SPDY_NV_NLEN_SIZE;
+
+ r->header_name_end = p + len;
+ r->lowcase_index = len;
+ r->invalid_header = 0;
+
+ state = sw_name;
+
+ /* fall through */
+
+ case sw_name:
+
+ if (r->header_name_end > end) {
+ break;
+ }
+
+ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
+
+ r->header_name_start = p;
+
+ hash = 0;
+
+ for ( /* void */ ; p != r->header_name_end; p++) {
+
+ ch = *p;
+
+ hash = ngx_hash(hash, ch);
+
+ if ((ch >= 'a' && ch <= 'z')
+ || (ch == '-')
+ || (ch >= '0' && ch <= '9')
+ || (ch == '_' && cscf->underscores_in_headers))
+ {
+ continue;
+ }
+
+ switch (ch) {
+ case '\0':
+ case LF:
+ case CR:
+ case ':':
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+
+ if (ch >= 'A' && ch <= 'Z') {
+ return NGX_HTTP_PARSE_INVALID_HEADER;
+ }
+
+ r->invalid_header = 1;
+ }
+
+ r->header_hash = hash;
+
+ state = sw_value_len;
+
+ /* fall through */
+
+ case sw_value_len:
+
+ if (end - p < NGX_SPDY_NV_VLEN_SIZE) {
+ break;
+ }
+
+ len = ngx_spdy_frame_parse_uint16(p);
+
+ if (!len) {
+ return NGX_ERROR;
+ }
+
+ p += NGX_SPDY_NV_VLEN_SIZE;
+
+ r->header_end = p + len;
+
+ state = sw_value;
+
+ /* fall through */
+
+ case sw_value:
+
+ if (r->header_end > end) {
+ break;
+ }
+
+ r->header_start = p;
+
+ for ( /* void */ ; p != r->header_end; p++) {
+
+ ch = *p;
+
+ if (ch == '\0') {
+
+ if (p == r->header_start) {
+ return NGX_ERROR;
+ }
+
+ r->header_size = p - r->header_start;
+ r->header_in->pos = p + 1;
+
+ return NGX_OK;
+ }
+
+ if (ch == CR || ch == LF) {
+ return NGX_HTTP_PARSE_INVALID_HEADER;
+ }
+ }
+
+ r->header_size = p - r->header_start;
+ r->header_in->pos = p;
+
+ r->state = 0;
+
+ return NGX_DONE;
+ }
+
+ r->header_in->pos = p;
+ r->state = state;
+
+ return NGX_AGAIN;
+}
+
+
+static ngx_int_t
+ngx_http_spdy_alloc_large_header_buffer(ngx_http_request_t *r)
+{
+ u_char *old, *new;
+ size_t rest;
+ ngx_buf_t *buf;
+ ngx_http_spdy_stream_t *stream;
+ ngx_http_core_srv_conf_t *cscf;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "spdy alloc large header buffer");
+
+ stream = r->spdy_stream;
+
+ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
+
+ if (stream->header_buffers
+ == (ngx_uint_t) cscf->large_client_header_buffers.num)
+ {
+ return NGX_DECLINED;
+ }
+
+ rest = r->header_in->last - r->header_in->pos;
+
+ if (rest >= cscf->large_client_header_buffers.size) {
+ return NGX_DECLINED;
+ }
+
+ buf = ngx_create_temp_buf(r->pool, cscf->large_client_header_buffers.size);
+ if (buf == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "spdy large header alloc: %p %uz",
+ buf->pos, buf->end - buf->last);
+
+ old = r->header_in->pos;
+ new = buf->pos;
+
+ if (rest) {
+ buf->last = ngx_cpymem(new, old, rest);
+ }
+
+ if (r->header_name_end > old) {
+ r->header_name_end = new + (r->header_name_end - old);
+
+ } else if (r->header_end > old) {
+ r->header_end = new + (r->header_end - old);
+ }
+
+ r->header_in = buf;
+
+ stream->header_buffers++;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_spdy_handle_request_header(ngx_http_request_t *r)
+{
+ ngx_uint_t i;
+ ngx_table_elt_t *h;
+ ngx_http_core_srv_conf_t *cscf;
+ ngx_http_spdy_request_header_t *sh;
+
+ if (r->invalid_header) {
+ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
+
+ if (cscf->ignore_invalid_headers) {
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client sent invalid header: \"%*s\"",
+ r->header_end - r->header_name_start,
+ r->header_name_start);
+ return NGX_OK;
+ }
+
+ } else {
+ for (i = 0; i < NGX_SPDY_REQUEST_HEADERS; i++) {
+ sh = &ngx_http_spdy_request_headers[i];
+
+ if (sh->hash != r->header_hash
+ || sh->len != r->lowcase_index
+ || ngx_strncmp(sh->header, r->header_name_start,
+ r->lowcase_index)
+ != 0)
+ {
+ continue;
+ }
+
+ return sh->handler(r);
+ }
+ }
+
+ h = ngx_list_push(&r->headers_in.headers);
+ if (h == NULL) {
+ ngx_http_spdy_close_stream(r->spdy_stream,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return NGX_ERROR;
+ }
+
+ h->hash = r->header_hash;
+
+ h->key.len = r->lowcase_index;
+ h->key.data = r->header_name_start;
+ h->key.data[h->key.len] = '\0';
+
+ h->value.len = r->header_size;
+ h->value.data = r->header_start;
+ h->value.data[h->value.len] = '\0';
+
+ h->lowcase_key = h->key.data;
+
+ return NGX_OK;
+}
+
+
+void
+ngx_http_spdy_request_headers_init()
+{
+ ngx_uint_t i;
+ ngx_http_spdy_request_header_t *h;
+
+ for (i = 0; i < NGX_SPDY_REQUEST_HEADERS; i++) {
+ h = &ngx_http_spdy_request_headers[i];
+ h->hash = ngx_hash_key(h->header, h->len);
+ }
+}
+
+
+static ngx_int_t
+ngx_http_spdy_parse_method(ngx_http_request_t *r)
+{
+ size_t k, len;
+ ngx_uint_t n;
+ const u_char *p, *m;
+
+ /*
+ * This array takes less than 256 sequential bytes,
+ * and if typical CPU cache line size is 64 bytes,
+ * it is prefetched for 4 load operations.
+ */
+ static const struct {
+ u_char len;
+ const u_char method[11];
+ uint32_t value;
+ } tests[] = {
+ { 3, "GET", NGX_HTTP_GET },
+ { 4, "POST", NGX_HTTP_POST },
+ { 4, "HEAD", NGX_HTTP_HEAD },
+ { 7, "OPTIONS", NGX_HTTP_OPTIONS },
+ { 8, "PROPFIND", NGX_HTTP_PROPFIND },
+ { 3, "PUT", NGX_HTTP_PUT },
+ { 5, "MKCOL", NGX_HTTP_MKCOL },
+ { 6, "DELETE", NGX_HTTP_DELETE },
+ { 4, "COPY", NGX_HTTP_COPY },
+ { 4, "MOVE", NGX_HTTP_MOVE },
+ { 9, "PROPPATCH", NGX_HTTP_PROPPATCH },
+ { 4, "LOCK", NGX_HTTP_LOCK },
+ { 6, "UNLOCK", NGX_HTTP_UNLOCK },
+ { 5, "PATCH", NGX_HTTP_PATCH },
+ { 5, "TRACE", NGX_HTTP_TRACE }
+ }, *test;
+
+ if (r->method_name.len) {
+ return NGX_HTTP_PARSE_INVALID_HEADER;
+ }
+
+ len = r->header_size;
+
+ r->method_name.len = len;
+ r->method_name.data = r->header_start;
+
+ test = tests;
+ n = sizeof(tests) / sizeof(tests[0]);
+
+ do {
+ if (len == test->len) {
+ p = r->method_name.data;
+ m = test->method;
+ k = len;
+
+ do {
+ if (*p++ != *m++) {
+ goto next;
+ }
+ } while (--k);
+
+ r->method = test->value;
+ return NGX_OK;
+ }
+
+ next:
+ test++;
+
+ } while (--n);
+
+ p = r->method_name.data;
+
+ do {
+ if ((*p < 'A' || *p > 'Z') && *p != '_') {
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client sent invalid method");
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+
+ p++;
+
+ } while (--len);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_spdy_parse_scheme(ngx_http_request_t *r)
+{
+ if (r->schema_start) {
+ return NGX_HTTP_PARSE_INVALID_HEADER;
+ }
+
+ r->schema_start = r->header_start;
+ r->schema_end = r->header_end;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_spdy_parse_url(ngx_http_request_t *r)
+{
+ if (r->unparsed_uri.len) {
+ return NGX_HTTP_PARSE_INVALID_HEADER;
+ }
+
+ r->uri_start = r->header_start;
+ r->uri_end = r->header_end;
+
+ if (ngx_http_parse_uri(r) != NGX_OK) {
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+
+ if (ngx_http_process_request_uri(r) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_spdy_parse_version(ngx_http_request_t *r)
+{
+ u_char *p, ch;
+
+ if (r->http_protocol.len) {
+ return NGX_HTTP_PARSE_INVALID_HEADER;
+ }
+
+ p = r->header_start;
+
+ if (r->header_size < 8 || !(ngx_str5cmp(p, 'H', 'T', 'T', 'P', '/'))) {
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+
+ ch = *(p + 5);
+
+ if (ch < '1' || ch > '9') {
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+
+ r->http_major = ch - '0';
+
+ for (p += 6; p != r->header_end - 2; p++) {
+
+ ch = *p;
+
+ if (ch < '0' || ch > '9') {
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+
+ r->http_major = r->http_major * 10 + ch - '0';
+ }
+
+ if (*p != '.') {
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+
+ ch = *(p + 1);
+
+ if (ch < '0' || ch > '9') {
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+
+ r->http_minor = ch - '0';
+
+ for (p += 2; p != r->header_end; p++) {
+
+ ch = *p;
+
+ if (ch < '0' || ch > '9') {
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+
+ r->http_minor = r->http_minor * 10 + ch - '0';
+ }
+
+ r->http_protocol.len = r->header_size;
+ r->http_protocol.data = r->header_start;
+ r->http_version = r->http_major * 1000 + r->http_minor;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_spdy_construct_request_line(ngx_http_request_t *r)
+{
+ u_char *p;
+
+ if (r->method_name.len == 0
+ || r->unparsed_uri.len == 0
+ || r->http_protocol.len == 0)
+ {
+ ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
+ return NGX_ERROR;
+ }
+
+ r->request_line.len = r->method_name.len + 1
+ + r->unparsed_uri.len + 1
+ + r->http_protocol.len;
+
+ p = ngx_pnalloc(r->pool, r->request_line.len + 1);
+ if (p == NULL) {
+ ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return NGX_ERROR;
+ }
+
+ r->request_line.data = p;
+
+ p = ngx_cpymem(p, r->method_name.data, r->method_name.len);
+
+ *p++ = ' ';
+
+ p = ngx_cpymem(p, r->unparsed_uri.data, r->unparsed_uri.len);
+
+ *p++ = ' ';
+
+ ngx_memcpy(p, r->http_protocol.data, r->http_protocol.len + 1);
+
+ /* some modules expect the space character after method name */
+ r->method_name.data = r->request_line.data;
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_http_spdy_run_request(ngx_http_request_t *r)
+{
+ ngx_uint_t i;
+ ngx_list_part_t *part;
+ ngx_table_elt_t *h;
+ ngx_http_header_t *hh;
+ ngx_http_core_main_conf_t *cmcf;
+
+ if (ngx_http_spdy_construct_request_line(r) != NGX_OK) {
+ return;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "spdy http request line: \"%V\"", &r->request_line);
+
+ cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
+
+ part = &r->headers_in.headers.part;
+ h = part->elts;
+
+ for (i = 0 ;; i++) {
+
+ if (i >= part->nelts) {
+ if (part->next == NULL) {
+ break;
+ }
+
+ part = part->next;
+ h = part->elts;
+ i = 0;
+ }
+
+ hh = ngx_hash_find(&cmcf->headers_in_hash, h[i].hash,
+ h[i].lowcase_key, h[i].key.len);
+
+ if (hh && hh->handler(r, &h[i], hh->offset) != NGX_OK) {
+ return;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http header: \"%V: %V\"", &h[i].key, &h[i].value);
+ }
+
+ r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE;
+
+ if (ngx_http_process_request_header(r) != NGX_OK) {
+ return;
+ }
+
+ ngx_http_process_request(r);
+}
+
+
+static ngx_int_t
+ngx_http_spdy_init_request_body(ngx_http_request_t *r)
+{
+ ngx_buf_t *buf;
+ ngx_temp_file_t *tf;
+ ngx_http_request_body_t *rb;
+ ngx_http_core_loc_conf_t *clcf;
+
+ rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));
+ if (rb == NULL) {
+ return NGX_ERROR;
+ }
+
+ r->request_body = rb;
+
+ if (r->spdy_stream->in_closed) {
+ return NGX_OK;
+ }
+
+ rb->rest = r->headers_in.content_length_n;
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (r->request_body_in_file_only
+ || rb->rest > (off_t) clcf->client_body_buffer_size
+ || rb->rest < 0)
+ {
+ tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t));
+ if (tf == NULL) {
+ return NGX_ERROR;
+ }
+
+ tf->file.fd = NGX_INVALID_FILE;
+ tf->file.log = r->connection->log;
+ tf->path = clcf->client_body_temp_path;
+ tf->pool = r->pool;
+ tf->warn = "a client request body is buffered to a temporary file";
+ tf->log_level = r->request_body_file_log_level;
+ tf->persistent = r->request_body_in_persistent_file;
+ tf->clean = r->request_body_in_clean_file;
+
+ if (r->request_body_file_group_access) {
+ tf->access = 0660;
+ }
+
+ rb->temp_file = tf;
+
+ if (r->spdy_stream->in_closed
+ && ngx_create_temp_file(&tf->file, tf->path, tf->pool,
+ tf->persistent, tf->clean, tf->access)
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ buf = ngx_calloc_buf(r->pool);
+ if (buf == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (rb->rest == 0) {
+ buf->in_file = 1;
+ buf->file = &tf->file;
+ } else {
+ rb->buf = buf;
+ }
+
+ } else {
+
+ if (rb->rest == 0) {
+ return NGX_OK;
+ }
+
+ buf = ngx_create_temp_buf(r->pool, (size_t) rb->rest);
+ if (buf == NULL) {
+ return NGX_ERROR;
+ }
+
+ rb->buf = buf;
+ }
+
+ rb->bufs = ngx_alloc_chain_link(r->pool);
+ if (rb->bufs == NULL) {
+ return NGX_ERROR;
+ }
+
+ rb->bufs->buf = buf;
+ rb->bufs->next = NULL;
+
+ rb->rest = 0;
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_http_spdy_read_request_body(ngx_http_request_t *r,
+ ngx_http_client_body_handler_pt post_handler)
+{
+ ngx_http_spdy_stream_t *stream;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "spdy read request body");
+
+ stream = r->spdy_stream;
+
+ switch (stream->skip_data) {
+
+ case NGX_SPDY_DATA_DISCARD:
+ post_handler(r);
+ return NGX_OK;
+
+ case NGX_SPDY_DATA_ERROR:
+ if (r->headers_in.content_length_n == -1) {
+ return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE;
+ } else {
+ return NGX_HTTP_BAD_REQUEST;
+ }
+
+ case NGX_SPDY_DATA_INTERNAL_ERROR:
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ if (!r->request_body && ngx_http_spdy_init_request_body(r) != NGX_OK) {
+ stream->skip_data = NGX_SPDY_DATA_INTERNAL_ERROR;
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ if (stream->in_closed) {
+ post_handler(r);
+ return NGX_OK;
+ }
+
+ r->request_body->post_handler = post_handler;
+
+ return NGX_AGAIN;
+}
+
+
+void
+ngx_http_spdy_close_stream(ngx_http_spdy_stream_t *stream, ngx_int_t rc)
+{
+ ngx_event_t *ev;
+ ngx_connection_t *fc;
+ ngx_http_spdy_stream_t **index, *s;
+ ngx_http_spdy_srv_conf_t *sscf;
+ ngx_http_spdy_connection_t *sc;
+
+ sc = stream->connection;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
+ "spdy close stream %ui, processing %ui",
+ stream->id, sc->processing);
+
+ if (!stream->out_closed) {
+ if (ngx_http_spdy_send_rst_stream(sc, stream->id,
+ NGX_SPDY_INTERNAL_ERROR,
+ stream->priority)
+ != NGX_OK)
+ {
+ sc->connection->error = 1;
+ }
+ }
+
+ sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx,
+ ngx_http_spdy_module);
+
+ index = sc->streams_index + ngx_http_spdy_stream_index(sscf, stream->id);
+
+ for ( ;; ) {
+ s = *index;
+
+ if (s == NULL) {
+ break;
+ }
+
+ if (s == stream) {
+ *index = s->index;
+ break;
+ }
+
+ index = &s->index;
+ }
+
+ fc = stream->request->connection;
+
+ ngx_http_free_request(stream->request, rc);
+
+ ev = fc->read;
+
+ if (ev->active || ev->disabled) {
+ ngx_del_event(ev, NGX_READ_EVENT, 0);
+ }
+
+ if (ev->timer_set) {
+ ngx_del_timer(ev);
+ }
+
+ if (ev->prev) {
+ ngx_delete_posted_event(ev);
+ }
+
+ ev = fc->write;
+
+ if (ev->active || ev->disabled) {
+ ngx_del_event(ev, NGX_WRITE_EVENT, 0);
+ }
+
+ if (ev->timer_set) {
+ ngx_del_timer(ev);
+ }
+
+ if (ev->prev) {
+ ngx_delete_posted_event(ev);
+ }
+
+ fc->data = sc->free_fake_connections;
+ sc->free_fake_connections = fc;
+
+ sc->processing--;
+
+ if (sc->processing || sc->blocked) {
+ return;
+ }
+
+ ev = sc->connection->read;
+
+ ev->handler = ngx_http_spdy_handle_connection_handler;
+ ngx_post_event(ev, &ngx_posted_events);
+}
+
+
+static void
+ngx_http_spdy_handle_connection_handler(ngx_event_t *rev)
+{
+ ngx_connection_t *c;
+
+ rev->handler = ngx_http_spdy_read_handler;
+
+ if (rev->ready) {
+ ngx_http_spdy_read_handler(rev);
+ return;
+ }
+
+ c = rev->data;
+
+ ngx_http_spdy_handle_connection(c->data);
+}
+
+
+static void
+ngx_http_spdy_keepalive_handler(ngx_event_t *rev)
+{
+ ngx_connection_t *c;
+ ngx_http_spdy_srv_conf_t *sscf;
+ ngx_http_spdy_connection_t *sc;
+
+ c = rev->data;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "spdy keepalive handler");
+
+ if (rev->timedout || c->close) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+#if (NGX_HAVE_KQUEUE)
+
+ if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
+ if (rev->pending_eof) {
+ c->log->handler = NULL;
+ ngx_log_error(NGX_LOG_INFO, c->log, rev->kq_errno,
+ "kevent() reported that client %V closed "
+ "keepalive connection", &c->addr_text);
+#if (NGX_HTTP_SSL)
+ if (c->ssl) {
+ c->ssl->no_send_shutdown = 1;
+ }
+#endif
+ ngx_http_close_connection(c);
+ return;
+ }
+ }
+
+#endif
+
+ c->destroyed = 0;
+ c->idle = 0;
+ ngx_reusable_connection(c, 0);
+
+ sc = c->data;
+
+ sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx,
+ ngx_http_spdy_module);
+
+ sc->pool = ngx_create_pool(sscf->pool_size, sc->connection->log);
+ if (sc->pool == NULL) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ sc->streams_index = ngx_pcalloc(sc->pool,
+ ngx_http_spdy_streams_index_size(sscf)
+ * sizeof(ngx_http_spdy_stream_t *));
+ if (sc->streams_index == NULL) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ c->write->handler = ngx_http_spdy_write_handler;
+
+ rev->handler = ngx_http_spdy_read_handler;
+ ngx_http_spdy_read_handler(rev);
+}
+
+
+static void
+ngx_http_spdy_finalize_connection(ngx_http_spdy_connection_t *sc,
+ ngx_int_t rc)
+{
+ ngx_uint_t i, size;
+ ngx_event_t *ev;
+ ngx_connection_t *c, *fc;
+ ngx_http_request_t *r;
+ ngx_http_spdy_stream_t *stream;
+ ngx_http_spdy_srv_conf_t *sscf;
+
+ c = sc->connection;
+
+ if (!sc->processing) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ c->error = 1;
+ c->read->handler = ngx_http_empty_handler;
+
+ sc->last_out = NULL;
+
+ sc->blocked = 1;
+
+ sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx,
+ ngx_http_spdy_module);
+
+ size = ngx_http_spdy_streams_index_size(sscf);
+
+ for (i = 0; i < size; i++) {
+ stream = sc->streams_index[i];
+
+ while (stream) {
+ r = stream->request;
+
+ fc = r->connection;
+ fc->error = 1;
+
+ if (stream->waiting) {
+ r->blocked -= stream->waiting;
+ stream->waiting = 0;
+ ev = fc->write;
+
+ } else {
+ ev = fc->read;
+ }
+
+ stream = stream->index;
+
+ ev->eof = 1;
+ ev->handler(ev);
+ }
+ }
+
+ sc->blocked = 0;
+
+ if (sc->processing) {
+ return;
+ }
+
+ ngx_http_close_connection(c);
+}
+
+
+static void
+ngx_http_spdy_pool_cleanup(void *data)
+{
+ ngx_http_spdy_connection_t *sc = data;
+
+ if (sc->pool) {
+ ngx_destroy_pool(sc->pool);
+ }
+}
+
+
+static void *
+ngx_http_spdy_zalloc(void *opaque, u_int items, u_int size)
+{
+ ngx_http_spdy_connection_t *sc = opaque;
+
+ return ngx_palloc(sc->connection->pool, items * size);
+}
+
+
+static void
+ngx_http_spdy_zfree(void *opaque, void *address)
+{
+#if 0
+ ngx_http_spdy_connection_t *sc = opaque;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
+ "spdy zfree: %p", address);
+#endif
+}
diff --git a/usr.sbin/nginx/src/http/ngx_http_spdy.h b/usr.sbin/nginx/src/http/ngx_http_spdy.h
new file mode 100644
index 00000000000..4294e3d5078
--- /dev/null
+++ b/usr.sbin/nginx/src/http/ngx_http_spdy.h
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) Nginx, Inc.
+ * Copyright (C) Valentin V. Bartenev
+ */
+
+
+#ifndef _NGX_HTTP_SPDY_H_INCLUDED_
+#define _NGX_HTTP_SPDY_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+#include <zlib.h>
+
+
+#define NGX_SPDY_VERSION 2
+
+#ifdef TLSEXT_TYPE_next_proto_neg
+#define NGX_SPDY_NPN_ADVERTISE "\x06spdy/2"
+#define NGX_SPDY_NPN_NEGOTIATED "spdy/2"
+#endif
+
+#define NGX_SPDY_STATE_BUFFER_SIZE 16
+
+#define NGX_SPDY_CTL_BIT 1
+
+#define NGX_SPDY_SYN_STREAM 1
+#define NGX_SPDY_SYN_REPLY 2
+#define NGX_SPDY_RST_STREAM 3
+#define NGX_SPDY_SETTINGS 4
+#define NGX_SPDY_NOOP 5
+#define NGX_SPDY_PING 6
+#define NGX_SPDY_GOAWAY 7
+#define NGX_SPDY_HEADERS 8
+
+#define NGX_SPDY_FRAME_HEADER_SIZE 8
+
+#define NGX_SPDY_SID_SIZE 4
+
+#define NGX_SPDY_SYN_STREAM_SIZE 10
+#define NGX_SPDY_SYN_REPLY_SIZE 6
+#define NGX_SPDY_RST_STREAM_SIZE 8
+#define NGX_SPDY_PING_SIZE 4
+#define NGX_SPDY_GOAWAY_SIZE 4
+#define NGX_SPDY_NV_NUM_SIZE 2
+#define NGX_SPDY_NV_NLEN_SIZE 2
+#define NGX_SPDY_NV_VLEN_SIZE 2
+#define NGX_SPDY_SETTINGS_NUM_SIZE 4
+#define NGX_SPDY_SETTINGS_IDF_SIZE 4
+#define NGX_SPDY_SETTINGS_VAL_SIZE 4
+
+#define NGX_SPDY_SETTINGS_PAIR_SIZE \
+ (NGX_SPDY_SETTINGS_IDF_SIZE + NGX_SPDY_SETTINGS_VAL_SIZE)
+
+#define NGX_SPDY_HIGHEST_PRIORITY 0
+#define NGX_SPDY_LOWEST_PRIORITY 3
+
+#define NGX_SPDY_FLAG_FIN 0x01
+#define NGX_SPDY_FLAG_UNIDIRECTIONAL 0x02
+#define NGX_SPDY_FLAG_CLEAR_SETTINGS 0x01
+
+#define NGX_SPDY_MAX_FRAME_SIZE ((1 << 24) - 1)
+
+#define NGX_SPDY_DATA_DISCARD 1
+#define NGX_SPDY_DATA_ERROR 2
+#define NGX_SPDY_DATA_INTERNAL_ERROR 3
+
+
+typedef struct ngx_http_spdy_connection_s ngx_http_spdy_connection_t;
+typedef struct ngx_http_spdy_out_frame_s ngx_http_spdy_out_frame_t;
+
+
+typedef u_char *(*ngx_http_spdy_handler_pt) (ngx_http_spdy_connection_t *sc,
+ u_char *pos, u_char *end);
+
+struct ngx_http_spdy_connection_s {
+ ngx_connection_t *connection;
+ ngx_http_connection_t *http_connection;
+
+ ngx_uint_t processing;
+
+ u_char buffer[NGX_SPDY_STATE_BUFFER_SIZE];
+ size_t buffer_used;
+ ngx_http_spdy_handler_pt handler;
+
+ z_stream zstream_in;
+ z_stream zstream_out;
+
+ ngx_pool_t *pool;
+
+ ngx_http_spdy_out_frame_t *free_ctl_frames;
+ ngx_connection_t *free_fake_connections;
+
+ ngx_http_spdy_stream_t **streams_index;
+
+ ngx_http_spdy_out_frame_t *last_out;
+ ngx_http_spdy_stream_t *last_stream;
+
+ ngx_http_spdy_stream_t *stream;
+
+ ngx_uint_t headers;
+ size_t length;
+ u_char flags;
+
+ ngx_uint_t last_sid;
+
+ unsigned blocked:2;
+ unsigned waiting:1; /* FIXME better name */
+};
+
+
+struct ngx_http_spdy_stream_s {
+ ngx_uint_t id;
+ ngx_http_request_t *request;
+ ngx_http_spdy_connection_t *connection;
+ ngx_http_spdy_stream_t *index;
+ ngx_http_spdy_stream_t *next;
+
+ ngx_uint_t header_buffers;
+ ngx_uint_t waiting;
+ ngx_http_spdy_out_frame_t *free_frames;
+ ngx_chain_t *free_data_headers;
+
+ unsigned priority:2;
+ unsigned handled:1;
+ unsigned in_closed:1;
+ unsigned out_closed:1;
+ unsigned skip_data:2;
+};
+
+
+struct ngx_http_spdy_out_frame_s {
+ ngx_http_spdy_out_frame_t *next;
+ ngx_chain_t *first;
+ ngx_chain_t *last;
+ ngx_int_t (*handler)(ngx_http_spdy_connection_t *sc,
+ ngx_http_spdy_out_frame_t *frame);
+
+ ngx_http_spdy_out_frame_t *free;
+
+ ngx_http_spdy_stream_t *stream;
+ size_t size;
+
+ ngx_uint_t priority;
+ unsigned blocked:1;
+ unsigned fin:1;
+};
+
+
+static ngx_inline void
+ngx_http_spdy_queue_frame(ngx_http_spdy_connection_t *sc,
+ ngx_http_spdy_out_frame_t *frame)
+{
+ ngx_http_spdy_out_frame_t **out;
+
+ for (out = &sc->last_out; *out; out = &(*out)->next)
+ {
+ if (frame->priority >= (*out)->priority) {
+ break;
+ }
+ }
+
+ frame->next = *out;
+ *out = frame;
+}
+
+
+static ngx_inline void
+ngx_http_spdy_queue_blocked_frame(ngx_http_spdy_connection_t *sc,
+ ngx_http_spdy_out_frame_t *frame)
+{
+ ngx_http_spdy_out_frame_t **out;
+
+ for (out = &sc->last_out; *out && !(*out)->blocked; out = &(*out)->next)
+ {
+ if (frame->priority >= (*out)->priority) {
+ break;
+ }
+ }
+
+ frame->next = *out;
+ *out = frame;
+}
+
+
+void ngx_http_spdy_init(ngx_event_t *rev);
+void ngx_http_spdy_request_headers_init();
+
+ngx_int_t ngx_http_spdy_read_request_body(ngx_http_request_t *r,
+ ngx_http_client_body_handler_pt post_handler);
+
+void ngx_http_spdy_close_stream(ngx_http_spdy_stream_t *stream, ngx_int_t rc);
+
+ngx_int_t ngx_http_spdy_send_output_queue(ngx_http_spdy_connection_t *sc);
+
+
+#define ngx_spdy_frame_aligned_write_uint16(p, s) \
+ (*(uint16_t *) (p) = htons((uint16_t) (s)), (p) + sizeof(uint16_t))
+
+#define ngx_spdy_frame_aligned_write_uint32(p, s) \
+ (*(uint32_t *) (p) = htonl((uint32_t) (s)), (p) + sizeof(uint32_t))
+
+#if (NGX_HAVE_NONALIGNED)
+
+#define ngx_spdy_frame_write_uint16 ngx_spdy_frame_aligned_write_uint16
+#define ngx_spdy_frame_write_uint32 ngx_spdy_frame_aligned_write_uint32
+
+#else
+
+#define ngx_spdy_frame_write_uint16(p, s) \
+ ((p)[0] = (u_char) (s) >> 8, (p)[1] = (u_char) (s), (p) + sizeof(uint16_t))
+
+#define ngx_spdy_frame_write_uint32(p, s) \
+ ((p)[0] = (u_char) (s) >> 24, \
+ (p)[1] = (u_char) (s) >> 16, \
+ (p)[2] = (u_char) (s) >> 8, \
+ (p)[3] = (u_char) (s), (p) + sizeof(uint32_t))
+
+#endif
+
+
+#define ngx_spdy_ctl_frame_head(t) \
+ ((uint32_t) NGX_SPDY_CTL_BIT << 31 | NGX_SPDY_VERSION << 16 | (t))
+
+#define ngx_spdy_frame_write_head(p, t) \
+ ngx_spdy_frame_aligned_write_uint32(p, ngx_spdy_ctl_frame_head(t))
+
+#define ngx_spdy_frame_write_flags_and_len(p, f, l) \
+ ngx_spdy_frame_aligned_write_uint32(p, (f) << 24 | (l))
+
+#define ngx_spdy_frame_write_sid ngx_spdy_frame_aligned_write_uint32
+
+#endif /* _NGX_HTTP_SPDY_H_INCLUDED_ */
diff --git a/usr.sbin/nginx/src/http/ngx_http_spdy_filter_module.c b/usr.sbin/nginx/src/http/ngx_http_spdy_filter_module.c
new file mode 100644
index 00000000000..8fe46b2e954
--- /dev/null
+++ b/usr.sbin/nginx/src/http/ngx_http_spdy_filter_module.c
@@ -0,0 +1,999 @@
+
+/*
+ * Copyright (C) Nginx, Inc.
+ * Copyright (C) Valentin V. Bartenev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+#include <nginx.h>
+#include <ngx_http_spdy_module.h>
+
+#include <zlib.h>
+
+
+#define NGX_SPDY_WRITE_BUFFERED NGX_HTTP_WRITE_BUFFERED
+
+#define ngx_http_spdy_nv_nsize(h) (NGX_SPDY_NV_NLEN_SIZE + sizeof(h) - 1)
+#define ngx_http_spdy_nv_vsize(h) (NGX_SPDY_NV_VLEN_SIZE + sizeof(h) - 1)
+
+#define ngx_http_spdy_nv_write_num ngx_spdy_frame_write_uint16
+#define ngx_http_spdy_nv_write_nlen ngx_spdy_frame_write_uint16
+#define ngx_http_spdy_nv_write_vlen ngx_spdy_frame_write_uint16
+
+#define ngx_http_spdy_nv_write_name(p, h) \
+ ngx_cpymem(ngx_http_spdy_nv_write_nlen(p, sizeof(h) - 1), h, sizeof(h) - 1)
+
+#define ngx_http_spdy_nv_write_val(p, h) \
+ ngx_cpymem(ngx_http_spdy_nv_write_vlen(p, sizeof(h) - 1), h, sizeof(h) - 1)
+
+static ngx_inline ngx_int_t ngx_http_spdy_filter_send(
+ ngx_connection_t *fc, ngx_http_spdy_stream_t *stream);
+
+static ngx_http_spdy_out_frame_t *ngx_http_spdy_filter_get_data_frame(
+ ngx_http_spdy_stream_t *stream, size_t len, ngx_uint_t flags,
+ ngx_chain_t *first, ngx_chain_t *last);
+
+static ngx_int_t ngx_http_spdy_syn_frame_handler(
+ ngx_http_spdy_connection_t *sc, ngx_http_spdy_out_frame_t *frame);
+static ngx_int_t ngx_http_spdy_data_frame_handler(
+ ngx_http_spdy_connection_t *sc, ngx_http_spdy_out_frame_t *frame);
+static ngx_inline void ngx_http_spdy_handle_frame(
+ ngx_http_spdy_stream_t *stream, ngx_http_spdy_out_frame_t *frame);
+static ngx_inline void ngx_http_spdy_handle_stream(
+ ngx_http_spdy_connection_t *sc, ngx_http_spdy_stream_t *stream);
+
+static void ngx_http_spdy_filter_cleanup(void *data);
+
+static ngx_int_t ngx_http_spdy_filter_init(ngx_conf_t *cf);
+
+
+static ngx_http_module_t ngx_http_spdy_filter_module_ctx = {
+ NULL, /* preconfiguration */
+ ngx_http_spdy_filter_init, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ NULL, /* create location configuration */
+ NULL /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_spdy_filter_module = {
+ NGX_MODULE_V1,
+ &ngx_http_spdy_filter_module_ctx, /* module context */
+ NULL, /* 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_http_output_header_filter_pt ngx_http_next_header_filter;
+static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
+
+
+static ngx_int_t
+ngx_http_spdy_header_filter(ngx_http_request_t *r)
+{
+ int rc;
+ size_t len;
+ u_char *p, *buf, *last;
+ ngx_buf_t *b;
+ ngx_str_t host;
+ ngx_uint_t i, j, count, port;
+ ngx_chain_t *cl;
+ ngx_list_part_t *part, *pt;
+ ngx_table_elt_t *header, *h;
+ ngx_connection_t *c;
+ ngx_http_cleanup_t *cln;
+ ngx_http_core_loc_conf_t *clcf;
+ ngx_http_core_srv_conf_t *cscf;
+ ngx_http_spdy_stream_t *stream;
+ ngx_http_spdy_out_frame_t *frame;
+ ngx_http_spdy_connection_t *sc;
+ struct sockaddr_in *sin;
+#if (NGX_HAVE_INET6)
+ struct sockaddr_in6 *sin6;
+#endif
+ u_char addr[NGX_SOCKADDR_STRLEN];
+
+ if (!r->spdy_stream) {
+ return ngx_http_next_header_filter(r);
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "spdy header filter");
+
+ if (r->header_sent) {
+ return NGX_OK;
+ }
+
+ r->header_sent = 1;
+
+ if (r != r->main) {
+ return NGX_OK;
+ }
+
+ c = r->connection;
+
+ if (r->method == NGX_HTTP_HEAD) {
+ r->header_only = 1;
+ }
+
+ switch (r->headers_out.status) {
+
+ case NGX_HTTP_OK:
+ case NGX_HTTP_PARTIAL_CONTENT:
+ break;
+
+ case NGX_HTTP_NOT_MODIFIED:
+ r->header_only = 1;
+ break;
+
+ case NGX_HTTP_NO_CONTENT:
+ r->header_only = 1;
+
+ ngx_str_null(&r->headers_out.content_type);
+
+ r->headers_out.content_length = NULL;
+ r->headers_out.content_length_n = -1;
+
+ /* fall through */
+
+ default:
+ r->headers_out.last_modified_time = -1;
+ r->headers_out.last_modified = NULL;
+ }
+
+ len = NGX_SPDY_NV_NUM_SIZE
+ + ngx_http_spdy_nv_nsize("version")
+ + ngx_http_spdy_nv_vsize("HTTP/1.1")
+ + ngx_http_spdy_nv_nsize("status")
+ + ngx_http_spdy_nv_vsize("418");
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (r->headers_out.server == NULL) {
+ len += ngx_http_spdy_nv_nsize("server");
+ len += clcf->server_tokens ? ngx_http_spdy_nv_vsize(NGINX_VER)
+ : ngx_http_spdy_nv_vsize("nginx");
+ }
+
+ if (r->headers_out.date == NULL) {
+ len += ngx_http_spdy_nv_nsize("date")
+ + ngx_http_spdy_nv_vsize("Wed, 31 Dec 1986 10:00:00 GMT");
+ }
+
+ if (r->headers_out.content_type.len) {
+ len += ngx_http_spdy_nv_nsize("content-type")
+ + NGX_SPDY_NV_VLEN_SIZE + r->headers_out.content_type.len;
+
+ if (r->headers_out.content_type_len == r->headers_out.content_type.len
+ && r->headers_out.charset.len)
+ {
+ len += sizeof("; charset=") - 1 + r->headers_out.charset.len;
+ }
+ }
+
+ if (r->headers_out.content_length == NULL
+ && r->headers_out.content_length_n >= 0)
+ {
+ len += ngx_http_spdy_nv_nsize("content-length")
+ + NGX_SPDY_NV_VLEN_SIZE + NGX_OFF_T_LEN;
+ }
+
+ if (r->headers_out.last_modified == NULL
+ && r->headers_out.last_modified_time != -1)
+ {
+ len += ngx_http_spdy_nv_nsize("last-modified")
+ + ngx_http_spdy_nv_vsize("Wed, 31 Dec 1986 10:00:00 GMT");
+ }
+
+ if (r->headers_out.location
+ && r->headers_out.location->value.len
+ && r->headers_out.location->value.data[0] == '/')
+ {
+ r->headers_out.location->hash = 0;
+
+ if (clcf->server_name_in_redirect) {
+ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
+ host = cscf->server_name;
+
+ } else if (r->headers_in.server.len) {
+ host = r->headers_in.server;
+
+ } else {
+ host.len = NGX_SOCKADDR_STRLEN;
+ host.data = addr;
+
+ if (ngx_connection_local_sockaddr(c, &host, 0) != NGX_OK) {
+ return NGX_ERROR;
+ }
+ }
+
+ switch (c->local_sockaddr->sa_family) {
+
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *) c->local_sockaddr;
+ port = ntohs(sin6->sin6_port);
+ break;
+#endif
+#if (NGX_HAVE_UNIX_DOMAIN)
+ case AF_UNIX:
+ port = 0;
+ break;
+#endif
+ default: /* AF_INET */
+ sin = (struct sockaddr_in *) c->local_sockaddr;
+ port = ntohs(sin->sin_port);
+ break;
+ }
+
+ len += ngx_http_spdy_nv_nsize("location")
+ + ngx_http_spdy_nv_vsize("https://")
+ + host.len
+ + r->headers_out.location->value.len;
+
+ if (clcf->port_in_redirect) {
+
+#if (NGX_HTTP_SSL)
+ if (c->ssl)
+ port = (port == 443) ? 0 : port;
+ else
+#endif
+ port = (port == 80) ? 0 : port;
+
+ } else {
+ port = 0;
+ }
+
+ if (port) {
+ len += sizeof(":65535") - 1;
+ }
+
+ } else {
+ ngx_str_null(&host);
+ port = 0;
+ }
+
+ part = &r->headers_out.headers.part;
+ header = part->elts;
+
+ for (i = 0; /* void */; i++) {
+
+ if (i >= part->nelts) {
+ if (part->next == NULL) {
+ break;
+ }
+
+ part = part->next;
+ header = part->elts;
+ i = 0;
+ }
+
+ if (header[i].hash == 0) {
+ continue;
+ }
+
+ len += NGX_SPDY_NV_NLEN_SIZE + header[i].key.len
+ + NGX_SPDY_NV_VLEN_SIZE + header[i].value.len;
+ }
+
+ buf = ngx_alloc(len, r->pool->log);
+ if (buf == NULL) {
+ return NGX_ERROR;
+ }
+
+ last = buf + NGX_SPDY_NV_NUM_SIZE;
+
+ last = ngx_http_spdy_nv_write_name(last, "version");
+ last = ngx_http_spdy_nv_write_val(last, "HTTP/1.1");
+
+ last = ngx_http_spdy_nv_write_name(last, "status");
+ last = ngx_spdy_frame_write_uint16(last, 3);
+ last = ngx_sprintf(last, "%03ui", r->headers_out.status);
+
+ count = 2;
+
+ if (r->headers_out.server == NULL) {
+ last = ngx_http_spdy_nv_write_name(last, "server");
+ last = clcf->server_tokens
+ ? ngx_http_spdy_nv_write_val(last, NGINX_VER)
+ : ngx_http_spdy_nv_write_val(last, "nginx");
+
+ count++;
+ }
+
+ if (r->headers_out.date == NULL) {
+ last = ngx_http_spdy_nv_write_name(last, "date");
+
+ last = ngx_http_spdy_nv_write_vlen(last, ngx_cached_http_time.len);
+
+ last = ngx_cpymem(last, ngx_cached_http_time.data,
+ ngx_cached_http_time.len);
+
+ count++;
+ }
+
+ if (r->headers_out.content_type.len) {
+
+ last = ngx_http_spdy_nv_write_name(last, "content-type");
+
+ p = last + NGX_SPDY_NV_VLEN_SIZE;
+
+ last = ngx_cpymem(p, r->headers_out.content_type.data,
+ r->headers_out.content_type.len);
+
+ if (r->headers_out.content_type_len == r->headers_out.content_type.len
+ && r->headers_out.charset.len)
+ {
+ last = ngx_cpymem(last, "; charset=", sizeof("; charset=") - 1);
+
+ last = ngx_cpymem(last, r->headers_out.charset.data,
+ r->headers_out.charset.len);
+
+ /* update r->headers_out.content_type for possible logging */
+
+ r->headers_out.content_type.len = last - p;
+ r->headers_out.content_type.data = p;
+ }
+
+ (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE,
+ r->headers_out.content_type.len);
+
+ count++;
+ }
+
+ if (r->headers_out.content_length == NULL
+ && r->headers_out.content_length_n >= 0)
+ {
+ last = ngx_http_spdy_nv_write_name(last, "content-length");
+
+ p = last + NGX_SPDY_NV_VLEN_SIZE;
+
+ last = ngx_sprintf(p, "%O", r->headers_out.content_length_n);
+
+ (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE,
+ last - p);
+
+ count++;
+ }
+
+ if (r->headers_out.last_modified == NULL
+ && r->headers_out.last_modified_time != -1)
+ {
+ last = ngx_http_spdy_nv_write_name(last, "last-modified");
+
+ p = last + NGX_SPDY_NV_VLEN_SIZE;
+
+ last = ngx_http_time(p, r->headers_out.last_modified_time);
+
+ (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE,
+ last - p);
+
+ count++;
+ }
+
+ if (host.data) {
+
+ last = ngx_http_spdy_nv_write_name(last, "location");
+
+ p = last + NGX_SPDY_NV_VLEN_SIZE;
+
+ last = ngx_cpymem(p, "http", sizeof("http") - 1);
+
+#if (NGX_HTTP_SSL)
+ if (c->ssl) {
+ *last++ ='s';
+ }
+#endif
+
+ *last++ = ':'; *last++ = '/'; *last++ = '/';
+
+ last = ngx_cpymem(last, host.data, host.len);
+
+ if (port) {
+ last = ngx_sprintf(last, ":%ui", port);
+ }
+
+ last = ngx_cpymem(last, r->headers_out.location->value.data,
+ r->headers_out.location->value.len);
+
+ /* update r->headers_out.location->value for possible logging */
+
+ r->headers_out.location->value.len = last - p;
+ r->headers_out.location->value.data = p;
+ ngx_str_set(&r->headers_out.location->key, "location");
+
+ (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE,
+ r->headers_out.location->value.len);
+
+ count++;
+ }
+
+ part = &r->headers_out.headers.part;
+ header = part->elts;
+
+ for (i = 0; /* void */; i++) {
+
+ if (i >= part->nelts) {
+ if (part->next == NULL) {
+ break;
+ }
+
+ part = part->next;
+ header = part->elts;
+ i = 0;
+ }
+
+ if (header[i].hash == 0 || header[i].hash == 2) {
+ continue;
+ }
+
+ if ((header[i].key.len == 6
+ && ngx_strncasecmp(header[i].key.data,
+ (u_char *) "status", 6) == 0)
+ || (header[i].key.len == 7
+ && ngx_strncasecmp(header[i].key.data,
+ (u_char *) "version", 7) == 0))
+ {
+ header[i].hash = 0;
+ continue;
+ }
+
+ last = ngx_http_spdy_nv_write_nlen(last, header[i].key.len);
+
+ ngx_strlow(last, header[i].key.data, header[i].key.len);
+ last += header[i].key.len;
+
+ p = last + NGX_SPDY_NV_VLEN_SIZE;
+
+ last = ngx_cpymem(p, header[i].value.data, header[i].value.len);
+
+ pt = part;
+ h = header;
+
+ for (j = i + 1; /* void */; j++) {
+
+ if (j >= pt->nelts) {
+ if (pt->next == NULL) {
+ break;
+ }
+
+ pt = pt->next;
+ h = pt->elts;
+ j = 0;
+ }
+
+ if (h[j].hash == 0 || h[j].hash == 2
+ || h[j].key.len != header[i].key.len
+ || ngx_strncasecmp(header[i].key.data, h[j].key.data,
+ header[i].key.len))
+ {
+ continue;
+ }
+
+ *last++ = '\0';
+
+ last = ngx_cpymem(last, h[j].value.data, h[j].value.len);
+
+ h[j].hash = 2;
+ }
+
+ (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE,
+ last - p);
+
+ count++;
+ }
+
+ (void) ngx_spdy_frame_write_uint16(buf, count);
+
+ stream = r->spdy_stream;
+ sc = stream->connection;
+
+ len = last - buf;
+
+ b = ngx_create_temp_buf(r->pool, NGX_SPDY_FRAME_HEADER_SIZE
+ + NGX_SPDY_SYN_REPLY_SIZE
+ + deflateBound(&sc->zstream_out, len));
+ if (b == NULL) {
+ ngx_free(buf);
+ return NGX_ERROR;
+ }
+
+ b->last += NGX_SPDY_FRAME_HEADER_SIZE + NGX_SPDY_SYN_REPLY_SIZE;
+
+ sc->zstream_out.next_in = buf;
+ sc->zstream_out.avail_in = len;
+ sc->zstream_out.next_out = b->last;
+ sc->zstream_out.avail_out = b->end - b->last;
+
+ rc = deflate(&sc->zstream_out, Z_SYNC_FLUSH);
+
+ ngx_free(buf);
+
+ if (rc != Z_OK) {
+ ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+ "spdy deflate() failed: %d", rc);
+ return NGX_ERROR;
+ }
+
+ ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "spdy deflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d",
+ sc->zstream_out.next_in, sc->zstream_out.next_out,
+ sc->zstream_out.avail_in, sc->zstream_out.avail_out,
+ rc);
+
+ b->last = sc->zstream_out.next_out;
+
+ p = b->pos;
+ p = ngx_spdy_frame_write_head(p, NGX_SPDY_SYN_REPLY);
+
+ len = b->last - b->pos;
+
+ r->header_size = len;
+
+ if (r->header_only) {
+ b->last_buf = 1;
+ p = ngx_spdy_frame_write_flags_and_len(p, NGX_SPDY_FLAG_FIN,
+ len - NGX_SPDY_FRAME_HEADER_SIZE);
+ } else {
+ p = ngx_spdy_frame_write_flags_and_len(p, 0,
+ len - NGX_SPDY_FRAME_HEADER_SIZE);
+ }
+
+ (void) ngx_spdy_frame_write_sid(p, stream->id);
+
+ cl = ngx_alloc_chain_link(r->pool);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl->buf = b;
+ cl->next = NULL;
+
+ frame = ngx_palloc(r->pool, sizeof(ngx_http_spdy_out_frame_t));
+ if (frame == NULL) {
+ return NGX_ERROR;
+ }
+
+ frame->first = cl;
+ frame->last = cl;
+ frame->handler = ngx_http_spdy_syn_frame_handler;
+ frame->free = NULL;
+ frame->stream = stream;
+ frame->size = len;
+ frame->priority = stream->priority;
+ frame->blocked = 1;
+ frame->fin = r->header_only;
+
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, stream->request->connection->log, 0,
+ "spdy:%ui create SYN_REPLY frame %p: size:%uz",
+ stream->id, frame, frame->size);
+
+ ngx_http_spdy_queue_blocked_frame(sc, frame);
+
+ r->blocked++;
+
+ cln = ngx_http_cleanup_add(r, 0);
+ if (cln == NULL) {
+ return NGX_ERROR;
+ }
+
+ cln->handler = ngx_http_spdy_filter_cleanup;
+ cln->data = stream;
+
+ stream->waiting = 1;
+
+ return ngx_http_spdy_filter_send(c, stream);
+}
+
+
+static ngx_int_t
+ngx_http_spdy_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
+{
+ off_t size;
+ ngx_buf_t *b;
+ ngx_chain_t *cl, *ll, *out, **ln;
+ ngx_http_spdy_stream_t *stream;
+ ngx_http_spdy_out_frame_t *frame;
+
+ stream = r->spdy_stream;
+
+ if (stream == NULL) {
+ return ngx_http_next_body_filter(r, in);
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "spdy body filter \"%V?%V\"", &r->uri, &r->args);
+
+ if (in == NULL || r->header_only) {
+
+ if (stream->waiting) {
+ return NGX_AGAIN;
+ }
+
+ r->connection->buffered &= ~NGX_SPDY_WRITE_BUFFERED;
+
+ return NGX_OK;
+ }
+
+ size = 0;
+ ln = &out;
+ ll = in;
+
+ for ( ;; ) {
+ b = ll->buf;
+#if 1
+ if (ngx_buf_size(b) == 0 && !ngx_buf_special(b)) {
+ ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+ "zero size buf in spdy body filter "
+ "t:%d r:%d f:%d %p %p-%p %p %O-%O",
+ b->temporary,
+ b->recycled,
+ b->in_file,
+ b->start,
+ b->pos,
+ b->last,
+ b->file,
+ b->file_pos,
+ b->file_last);
+
+ ngx_debug_point();
+ return NGX_ERROR;
+ }
+#endif
+ cl = ngx_alloc_chain_link(r->pool);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ size += ngx_buf_size(b);
+ cl->buf = b;
+
+ *ln = cl;
+ ln = &cl->next;
+
+ if (ll->next == NULL) {
+ break;
+ }
+
+ ll = ll->next;
+ }
+
+ if (size > NGX_SPDY_MAX_FRAME_SIZE) {
+ ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+ "FIXME: chain too big in spdy filter: %O", size);
+ return NGX_ERROR;
+ }
+
+ frame = ngx_http_spdy_filter_get_data_frame(stream, (size_t) size,
+ b->last_buf, out, cl);
+ if (frame == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_http_spdy_queue_frame(stream->connection, frame);
+
+ stream->waiting++;
+
+ r->main->blocked++;
+
+ return ngx_http_spdy_filter_send(r->connection, stream);
+}
+
+
+static ngx_http_spdy_out_frame_t *
+ngx_http_spdy_filter_get_data_frame(ngx_http_spdy_stream_t *stream,
+ size_t len, ngx_uint_t fin, ngx_chain_t *first, ngx_chain_t *last)
+{
+ u_char *p;
+ ngx_buf_t *buf;
+ ngx_uint_t flags;
+ ngx_chain_t *cl;
+ ngx_http_spdy_out_frame_t *frame;
+
+
+ frame = stream->free_frames;
+
+ if (frame) {
+ stream->free_frames = frame->free;
+
+ } else {
+ frame = ngx_palloc(stream->request->pool,
+ sizeof(ngx_http_spdy_out_frame_t));
+ if (frame == NULL) {
+ return NULL;
+ }
+ }
+
+ ngx_log_debug4(NGX_LOG_DEBUG_HTTP, stream->request->connection->log, 0,
+ "spdy:%ui create DATA frame %p: len:%uz fin:%ui",
+ stream->id, frame, len, fin);
+
+ if (len || fin) {
+
+ flags = fin ? NGX_SPDY_FLAG_FIN : 0;
+
+ cl = ngx_chain_get_free_buf(stream->request->pool,
+ &stream->free_data_headers);
+ if (cl == NULL) {
+ return NULL;
+ }
+
+ buf = cl->buf;
+
+ if (buf->start) {
+ p = buf->start;
+ buf->pos = p;
+
+ p += sizeof(uint32_t);
+
+ (void) ngx_spdy_frame_write_flags_and_len(p, flags, len);
+
+ } else {
+ p = ngx_palloc(stream->request->pool, NGX_SPDY_FRAME_HEADER_SIZE);
+ if (p == NULL) {
+ return NULL;
+ }
+
+ buf->pos = p;
+ buf->start = p;
+
+ p = ngx_spdy_frame_write_sid(p, stream->id);
+ p = ngx_spdy_frame_write_flags_and_len(p, flags, len);
+
+ buf->last = p;
+ buf->end = p;
+
+ buf->tag = (ngx_buf_tag_t) &ngx_http_spdy_filter_module;
+ buf->memory = 1;
+ }
+
+ cl->next = first;
+ first = cl;
+ }
+
+ frame->first = first;
+ frame->last = last;
+ frame->handler = ngx_http_spdy_data_frame_handler;
+ frame->free = NULL;
+ frame->stream = stream;
+ frame->size = NGX_SPDY_FRAME_HEADER_SIZE + len;
+ frame->priority = stream->priority;
+ frame->blocked = 0;
+ frame->fin = fin;
+
+ return frame;
+}
+
+
+static ngx_inline ngx_int_t
+ngx_http_spdy_filter_send(ngx_connection_t *fc, ngx_http_spdy_stream_t *stream)
+{
+ if (ngx_http_spdy_send_output_queue(stream->connection) == NGX_ERROR) {
+ fc->error = 1;
+ return NGX_ERROR;
+ }
+
+ if (stream->waiting) {
+ fc->buffered |= NGX_SPDY_WRITE_BUFFERED;
+ fc->write->delayed = 1;
+ return NGX_AGAIN;
+ }
+
+ fc->buffered &= ~NGX_SPDY_WRITE_BUFFERED;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_spdy_syn_frame_handler(ngx_http_spdy_connection_t *sc,
+ ngx_http_spdy_out_frame_t *frame)
+{
+ ngx_buf_t *buf;
+ ngx_http_spdy_stream_t *stream;
+
+ buf = frame->first->buf;
+
+ if (buf->pos != buf->last) {
+ return NGX_AGAIN;
+ }
+
+ stream = frame->stream;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
+ "spdy:%ui SYN_REPLY frame %p was sent", stream->id, frame);
+
+ ngx_free_chain(stream->request->pool, frame->first);
+
+ ngx_http_spdy_handle_frame(stream, frame);
+
+ ngx_http_spdy_handle_stream(sc, stream);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_spdy_data_frame_handler(ngx_http_spdy_connection_t *sc,
+ ngx_http_spdy_out_frame_t *frame)
+{
+ ngx_chain_t *cl, *ln;
+ ngx_http_spdy_stream_t *stream;
+
+ stream = frame->stream;
+
+ cl = frame->first;
+
+ if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_spdy_filter_module) {
+
+ if (cl->buf->pos != cl->buf->last) {
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
+ "spdy:%ui DATA frame %p was sent partially",
+ stream->id, frame);
+
+ return NGX_AGAIN;
+ }
+
+ ln = cl->next;
+
+ cl->next = stream->free_data_headers;
+ stream->free_data_headers = cl;
+
+ if (cl == frame->last) {
+ goto done;
+ }
+
+ cl = ln;
+ }
+
+ for ( ;; ) {
+ if (ngx_buf_size(cl->buf) != 0) {
+
+ if (cl != frame->first) {
+ frame->first = cl;
+ ngx_http_spdy_handle_stream(sc, stream);
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
+ "spdy:%ui DATA frame %p was sent partially",
+ stream->id, frame);
+
+ return NGX_AGAIN;
+ }
+
+ ln = cl->next;
+
+ ngx_free_chain(stream->request->pool, cl);
+
+ if (cl == frame->last) {
+ goto done;
+ }
+
+ cl = ln;
+ }
+
+done:
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
+ "spdy:%ui DATA frame %p was sent", stream->id, frame);
+
+ stream->request->header_size += NGX_SPDY_FRAME_HEADER_SIZE;
+
+ ngx_http_spdy_handle_frame(stream, frame);
+
+ ngx_http_spdy_handle_stream(sc, stream);
+
+ return NGX_OK;
+}
+
+
+static ngx_inline void
+ngx_http_spdy_handle_frame(ngx_http_spdy_stream_t *stream,
+ ngx_http_spdy_out_frame_t *frame)
+{
+ ngx_http_request_t *r;
+
+ r = stream->request;
+
+ r->connection->sent += frame->size;
+ r->blocked--;
+
+ if (frame->fin) {
+ stream->out_closed = 1;
+ }
+
+ frame->free = stream->free_frames;
+ stream->free_frames = frame;
+
+ stream->waiting--;
+}
+
+
+static ngx_inline void
+ngx_http_spdy_handle_stream(ngx_http_spdy_connection_t *sc,
+ ngx_http_spdy_stream_t *stream)
+{
+ ngx_connection_t *fc;
+
+ fc = stream->request->connection;
+
+ fc->write->delayed = 0;
+
+ if (stream->handled) {
+ return;
+ }
+
+ if (sc->blocked == 2) {
+ stream->handled = 1;
+
+ stream->next = sc->last_stream;
+ sc->last_stream = stream;
+ }
+}
+
+
+static void
+ngx_http_spdy_filter_cleanup(void *data)
+{
+ ngx_http_spdy_stream_t *stream = data;
+
+ ngx_http_request_t *r;
+ ngx_http_spdy_out_frame_t *frame, **fn;
+
+ if (stream->waiting == 0) {
+ return;
+ }
+
+ r = stream->request;
+
+ fn = &stream->connection->last_out;
+
+ for ( ;; ) {
+ frame = *fn;
+
+ if (frame == NULL) {
+ break;
+ }
+
+ if (frame->stream == stream && !frame->blocked) {
+
+ stream->waiting--;
+ r->blocked--;
+
+ *fn = frame->next;
+ continue;
+ }
+
+ fn = &frame->next;
+ }
+}
+
+
+static ngx_int_t
+ngx_http_spdy_filter_init(ngx_conf_t *cf)
+{
+ ngx_http_next_header_filter = ngx_http_top_header_filter;
+ ngx_http_top_header_filter = ngx_http_spdy_header_filter;
+
+ ngx_http_next_body_filter = ngx_http_top_body_filter;
+ ngx_http_top_body_filter = ngx_http_spdy_body_filter;
+
+ return NGX_OK;
+}
diff --git a/usr.sbin/nginx/src/http/ngx_http_spdy_module.c b/usr.sbin/nginx/src/http/ngx_http_spdy_module.c
new file mode 100644
index 00000000000..7f02a18ca0c
--- /dev/null
+++ b/usr.sbin/nginx/src/http/ngx_http_spdy_module.c
@@ -0,0 +1,351 @@
+
+/*
+ * Copyright (C) Nginx, Inc.
+ * Copyright (C) Valentin V. Bartenev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+#include <ngx_http_spdy_module.h>
+
+
+static ngx_int_t ngx_http_spdy_add_variables(ngx_conf_t *cf);
+
+static ngx_int_t ngx_http_spdy_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_spdy_request_priority_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+
+static ngx_int_t ngx_http_spdy_module_init(ngx_cycle_t *cycle);
+
+static void *ngx_http_spdy_create_main_conf(ngx_conf_t *cf);
+static char *ngx_http_spdy_init_main_conf(ngx_conf_t *cf, void *conf);
+
+static void *ngx_http_spdy_create_srv_conf(ngx_conf_t *cf);
+static char *ngx_http_spdy_merge_srv_conf(ngx_conf_t *cf, void *parent,
+ void *child);
+
+static char *ngx_http_spdy_recv_buffer_size(ngx_conf_t *cf, void *post,
+ void *data);
+static char *ngx_http_spdy_pool_size(ngx_conf_t *cf, void *post, void *data);
+static char *ngx_http_spdy_streams_index_mask(ngx_conf_t *cf, void *post,
+ void *data);
+
+
+static ngx_conf_num_bounds_t ngx_http_spdy_headers_comp_bounds = {
+ ngx_conf_check_num_bounds, 0, 9
+};
+
+static ngx_conf_post_t ngx_http_spdy_recv_buffer_size_post =
+ { ngx_http_spdy_recv_buffer_size };
+static ngx_conf_post_t ngx_http_spdy_pool_size_post =
+ { ngx_http_spdy_pool_size };
+static ngx_conf_post_t ngx_http_spdy_streams_index_mask_post =
+ { ngx_http_spdy_streams_index_mask };
+
+
+static ngx_command_t ngx_http_spdy_commands[] = {
+
+ { ngx_string("spdy_recv_buffer_size"),
+ NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_MAIN_CONF_OFFSET,
+ offsetof(ngx_http_spdy_main_conf_t, recv_buffer_size),
+ &ngx_http_spdy_recv_buffer_size_post },
+
+ { ngx_string("spdy_pool_size"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_spdy_srv_conf_t, pool_size),
+ &ngx_http_spdy_pool_size_post },
+
+ { ngx_string("spdy_max_concurrent_streams"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_spdy_srv_conf_t, concurrent_streams),
+ NULL },
+
+ { ngx_string("spdy_streams_index_size"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_spdy_srv_conf_t, streams_index_mask),
+ &ngx_http_spdy_streams_index_mask_post },
+
+ { ngx_string("spdy_recv_timeout"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_spdy_srv_conf_t, recv_timeout),
+ NULL },
+
+ { ngx_string("spdy_keepalive_timeout"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_spdy_srv_conf_t, keepalive_timeout),
+ NULL },
+
+ { ngx_string("spdy_headers_comp"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_spdy_srv_conf_t, headers_comp),
+ &ngx_http_spdy_headers_comp_bounds },
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_spdy_module_ctx = {
+ ngx_http_spdy_add_variables, /* preconfiguration */
+ NULL, /* postconfiguration */
+
+ ngx_http_spdy_create_main_conf, /* create main configuration */
+ ngx_http_spdy_init_main_conf, /* init main configuration */
+
+ ngx_http_spdy_create_srv_conf, /* create server configuration */
+ ngx_http_spdy_merge_srv_conf, /* merge server configuration */
+
+ NULL, /* create location configuration */
+ NULL /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_spdy_module = {
+ NGX_MODULE_V1,
+ &ngx_http_spdy_module_ctx, /* module context */
+ ngx_http_spdy_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ ngx_http_spdy_module_init, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_http_variable_t ngx_http_spdy_vars[] = {
+
+ { ngx_string("spdy"), NULL,
+ ngx_http_spdy_variable, 0, 0, 0 },
+
+ { ngx_string("spdy_request_priority"), NULL,
+ ngx_http_spdy_request_priority_variable, 0, 0, 0 },
+
+ { ngx_null_string, NULL, NULL, 0, 0, 0 }
+};
+
+
+static ngx_int_t
+ngx_http_spdy_add_variables(ngx_conf_t *cf)
+{
+ ngx_http_variable_t *var, *v;
+
+ for (v = ngx_http_spdy_vars; v->name.len; v++) {
+ var = ngx_http_add_variable(cf, &v->name, v->flags);
+ if (var == NULL) {
+ return NGX_ERROR;
+ }
+
+ var->get_handler = v->get_handler;
+ var->data = v->data;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_spdy_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ if (r->spdy_stream) {
+ v->len = 1;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = (u_char *) "2";
+
+ return NGX_OK;
+ }
+
+ *v = ngx_http_variable_null_value;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_spdy_request_priority_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ if (r->spdy_stream) {
+ v->len = 1;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ v->data = ngx_pnalloc(r->pool, 1);
+ if (v->data == NULL) {
+ return NGX_ERROR;
+ }
+
+ v->data[0] = '0' + (u_char) r->spdy_stream->priority;
+
+ return NGX_OK;
+ }
+
+ *v = ngx_http_variable_null_value;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_spdy_module_init(ngx_cycle_t *cycle)
+{
+ ngx_http_spdy_request_headers_init();
+
+ return NGX_OK;
+}
+
+
+static void *
+ngx_http_spdy_create_main_conf(ngx_conf_t *cf)
+{
+ ngx_http_spdy_main_conf_t *smcf;
+
+ smcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_spdy_main_conf_t));
+ if (smcf == NULL) {
+ return NULL;
+ }
+
+ smcf->recv_buffer_size = NGX_CONF_UNSET_SIZE;
+
+ return smcf;
+}
+
+
+static char *
+ngx_http_spdy_init_main_conf(ngx_conf_t *cf, void *conf)
+{
+ ngx_http_spdy_main_conf_t *smcf = conf;
+
+ if (smcf->recv_buffer_size == NGX_CONF_UNSET_SIZE) {
+ smcf->recv_buffer_size = 256 * 1024;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static void *
+ngx_http_spdy_create_srv_conf(ngx_conf_t *cf)
+{
+ ngx_http_spdy_srv_conf_t *sscf;
+
+ sscf = ngx_pcalloc(cf->pool, sizeof(ngx_http_spdy_srv_conf_t));
+ if (sscf == NULL) {
+ return NULL;
+ }
+
+ sscf->pool_size = NGX_CONF_UNSET_SIZE;
+
+ sscf->concurrent_streams = NGX_CONF_UNSET_UINT;
+ sscf->streams_index_mask = NGX_CONF_UNSET_UINT;
+
+ sscf->recv_timeout = NGX_CONF_UNSET_MSEC;
+ sscf->keepalive_timeout = NGX_CONF_UNSET_MSEC;
+
+ sscf->headers_comp = NGX_CONF_UNSET;
+
+ return sscf;
+}
+
+
+static char *
+ngx_http_spdy_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_http_spdy_srv_conf_t *prev = parent;
+ ngx_http_spdy_srv_conf_t *conf = child;
+
+ ngx_conf_merge_size_value(conf->pool_size, prev->pool_size, 4096);
+
+ ngx_conf_merge_uint_value(conf->concurrent_streams,
+ prev->concurrent_streams, 100);
+
+ ngx_conf_merge_uint_value(conf->streams_index_mask,
+ prev->streams_index_mask, 32 - 1);
+
+ ngx_conf_merge_msec_value(conf->recv_timeout,
+ prev->recv_timeout, 30000);
+ ngx_conf_merge_msec_value(conf->keepalive_timeout,
+ prev->keepalive_timeout, 180000);
+
+ ngx_conf_merge_value(conf->headers_comp, prev->headers_comp, 0);
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_spdy_recv_buffer_size(ngx_conf_t *cf, void *post, void *data)
+{
+ size_t *sp = data;
+
+ if (*sp <= 2 * NGX_SPDY_STATE_BUFFER_SIZE) {
+ return "value is too small";
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_spdy_pool_size(ngx_conf_t *cf, void *post, void *data)
+{
+ size_t *sp = data;
+
+ if (*sp < NGX_MIN_POOL_SIZE) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "the pool size must be no less than %uz",
+ NGX_MIN_POOL_SIZE);
+ return NGX_CONF_ERROR;
+ }
+
+ if (*sp % NGX_POOL_ALIGNMENT) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "the pool size must be a multiple of %uz",
+ NGX_POOL_ALIGNMENT);
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_spdy_streams_index_mask(ngx_conf_t *cf, void *post, void *data)
+{
+ ngx_uint_t *np = data;
+
+ ngx_uint_t mask;
+
+ mask = *np - 1;
+
+ if (*np == 0 || (*np & mask)) {
+ return "must be a power of two";
+ }
+
+ *np = mask;
+
+ return NGX_CONF_OK;
+}
diff --git a/usr.sbin/nginx/src/http/ngx_http_spdy_module.h b/usr.sbin/nginx/src/http/ngx_http_spdy_module.h
new file mode 100644
index 00000000000..97a7a13a6fc
--- /dev/null
+++ b/usr.sbin/nginx/src/http/ngx_http_spdy_module.h
@@ -0,0 +1,36 @@
+
+/*
+ * Copyright (C) Nginx, Inc.
+ * Copyright (C) Valentin V. Bartenev
+ */
+
+
+#ifndef _NGX_HTTP_SPDY_MODULE_H_INCLUDED_
+#define _NGX_HTTP_SPDY_MODULE_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct {
+ size_t recv_buffer_size;
+ u_char *recv_buffer;
+} ngx_http_spdy_main_conf_t;
+
+
+typedef struct {
+ size_t pool_size;
+ ngx_uint_t concurrent_streams;
+ ngx_uint_t streams_index_mask;
+ ngx_msec_t recv_timeout;
+ ngx_msec_t keepalive_timeout;
+ ngx_int_t headers_comp;
+} ngx_http_spdy_srv_conf_t;
+
+
+extern ngx_module_t ngx_http_spdy_module;
+
+
+#endif /* _NGX_HTTP_SPDY_MODULE_H_INCLUDED_ */
diff --git a/usr.sbin/nginx/src/http/ngx_http_special_response.c b/usr.sbin/nginx/src/http/ngx_http_special_response.c
index be495f9096c..875c24d9c75 100644
--- a/usr.sbin/nginx/src/http/ngx_http_special_response.c
+++ b/usr.sbin/nginx/src/http/ngx_http_special_response.c
@@ -421,7 +421,7 @@ ngx_http_special_response_handler(ngx_http_request_t *r, ngx_int_t error)
r->expect_tested = 1;
if (ngx_http_discard_request_body(r) != NGX_OK) {
- error = NGX_HTTP_INTERNAL_SERVER_ERROR;
+ r->keepalive = 0;
}
if (clcf->msie_refresh
@@ -657,6 +657,7 @@ ngx_http_send_special_response(ngx_http_request_t *r,
ngx_http_clear_accept_ranges(r);
ngx_http_clear_last_modified(r);
+ ngx_http_clear_etag(r);
rc = ngx_http_send_header(r);
@@ -755,6 +756,7 @@ ngx_http_send_refresh(ngx_http_request_t *r)
ngx_http_clear_accept_ranges(r);
ngx_http_clear_last_modified(r);
+ ngx_http_clear_etag(r);
rc = ngx_http_send_header(r);
diff --git a/usr.sbin/nginx/src/http/ngx_http_upstream.c b/usr.sbin/nginx/src/http/ngx_http_upstream.c
index dc1283a205a..45e2eb7b961 100644
--- a/usr.sbin/nginx/src/http/ngx_http_upstream.c
+++ b/usr.sbin/nginx/src/http/ngx_http_upstream.c
@@ -46,6 +46,16 @@ static void ngx_http_upstream_process_body_in_memory(ngx_http_request_t *r,
ngx_http_upstream_t *u);
static void ngx_http_upstream_send_response(ngx_http_request_t *r,
ngx_http_upstream_t *u);
+static void ngx_http_upstream_upgrade(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+static void ngx_http_upstream_upgraded_read_downstream(ngx_http_request_t *r);
+static void ngx_http_upstream_upgraded_write_downstream(ngx_http_request_t *r);
+static void ngx_http_upstream_upgraded_read_upstream(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+static void ngx_http_upstream_upgraded_write_upstream(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+static void ngx_http_upstream_process_upgraded(ngx_http_request_t *r,
+ ngx_uint_t from_upstream, ngx_uint_t do_write);
static void
ngx_http_upstream_process_non_buffered_downstream(ngx_http_request_t *r);
static void
@@ -134,6 +144,9 @@ static char *ngx_http_upstream(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy);
static char *ngx_http_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
+static ngx_addr_t *ngx_http_upstream_get_local(ngx_http_request_t *r,
+ ngx_http_upstream_local_t *local);
+
static void *ngx_http_upstream_create_main_conf(ngx_conf_t *cf);
static char *ngx_http_upstream_init_main_conf(ngx_conf_t *cf, void *conf);
@@ -427,6 +440,13 @@ ngx_http_upstream_init(ngx_http_request_t *r)
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http init upstream, client timer: %d", c->read->timer_set);
+#if (NGX_HTTP_SPDY)
+ if (r->spdy_stream) {
+ ngx_http_upstream_init_request(r);
+ return;
+ }
+#endif
+
if (c->read->timer_set) {
ngx_del_timer(c->read);
}
@@ -507,7 +527,7 @@ ngx_http_upstream_init_request(ngx_http_request_t *r)
return;
}
- u->peer.local = u->conf->local;
+ u->peer.local = ngx_http_upstream_get_local(r, u->conf->local);
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
@@ -1005,6 +1025,12 @@ ngx_http_upstream_check_broken_connection(ngx_http_request_t *r,
return;
}
+#if (NGX_HTTP_SPDY)
+ if (r->spdy_stream) {
+ return;
+ }
+#endif
+
#if (NGX_HAVE_KQUEUE)
if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
@@ -1111,8 +1137,6 @@ ngx_http_upstream_connect(ngx_http_request_t *r, ngx_http_upstream_t *u)
r->connection->log->action = "connecting to upstream";
- r->connection->single_connection = 0;
-
if (u->state && u->state->response_sec) {
tp = ngx_timeofday();
u->state->response_sec = tp->sec - u->state->response_sec;
@@ -1330,6 +1354,7 @@ ngx_http_upstream_reinit(ngx_http_request_t *r, ngx_http_upstream_t *u)
}
u->keepalive = 0;
+ u->upgrade = 0;
ngx_memzero(&u->headers_in, sizeof(ngx_http_upstream_headers_in_t));
u->headers_in.content_length_n = -1;
@@ -2081,6 +2106,11 @@ ngx_http_upstream_send_response(ngx_http_request_t *r, ngx_http_upstream_t *u)
return;
}
+ if (u->upgrade) {
+ ngx_http_upstream_upgrade(r, u);
+ return;
+ }
+
c = r->connection;
if (r->header_only) {
@@ -2364,6 +2394,284 @@ ngx_http_upstream_send_response(ngx_http_request_t *r, ngx_http_upstream_t *u)
static void
+ngx_http_upstream_upgrade(ngx_http_request_t *r, ngx_http_upstream_t *u)
+{
+ int tcp_nodelay;
+ ngx_connection_t *c;
+ ngx_http_core_loc_conf_t *clcf;
+
+ c = r->connection;
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ /* TODO: prevent upgrade if not requested or not possible */
+
+ r->keepalive = 0;
+ c->log->action = "proxying upgraded connection";
+
+ u->read_event_handler = ngx_http_upstream_upgraded_read_upstream;
+ u->write_event_handler = ngx_http_upstream_upgraded_write_upstream;
+ r->read_event_handler = ngx_http_upstream_upgraded_read_downstream;
+ r->write_event_handler = ngx_http_upstream_upgraded_write_downstream;
+
+ if (clcf->tcp_nodelay) {
+ tcp_nodelay = 1;
+
+ if (c->tcp_nodelay == NGX_TCP_NODELAY_UNSET) {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "tcp_nodelay");
+
+ if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY,
+ (const void *) &tcp_nodelay, sizeof(int)) == -1)
+ {
+ ngx_connection_error(c, ngx_socket_errno,
+ "setsockopt(TCP_NODELAY) failed");
+ ngx_http_upstream_finalize_request(r, u, 0);
+ return;
+ }
+
+ c->tcp_nodelay = NGX_TCP_NODELAY_SET;
+ }
+
+ if (u->peer.connection->tcp_nodelay == NGX_TCP_NODELAY_UNSET) {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, u->peer.connection->log, 0,
+ "tcp_nodelay");
+
+ if (setsockopt(u->peer.connection->fd, IPPROTO_TCP, TCP_NODELAY,
+ (const void *) &tcp_nodelay, sizeof(int)) == -1)
+ {
+ ngx_connection_error(u->peer.connection, ngx_socket_errno,
+ "setsockopt(TCP_NODELAY) failed");
+ ngx_http_upstream_finalize_request(r, u, 0);
+ return;
+ }
+
+ u->peer.connection->tcp_nodelay = NGX_TCP_NODELAY_SET;
+ }
+ }
+
+ if (ngx_http_send_special(r, NGX_HTTP_FLUSH) == NGX_ERROR) {
+ ngx_http_upstream_finalize_request(r, u, 0);
+ return;
+ }
+
+ if (u->peer.connection->read->ready
+ || u->buffer.pos != u->buffer.last)
+ {
+ ngx_http_upstream_process_upgraded(r, 1, 1);
+ }
+
+ if (c->read->ready
+ || r->header_in->pos != r->header_in->last)
+ {
+ ngx_http_upstream_process_upgraded(r, 0, 1);
+ }
+}
+
+
+static void
+ngx_http_upstream_upgraded_read_downstream(ngx_http_request_t *r)
+{
+ ngx_http_upstream_process_upgraded(r, 0, 0);
+}
+
+
+static void
+ngx_http_upstream_upgraded_write_downstream(ngx_http_request_t *r)
+{
+ ngx_http_upstream_process_upgraded(r, 1, 1);
+}
+
+
+static void
+ngx_http_upstream_upgraded_read_upstream(ngx_http_request_t *r,
+ ngx_http_upstream_t *u)
+{
+ ngx_http_upstream_process_upgraded(r, 1, 0);
+}
+
+
+static void
+ngx_http_upstream_upgraded_write_upstream(ngx_http_request_t *r,
+ ngx_http_upstream_t *u)
+{
+ ngx_http_upstream_process_upgraded(r, 0, 1);
+}
+
+
+static void
+ngx_http_upstream_process_upgraded(ngx_http_request_t *r,
+ ngx_uint_t from_upstream, ngx_uint_t do_write)
+{
+ size_t size;
+ ssize_t n;
+ ngx_buf_t *b;
+ ngx_connection_t *c, *downstream, *upstream, *dst, *src;
+ ngx_http_upstream_t *u;
+ ngx_http_core_loc_conf_t *clcf;
+
+ c = r->connection;
+ u = r->upstream;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http upstream process upgraded, fu:%ui", from_upstream);
+
+ downstream = c;
+ upstream = u->peer.connection;
+
+ if (downstream->write->timedout) {
+ c->timedout = 1;
+ ngx_connection_error(c, NGX_ETIMEDOUT, "client timed out");
+ ngx_http_upstream_finalize_request(r, u, NGX_HTTP_REQUEST_TIME_OUT);
+ return;
+ }
+
+ if (upstream->read->timedout || upstream->write->timedout) {
+ ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out");
+ ngx_http_upstream_finalize_request(r, u, 0);
+ return;
+ }
+
+ if (from_upstream) {
+ src = upstream;
+ dst = downstream;
+ b = &u->buffer;
+
+ } else {
+ src = downstream;
+ dst = upstream;
+ b = &u->from_client;
+
+ if (r->header_in->last > r->header_in->pos) {
+ b = r->header_in;
+ b->end = b->last;
+ do_write = 1;
+ }
+
+ if (b->start == NULL) {
+ b->start = ngx_palloc(r->pool, u->conf->buffer_size);
+ if (b->start == NULL) {
+ ngx_http_upstream_finalize_request(r, u, 0);
+ return;
+ }
+
+ b->pos = b->start;
+ b->last = b->start;
+ b->end = b->start + u->conf->buffer_size;
+ b->temporary = 1;
+ b->tag = u->output.tag;
+ }
+ }
+
+ for ( ;; ) {
+
+ if (do_write) {
+
+ size = b->last - b->pos;
+
+ if (size && dst->write->ready) {
+
+ n = dst->send(dst, b->pos, size);
+
+ if (n == NGX_ERROR) {
+ ngx_http_upstream_finalize_request(r, u, 0);
+ return;
+ }
+
+ if (n > 0) {
+ b->pos += n;
+
+ if (b->pos == b->last) {
+ b->pos = b->start;
+ b->last = b->start;
+ }
+ }
+ }
+ }
+
+ size = b->end - b->last;
+
+ if (size && src->read->ready) {
+
+ n = src->recv(src, b->last, size);
+
+ if (n == NGX_AGAIN || n == 0) {
+ break;
+ }
+
+ if (n > 0) {
+ do_write = 1;
+ b->last += n;
+
+ continue;
+ }
+
+ if (n == NGX_ERROR) {
+ src->read->eof = 1;
+ }
+ }
+
+ break;
+ }
+
+ if ((upstream->read->eof && u->buffer.pos == u->buffer.last)
+ || (downstream->read->eof && u->from_client.pos == u->from_client.last)
+ || (downstream->read->eof && upstream->read->eof))
+ {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http upstream upgraded done");
+ ngx_http_upstream_finalize_request(r, u, 0);
+ return;
+ }
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (ngx_handle_write_event(upstream->write, u->conf->send_lowat)
+ != NGX_OK)
+ {
+ ngx_http_upstream_finalize_request(r, u, 0);
+ return;
+ }
+
+ if (upstream->write->active && !upstream->write->ready) {
+ ngx_add_timer(upstream->write, u->conf->send_timeout);
+
+ } else if (upstream->write->timer_set) {
+ ngx_del_timer(upstream->write);
+ }
+
+ if (ngx_handle_read_event(upstream->read, 0) != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u, 0);
+ return;
+ }
+
+ if (upstream->read->active && !upstream->read->ready) {
+ ngx_add_timer(upstream->read, u->conf->read_timeout);
+
+ } else if (upstream->read->timer_set) {
+ ngx_del_timer(upstream->read);
+ }
+
+ if (ngx_handle_write_event(downstream->write, clcf->send_lowat)
+ != NGX_OK)
+ {
+ ngx_http_upstream_finalize_request(r, u, 0);
+ return;
+ }
+
+ if (ngx_handle_read_event(downstream->read, 0) != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u, 0);
+ return;
+ }
+
+ if (downstream->write->active && !downstream->write->ready) {
+ ngx_add_timer(downstream->write, clcf->send_timeout);
+
+ } else if (downstream->write->timer_set) {
+ ngx_del_timer(downstream->write);
+ }
+}
+
+
+static void
ngx_http_upstream_process_non_buffered_downstream(ngx_http_request_t *r)
{
ngx_event_t *wev;
@@ -2968,19 +3276,10 @@ ngx_http_upstream_cleanup(void *data)
{
ngx_http_request_t *r = data;
- ngx_http_upstream_t *u;
-
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"cleanup http upstream request: \"%V\"", &r->uri);
- u = r->upstream;
-
- if (u->resolved && u->resolved->ctx) {
- ngx_resolve_name_done(u->resolved->ctx);
- u->resolved->ctx = NULL;
- }
-
- ngx_http_upstream_finalize_request(r, u, NGX_DONE);
+ ngx_http_upstream_finalize_request(r, r->upstream, NGX_DONE);
}
@@ -3008,7 +3307,7 @@ ngx_http_upstream_finalize_request(ngx_http_request_t *r,
u->state->response_sec = tp->sec - u->state->response_sec;
u->state->response_msec = tp->msec - u->state->response_msec;
- if (u->pipe) {
+ if (u->pipe && u->pipe->read_length) {
u->state->response_length = u->pipe->read_length;
}
}
@@ -4130,6 +4429,7 @@ ngx_http_upstream(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy)
value = cf->args->elts;
u.host = value[1];
u.no_resolve = 1;
+ u.no_port = 1;
uscf = ngx_http_upstream_add(cf, &u, NGX_HTTP_UPSTREAM_CREATE
|NGX_HTTP_UPSTREAM_WEIGHT
@@ -4400,14 +4700,14 @@ ngx_http_upstream_add(ngx_conf_t *cf, ngx_url_t *u, ngx_uint_t flags)
return NULL;
}
- if ((uscfp[i]->flags & NGX_HTTP_UPSTREAM_CREATE) && u->port) {
+ if ((uscfp[i]->flags & NGX_HTTP_UPSTREAM_CREATE) && !u->no_port) {
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
"upstream \"%V\" may not have port %d",
&u->host, u->port);
return NULL;
}
- if ((flags & NGX_HTTP_UPSTREAM_CREATE) && uscfp[i]->port) {
+ if ((flags & NGX_HTTP_UPSTREAM_CREATE) && !uscfp[i]->no_port) {
ngx_log_error(NGX_LOG_WARN, cf->log, 0,
"upstream \"%V\" may not have port %d in %s:%ui",
&u->host, uscfp[i]->port,
@@ -4415,7 +4715,9 @@ ngx_http_upstream_add(ngx_conf_t *cf, ngx_url_t *u, ngx_uint_t flags)
return NULL;
}
- if (uscfp[i]->port != u->port) {
+ if (uscfp[i]->port && u->port
+ && uscfp[i]->port != u->port)
+ {
continue;
}
@@ -4443,6 +4745,7 @@ ngx_http_upstream_add(ngx_conf_t *cf, ngx_url_t *u, ngx_uint_t flags)
uscf->line = cf->conf_file->line;
uscf->port = u->port;
uscf->default_port = u->default_port;
+ uscf->no_port = u->no_port;
if (u->naddrs == 1) {
uscf->servers = ngx_array_create(cf->pool, 1,
@@ -4479,24 +4782,63 @@ ngx_http_upstream_bind_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,
{
char *p = conf;
- ngx_int_t rc;
- ngx_str_t *value;
- ngx_addr_t **paddr;
+ ngx_int_t rc;
+ ngx_str_t *value;
+ ngx_http_complex_value_t cv;
+ ngx_http_upstream_local_t **plocal, *local;
+ ngx_http_compile_complex_value_t ccv;
- paddr = (ngx_addr_t **) (p + cmd->offset);
+ plocal = (ngx_http_upstream_local_t **) (p + cmd->offset);
- *paddr = ngx_palloc(cf->pool, sizeof(ngx_addr_t));
- if (*paddr == NULL) {
- return NGX_CONF_ERROR;
+ if (*plocal != NGX_CONF_UNSET_PTR) {
+ return "is duplicate";
}
value = cf->args->elts;
- rc = ngx_parse_addr(cf->pool, *paddr, value[1].data, value[1].len);
+ if (ngx_strcmp(value[1].data, "off") == 0) {
+ *plocal = NULL;
+ return NGX_CONF_OK;
+ }
+
+ ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+ ccv.cf = cf;
+ ccv.value = &value[1];
+ ccv.complex_value = &cv;
+
+ if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ local = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_local_t));
+ if (local == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *plocal = local;
+
+ if (cv.lengths) {
+ local->value = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));
+ if (local->value == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *local->value = cv;
+
+ return NGX_CONF_OK;
+ }
+
+ local->addr = ngx_palloc(cf->pool, sizeof(ngx_addr_t));
+ if (local->addr == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ rc = ngx_parse_addr(cf->pool, local->addr, value[1].data, value[1].len);
switch (rc) {
case NGX_OK:
- (*paddr)->name = value[1];
+ local->addr->name = value[1];
return NGX_CONF_OK;
case NGX_DECLINED:
@@ -4510,6 +4852,53 @@ ngx_http_upstream_bind_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,
}
+static ngx_addr_t *
+ngx_http_upstream_get_local(ngx_http_request_t *r,
+ ngx_http_upstream_local_t *local)
+{
+ ngx_int_t rc;
+ ngx_str_t val;
+ ngx_addr_t *addr;
+
+ if (local == NULL) {
+ return NULL;
+ }
+
+ if (local->value == NULL) {
+ return local->addr;
+ }
+
+ if (ngx_http_complex_value(r, local->value, &val) != NGX_OK) {
+ return NULL;
+ }
+
+ if (val.len == 0) {
+ return NULL;
+ }
+
+ addr = ngx_palloc(r->pool, sizeof(ngx_addr_t));
+ if (addr == NULL) {
+ return NULL;
+ }
+
+ rc = ngx_parse_addr(r->pool, addr, val.data, val.len);
+
+ switch (rc) {
+ case NGX_OK:
+ addr->name = val;
+ return addr;
+
+ case NGX_DECLINED:
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "invalid local address \"%V\"", &val);
+ /* fall through */
+
+ default:
+ return NULL;
+ }
+}
+
+
char *
ngx_http_upstream_param_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf)
diff --git a/usr.sbin/nginx/src/http/ngx_http_upstream.h b/usr.sbin/nginx/src/http/ngx_http_upstream.h
index f32c9852269..29ebf9bd92d 100644
--- a/usr.sbin/nginx/src/http/ngx_http_upstream.h
+++ b/usr.sbin/nginx/src/http/ngx_http_upstream.h
@@ -116,10 +116,17 @@ struct ngx_http_upstream_srv_conf_s {
ngx_uint_t line;
in_port_t port;
in_port_t default_port;
+ ngx_uint_t no_port; /* unsigned no_port:1 */
};
typedef struct {
+ ngx_addr_t *addr;
+ ngx_http_complex_value_t *value;
+} ngx_http_upstream_local_t;
+
+
+typedef struct {
ngx_http_upstream_srv_conf_t *upstream;
ngx_msec_t connect_timeout;
@@ -157,7 +164,7 @@ typedef struct {
ngx_array_t *hide_headers;
ngx_array_t *pass_headers;
- ngx_addr_t *local;
+ ngx_http_upstream_local_t *local;
#if (NGX_HTTP_CACHE)
ngx_shm_zone_t *cache;
@@ -277,6 +284,8 @@ struct ngx_http_upstream_s {
ngx_http_upstream_resolved_t *resolved;
+ ngx_buf_t from_client;
+
ngx_buf_t buffer;
off_t length;
@@ -322,6 +331,7 @@ struct ngx_http_upstream_s {
unsigned buffering:1;
unsigned keepalive:1;
+ unsigned upgrade:1;
unsigned request_sent:1;
unsigned header_sent:1;
diff --git a/usr.sbin/nginx/src/http/ngx_http_upstream_round_robin.c b/usr.sbin/nginx/src/http/ngx_http_upstream_round_robin.c
index c9ecfb22acd..d786ed14254 100644
--- a/usr.sbin/nginx/src/http/ngx_http_upstream_round_robin.c
+++ b/usr.sbin/nginx/src/http/ngx_http_upstream_round_robin.c
@@ -10,8 +10,6 @@
#include <ngx_http.h>
-static ngx_int_t ngx_http_upstream_cmp_servers(const void *one,
- const void *two);
static ngx_http_upstream_rr_peer_t *ngx_http_upstream_get_peer(
ngx_http_upstream_rr_peer_data_t *rrp);
@@ -93,10 +91,6 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
us->peer.data = peers;
- ngx_sort(&peers->peer[0], (size_t) n,
- sizeof(ngx_http_upstream_rr_peer_t),
- ngx_http_upstream_cmp_servers);
-
/* backup servers */
n = 0;
@@ -151,17 +145,13 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
peers->next = backup;
- ngx_sort(&backup->peer[0], (size_t) n,
- sizeof(ngx_http_upstream_rr_peer_t),
- ngx_http_upstream_cmp_servers);
-
return NGX_OK;
}
/* an upstream implicitly defined by proxy_pass, etc. */
- if (us->port == 0 && us->default_port == 0) {
+ if (us->port == 0) {
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
"no port in upstream \"%V\" in %s:%ui",
&us->host, us->file_name, us->line);
@@ -171,7 +161,7 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
ngx_memzero(&u, sizeof(ngx_url_t));
u.host = us->host;
- u.port = (in_port_t) (us->port ? us->port : us->default_port);
+ u.port = us->port;
if (ngx_inet_resolve_host(cf->pool, &u) != NGX_OK) {
if (u.err) {
@@ -216,18 +206,6 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
}
-static ngx_int_t
-ngx_http_upstream_cmp_servers(const void *one, const void *two)
-{
- ngx_http_upstream_rr_peer_t *first, *second;
-
- first = (ngx_http_upstream_rr_peer_t *) one;
- second = (ngx_http_upstream_rr_peer_t *) two;
-
- return (first->weight < second->weight);
-}
-
-
ngx_int_t
ngx_http_upstream_init_round_robin_peer(ngx_http_request_t *r,
ngx_http_upstream_srv_conf_t *us)
@@ -395,7 +373,6 @@ ngx_http_upstream_get_round_robin_peer(ngx_peer_connection_t *pc, void *data)
ngx_int_t rc;
ngx_uint_t i, n;
- ngx_connection_t *c;
ngx_http_upstream_rr_peer_t *peer;
ngx_http_upstream_rr_peers_t *peers;
@@ -404,26 +381,6 @@ ngx_http_upstream_get_round_robin_peer(ngx_peer_connection_t *pc, void *data)
/* ngx_lock_mutex(rrp->peers->mutex); */
- if (rrp->peers->last_cached) {
-
- /* cached connection */
-
- c = rrp->peers->cached[rrp->peers->last_cached];
- rrp->peers->last_cached--;
-
- /* ngx_unlock_mutex(ppr->peers->mutex); */
-
-#if (NGX_THREADS)
- c->read->lock = c->read->own_lock;
- c->write->lock = c->write->own_lock;
-#endif
-
- pc->connection = c;
- pc->cached = 1;
-
- return NGX_OK;
- }
-
pc->cached = 0;
pc->connection = NULL;
diff --git a/usr.sbin/nginx/src/http/ngx_http_upstream_round_robin.h b/usr.sbin/nginx/src/http/ngx_http_upstream_round_robin.h
index 3f8cbf87f7f..ea90ab9181e 100644
--- a/usr.sbin/nginx/src/http/ngx_http_upstream_round_robin.h
+++ b/usr.sbin/nginx/src/http/ngx_http_upstream_round_robin.h
@@ -42,10 +42,8 @@ typedef struct ngx_http_upstream_rr_peers_s ngx_http_upstream_rr_peers_t;
struct ngx_http_upstream_rr_peers_s {
ngx_uint_t number;
- ngx_uint_t last_cached;
/* ngx_mutex_t *mutex; */
- ngx_connection_t **cached;
ngx_uint_t total_weight;
diff --git a/usr.sbin/nginx/src/http/ngx_http_variables.c b/usr.sbin/nginx/src/http/ngx_http_variables.c
index d969fe1466b..6f1e0344dda 100644
--- a/usr.sbin/nginx/src/http/ngx_http_variables.c
+++ b/usr.sbin/nginx/src/http/ngx_http_variables.c
@@ -21,8 +21,13 @@ static void ngx_http_variable_request_set_size(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_header(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
+
+static ngx_int_t ngx_http_variable_cookies(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_headers(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_headers_internal(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data, u_char sep);
static ngx_int_t ngx_http_variable_unknown_header_in(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
@@ -39,6 +44,8 @@ static ngx_int_t ngx_http_variable_tcpinfo(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
#endif
+static ngx_int_t ngx_http_variable_content_length(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_host(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_binary_remote_addr(ngx_http_request_t *r,
@@ -131,8 +138,8 @@ static ngx_int_t ngx_http_variable_time_local(ngx_http_request_t *r,
*/
/*
- * the $http_host, $http_user_agent, $http_referer, $http_via,
- * and $http_x_forwarded_for variables may be handled by generic
+ * the $http_host, $http_user_agent, $http_referer, and $http_via
+ * variables may be handled by generic
* ngx_http_variable_unknown_header_in(), but for performance reasons
* they are handled using dedicated entries
*/
@@ -154,15 +161,15 @@ static ngx_http_variable_t ngx_http_core_variables[] = {
#endif
#if (NGX_HTTP_X_FORWARDED_FOR)
- { ngx_string("http_x_forwarded_for"), NULL, ngx_http_variable_header,
+ { ngx_string("http_x_forwarded_for"), NULL, ngx_http_variable_headers,
offsetof(ngx_http_request_t, headers_in.x_forwarded_for), 0, 0 },
#endif
- { ngx_string("http_cookie"), NULL, ngx_http_variable_headers,
+ { ngx_string("http_cookie"), NULL, ngx_http_variable_cookies,
offsetof(ngx_http_request_t, headers_in.cookies), 0, 0 },
- { ngx_string("content_length"), NULL, ngx_http_variable_header,
- offsetof(ngx_http_request_t, headers_in.content_length), 0, 0 },
+ { ngx_string("content_length"), NULL, ngx_http_variable_content_length,
+ 0, 0, 0 },
{ ngx_string("content_type"), NULL, ngx_http_variable_header,
offsetof(ngx_http_request_t, headers_in.content_type), 0, 0 },
@@ -724,8 +731,24 @@ ngx_http_variable_header(ngx_http_request_t *r, ngx_http_variable_value_t *v,
static ngx_int_t
-ngx_http_variable_headers(ngx_http_request_t *r, ngx_http_variable_value_t *v,
- uintptr_t data)
+ngx_http_variable_cookies(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ return ngx_http_variable_headers_internal(r, v, data, ';');
+}
+
+
+static ngx_int_t
+ngx_http_variable_headers(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ return ngx_http_variable_headers_internal(r, v, data, ',');
+}
+
+
+static ngx_int_t
+ngx_http_variable_headers_internal(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data, u_char sep)
{
size_t len;
u_char *p, *end;
@@ -746,7 +769,7 @@ ngx_http_variable_headers(ngx_http_request_t *r, ngx_http_variable_value_t *v,
continue;
}
- len += h[i]->value.len + sizeof("; ") - 1;
+ len += h[i]->value.len + 2;
}
if (len == 0) {
@@ -754,7 +777,7 @@ ngx_http_variable_headers(ngx_http_request_t *r, ngx_http_variable_value_t *v,
return NGX_OK;
}
- len -= sizeof("; ") - 1;
+ len -= 2;
v->valid = 1;
v->no_cacheable = 0;
@@ -789,7 +812,7 @@ ngx_http_variable_headers(ngx_http_request_t *r, ngx_http_variable_value_t *v,
break;
}
- *p++ = ';'; *p++ = ' ';
+ *p++ = sep; *p++ = ' ';
}
return NGX_OK;
@@ -1022,6 +1045,39 @@ ngx_http_variable_tcpinfo(ngx_http_request_t *r, ngx_http_variable_value_t *v,
static ngx_int_t
+ngx_http_variable_content_length(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ u_char *p;
+
+ if (r->headers_in.content_length) {
+ v->len = r->headers_in.content_length->value.len;
+ v->data = r->headers_in.content_length->value.data;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ } else if (r->headers_in.content_length_n >= 0) {
+ p = ngx_pnalloc(r->pool, NGX_OFF_T_LEN);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ v->len = ngx_sprintf(p, "%O", r->headers_in.content_length_n) - p;
+ v->data = p;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ } else {
+ v->not_found = 1;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
ngx_http_variable_host(ngx_http_request_t *r, ngx_http_variable_value_t *v,
uintptr_t data)
{
@@ -1712,7 +1768,11 @@ ngx_http_variable_sent_connection(ngx_http_request_t *r,
size_t len;
char *p;
- if (r->keepalive) {
+ if (r->headers_out.status == NGX_HTTP_SWITCHING_PROTOCOLS) {
+ len = sizeof("upgrade") - 1;
+ p = "upgrade";
+
+ } else if (r->keepalive) {
len = sizeof("keep-alive") - 1;
p = "keep-alive";
diff --git a/usr.sbin/nginx/src/mail/ngx_mail.h b/usr.sbin/nginx/src/mail/ngx_mail.h
index 1fceb42856e..ccdfb8c6f10 100644
--- a/usr.sbin/nginx/src/mail/ngx_mail.h
+++ b/usr.sbin/nginx/src/mail/ngx_mail.h
@@ -39,7 +39,7 @@ typedef struct {
unsigned ssl:1;
#endif
#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
- unsigned ipv6only:2;
+ unsigned ipv6only:1;
#endif
unsigned so_keepalive:2;
#if (NGX_HAVE_KEEPALIVE_TUNABLE)
@@ -100,7 +100,7 @@ typedef struct {
unsigned ssl:1;
#endif
#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
- unsigned ipv6only:2;
+ unsigned ipv6only:1;
#endif
unsigned so_keepalive:2;
#if (NGX_HAVE_KEEPALIVE_TUNABLE)
diff --git a/usr.sbin/nginx/src/mail/ngx_mail_auth_http_module.c b/usr.sbin/nginx/src/mail/ngx_mail_auth_http_module.c
index d2bd4ad61a8..2e9b9f24da8 100644
--- a/usr.sbin/nginx/src/mail/ngx_mail_auth_http_module.c
+++ b/usr.sbin/nginx/src/mail/ngx_mail_auth_http_module.c
@@ -454,12 +454,15 @@ static void
ngx_mail_auth_http_process_headers(ngx_mail_session_t *s,
ngx_mail_auth_http_ctx_t *ctx)
{
- u_char *p;
- time_t timer;
- size_t len, size;
- ngx_int_t rc, port, n;
- ngx_addr_t *peer;
- struct sockaddr_in *sin;
+ u_char *p;
+ time_t timer;
+ size_t len, size;
+ ngx_int_t rc, port, n;
+ ngx_addr_t *peer;
+ struct sockaddr_in *sin;
+#if (NGX_HAVE_INET6)
+ struct sockaddr_in6 *sin6;
+#endif
ngx_log_debug0(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
"mail auth http process headers");
@@ -772,17 +775,26 @@ ngx_mail_auth_http_process_headers(ngx_mail_session_t *s,
return;
}
- /* AF_INET only */
+ rc = ngx_parse_addr(s->connection->pool, peer,
+ ctx->addr.data, ctx->addr.len);
+
+ switch (rc) {
+ case NGX_OK:
+ break;
- sin = ngx_pcalloc(s->connection->pool, sizeof(struct sockaddr_in));
- if (sin == NULL) {
+ case NGX_DECLINED:
+ ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
+ "auth http server %V sent invalid server "
+ "address:\"%V\"",
+ ctx->peer.name, &ctx->addr);
+ /* fall through */
+
+ default:
ngx_destroy_pool(ctx->pool);
ngx_mail_session_internal_server_error(s);
return;
}
- sin->sin_family = AF_INET;
-
port = ngx_atoi(ctx->port.data, ctx->port.len);
if (port == NGX_ERROR || port < 1 || port > 65535) {
ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
@@ -794,21 +806,20 @@ ngx_mail_auth_http_process_headers(ngx_mail_session_t *s,
return;
}
- sin->sin_port = htons((in_port_t) port);
+ switch (peer->sockaddr->sa_family) {
- sin->sin_addr.s_addr = ngx_inet_addr(ctx->addr.data, ctx->addr.len);
- if (sin->sin_addr.s_addr == INADDR_NONE) {
- ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
- "auth http server %V sent invalid server "
- "address:\"%V\"",
- ctx->peer.name, &ctx->addr);
- ngx_destroy_pool(ctx->pool);
- ngx_mail_session_internal_server_error(s);
- return;
- }
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *) peer->sockaddr;
+ sin6->sin6_port = htons((in_port_t) port);
+ break;
+#endif
- peer->sockaddr = (struct sockaddr *) sin;
- peer->socklen = sizeof(struct sockaddr_in);
+ default: /* AF_INET */
+ sin = (struct sockaddr_in *) peer->sockaddr;
+ sin->sin_port = htons((in_port_t) port);
+ break;
+ }
len = ctx->addr.len + 1 + ctx->port.len;
@@ -1388,7 +1399,6 @@ ngx_mail_auth_http(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
u.url = value[1];
u.default_port = 80;
u.uri_part = 1;
- u.one_addr = 1;
if (ngx_strncmp(u.url.data, "http://", 7) == 0) {
u.url.len -= 7;
diff --git a/usr.sbin/nginx/src/mail/ngx_mail_core_module.c b/usr.sbin/nginx/src/mail/ngx_mail_core_module.c
index 6509b989b1c..be8673c299f 100644
--- a/usr.sbin/nginx/src/mail/ngx_mail_core_module.c
+++ b/usr.sbin/nginx/src/mail/ngx_mail_core_module.c
@@ -382,6 +382,10 @@ ngx_mail_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
ls->wildcard = u.wildcard;
ls->ctx = cf->ctx;
+#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
+ ls->ipv6only = 1;
+#endif
+
if (cscf->protocol == NULL) {
for (m = 0; ngx_modules[m]; m++) {
if (ngx_modules[m]->type != NGX_MAIL_MODULE) {
@@ -423,7 +427,7 @@ ngx_mail_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
ls->ipv6only = 1;
} else if (ngx_strcmp(&value[i].data[10], "ff") == 0) {
- ls->ipv6only = 2;
+ ls->ipv6only = 0;
} else {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,