diff --git a/thirdparty/nginx/Makefile b/thirdparty/nginx/Makefile index 4f582c8b8..e282aeb8e 100644 --- a/thirdparty/nginx/Makefile +++ b/thirdparty/nginx/Makefile @@ -6,8 +6,10 @@ psrctmp := $(PLATFORM_DIR)/srctmp pvers := $(NGINX_VERSION) pname := nginx zname := zimbra-$(pname) -pfile := $(zname)-$(pvers).tar.gz +pfile := $(pname)-$(pvers).tar.gz +purl := http://nginx.org/download/$(pname)-$(pvers).tar.gz psrc_file := $(SRC_DIR)/$(pfile) +psrc_final_file := $(SRC_DIR)/$(zname)-$(pvers).tar.gz zspec := $(pname).spec zmodules := zmmodules @@ -19,7 +21,11 @@ getsrc: mkdir -p $(SRC_DIR) mkdir -p $(psrctmp) mkdir -p $(psrctmp)/$(pname)-$(pvers)-zimbra/ - cp -pr $(pname)/* $(psrctmp)/$(pname)-$(pvers)-zimbra/ + cd $(psrctmp) && \ + $(WGET) $(purl) && \ + $(TAR) -xvf $(pname)-$(pvers).tar.gz && \ + cp -pr $(pname)-$(pvers)/* $(psrctmp)/$(pname)-$(pvers)-zimbra/ + cd $(PLATFORM_DIR) mkdir -p $(psrctmp)/$(pname)-$(pvers)-zimbra/modules cd $(psrctmp)/$(pname)-$(pvers)-zimbra/modules && \ git clone https://github.com/nviennot/nginx-tcp-keepalive.git nviennot-nginx-tcp-keepalive >/dev/null 2>&1 && \ @@ -51,7 +57,8 @@ build_rpm: $(replace-pkginfo) $(specfile) && \ $(replace-pathinfo) $(specfile) && \ $(MKDIR) BUILD BUILDROOT RPMS SOURCES SRPMS && \ - $(CP) $(psrc_file) SOURCES/ && \ + $(CP) $(psrc_final_file) SOURCES/ && \ + $(CP) $(PKG_ROOT)/patches/*.patch SOURCES/ && \ $(PKG_BUILD) $(specfile) build_deb: z_tgz = $(zname)_$(pvers).orig.tar.gz @@ -60,8 +67,9 @@ build_deb: $(CD) $(PLATFORM_DIR)/$(zname) && \ $(replace-pkginfo) debian/changelog $(z_shlibs) && \ $(replace-pathinfo) debian/rules && \ - $(CP) $(psrc_file) ../$(z_tgz) && \ + $(CP) $(psrc_final_file) ../$(z_tgz) && \ $(TAR) xfz ../$(z_tgz) --strip-components=1 && \ + $(CP) $(PKG_ROOT)/patches/*.patch debian/patches/ && \ $(PKG_BUILD) clean: pkgrm diff --git a/thirdparty/nginx/patches/nginx_README.patch b/thirdparty/nginx/patches/nginx_README.patch new file mode 100644 index 000000000..7d4316861 --- /dev/null +++ b/thirdparty/nginx/patches/nginx_README.patch @@ -0,0 +1,14 @@ +--- nginx/README.md 1970-01-01 05:30:00.000000000 +0530 ++++ nginx/README.md 2023-09-14 18:47:11.380437400 +0530 +@@ -0,0 +1,11 @@ ++# nginx ++ ++This is the official repository for building out the third party dependency nginx for Zimbra Collaboration Suite 8.7 and later. ++ ++Issues should be reported via [Zimbra Forum](https://forums.zimbra.org/) ++ ++## Development branch ++zimbra/develop ++ ++## Release tags ++zimbra-release-1.24.0 diff --git a/thirdparty/nginx/patches/nginx_auto_cc.patch b/thirdparty/nginx/patches/nginx_auto_cc.patch new file mode 100644 index 000000000..b56cb14b3 --- /dev/null +++ b/thirdparty/nginx/patches/nginx_auto_cc.patch @@ -0,0 +1,15 @@ +diff -urN nginx/auto/cc/gcc nginx/auto/cc/gcc +--- nginx/auto/cc/gcc 2023-03-07 16:26:44.050300500 +0530 ++++ nginx/auto/cc/gcc 2023-09-14 18:47:11.398148600 +0530 +@@ -50,7 +50,10 @@ + + #NGX_GCC_OPT="-O2" + #NGX_GCC_OPT="-Os" +-NGX_GCC_OPT="-O" ++# Zimbra customizations start here (Jira Tickets: ) ++#NGX_GCC_OPT="-O" ++NGX_GCC_OPT="-O0" ++# Zimbra customizations end here + + #CFLAGS="$CFLAGS -fomit-frame-pointer" + diff --git a/thirdparty/nginx/patches/nginx_auto_lib.patch b/thirdparty/nginx/patches/nginx_auto_lib.patch new file mode 100644 index 000000000..cf6aff81c --- /dev/null +++ b/thirdparty/nginx/patches/nginx_auto_lib.patch @@ -0,0 +1,110 @@ +diff -urN nginx/auto/lib/conf nginx/auto/lib/conf +--- nginx/auto/lib/conf 2023-03-07 16:26:44.070240100 +0530 ++++ nginx/auto/lib/conf 2023-09-14 18:47:11.422502700 +0530 +@@ -52,3 +52,9 @@ + if [ $NGX_LIBATOMIC != NO ]; then + . auto/lib/libatomic/conf + fi ++ ++# Zimbra customizations start here: Implement SASL Authentication (Jira Tickets: ) ++if [ $USE_SASL = YES ]; then ++ . auto/lib/sasl/conf ++fi ++# Zimbra customizations end here +diff -urN nginx/auto/lib/sasl/conf nginx/auto/lib/sasl/conf +--- nginx/auto/lib/sasl/conf 1970-01-01 05:30:00.000000000 +0530 ++++ nginx/auto/lib/sasl/conf 2023-03-04 13:25:59.784471400 +0530 +@@ -0,0 +1,93 @@ ++# ++# ***** BEGIN LICENSE BLOCK ***** ++# Zimbra Collaboration Suite Server ++# Copyright (C) 2011 Zimbra Software, LLC. ++# ++# The contents of this file are subject to the Zimbra Public License ++# Version 1.4 ("License"); you may not use this file except in ++# compliance with the License. You may obtain a copy of the License at ++# http://www.zimbra.com/license. ++# ++# Software distributed under the License is distributed on an "AS IS" ++# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. ++# ***** END LICENSE BLOCK ***** ++# ++ ngx_feature="SASL library" ++ ngx_feature_name="NGX_SASL" ++ ngx_feature_run=no ++ ngx_feature_incs="#include " ++ ngx_feature_path= ++ ngx_feature_libs="-lsasl2" ++ ngx_feature_test="int rc = sasl_server_init(NULL, \"test\");" ++ . auto/feature ++ ++ ++if [ $ngx_found = no ]; then ++ ++ # FreeBSD port ++ ++ ngx_feature="SASL library in /usr/local/" ++ ngx_feature_path="/usr/local/include" ++ ++ if [ $NGX_RPATH = YES ]; then ++ ngx_feature_libs="-R/usr/local/lib -L/usr/local/lib -lsasl2" ++ else ++ ngx_feature_libs="-L/usr/local/lib -lsasl2" ++ fi ++ ++ . auto/feature ++fi ++ ++ ++if [ $ngx_found = no ]; then ++ ++ # NetBSD port ++ ++ ngx_feature="SASL library in /usr/pkg/" ++ ngx_feature_path="/usr/pkg/include/" ++ ++ if [ $NGX_RPATH = YES ]; then ++ ngx_feature_libs="-R/usr/pkg/lib -L/usr/pkg/lib -lsasl2" ++ else ++ ngx_feature_libs="-L/usr/pkg/lib -lsasl2" ++ fi ++ ++ . auto/feature ++fi ++ ++ ++if [ $ngx_found = no ]; then ++ ++ # MacPorts ++ ++ ngx_feature="SASL library in /opt/local/" ++ ngx_feature_path="/opt/local/include" ++ ++ if [ $NGX_RPATH = YES ]; then ++ ngx_feature_libs="-R/opt/local/lib -L/opt/local/lib -lsasl2" ++ else ++ ngx_feature_libs="-L/opt/local/lib -lsasl2" ++ fi ++ ++ . auto/feature ++fi ++ ++ ++if [ $ngx_found = yes ]; then ++ ++ CORE_INCS="$CORE_INCS $ngx_feature_path" ++ CORE_LIBS="$CORE_LIBS $ngx_feature_libs" ++ SASL=YES ++ ++else ++ ++cat << END ++ ++$0: error: SASL support in the MAIL filter modules requires the SASL library. ++You can either not enable the feature or install the libraries. ++ ++END ++ ++ exit 1 ++ ++fi diff --git a/thirdparty/nginx/patches/nginx_auto_modules.patch b/thirdparty/nginx/patches/nginx_auto_modules.patch new file mode 100644 index 000000000..aea232fbd --- /dev/null +++ b/thirdparty/nginx/patches/nginx_auto_modules.patch @@ -0,0 +1,65 @@ +--- nginx/auto/modules 2023-08-18 17:02:49.192905800 +0530 ++++ nginx/auto/modules 2023-09-14 18:47:11.461941600 +0530 +@@ -63,6 +63,7 @@ + + ngx_module_type=HTTP + ++ # Zimbra customizations start here: Adding ngx_http_upstream_fair (Jira Tickets: ) + if :; then + ngx_module_name="ngx_http_module \ + ngx_http_core_module \ +@@ -77,7 +78,8 @@ + src/http/ngx_http_variables.h \ + src/http/ngx_http_script.h \ + src/http/ngx_http_upstream.h \ +- src/http/ngx_http_upstream_round_robin.h" ++ src/http/ngx_http_upstream_round_robin.h \ ++ src/http/ngx_http_upstream_fair.h" + ngx_module_srcs="src/http/ngx_http.c \ + src/http/ngx_http_core_module.c \ + src/http/ngx_http_special_response.c \ +@@ -88,13 +90,14 @@ + src/http/ngx_http_variables.c \ + src/http/ngx_http_script.c \ + src/http/ngx_http_upstream.c \ +- src/http/ngx_http_upstream_round_robin.c" ++ src/http/ngx_http_upstream_round_robin.c \ ++ src/http/ngx_http_upstream_fair.c" + ngx_module_libs= + ngx_module_link=YES + + . auto/module + fi +- ++ # Zimbra customizations end here (Jira Tickets: ) + + if [ $HTTP_CACHE = YES ]; then + have=NGX_HTTP_CACHE . auto/have +@@ -939,6 +942,13 @@ + + ngx_module_incs= + ++ # Zimbra customizations start here: Implement SASL Authentication (Jira Tickets: ) ++ if [ $MAIL_SASL = YES ]; then ++ USE_SASL=YES ++ have=NGX_MAIL_SASL . auto/have ++ fi ++ # Zimbra customizations end ++ + if [ $MAIL_SSL = YES ]; then + USE_OPENSSL=YES + have=NGX_MAIL_SSL . auto/have +@@ -1285,9 +1295,11 @@ + . auto/module + fi + +- ++# Zimbra customizations start here: Adding Nginx memcache and zm_lookup module (Jira Tickets: ) + modules="$CORE_MODULES $EVENT_MODULES" +- ++modules="$modules $MEMCACHE_MODULE" ++modules="$modules $ZM_LOOKUP_MODULE" ++# Zimbra customizations end here + + # thread pool module should be initialized after events + if [ $USE_THREADS = YES ]; then diff --git a/thirdparty/nginx/patches/nginx_auto_options.patch b/thirdparty/nginx/patches/nginx_auto_options.patch new file mode 100644 index 000000000..e6da5891e --- /dev/null +++ b/thirdparty/nginx/patches/nginx_auto_options.patch @@ -0,0 +1,58 @@ +--- nginx/auto/options 2023-08-18 17:02:49.224080200 +0530 ++++ nginx/auto/options 2023-09-14 18:47:11.483533400 +0530 +@@ -151,6 +151,12 @@ + USE_OPENSSL=NO + OPENSSL=NONE + ++# Zimbra customizations start here: Implement SASL Authentication (Jira Tickets: ) ++USE_SASL=NO ++SASL=NONE ++SASL_OPT= ++# Zimbra customizations end here ++ + USE_ZLIB=NO + ZLIB=NONE + ZLIB_OPT= +@@ -292,6 +298,9 @@ + --with-http_stub_status_module) HTTP_STUB_STATUS=YES ;; + + --with-mail) MAIL=YES ;; ++ # Zimbra customizations start here: Implement SASL Authentication (Jira Tickets: ) ++ --with-mail-sasl) MAIL_SASL=YES ;; ++ # Zimbra customizations end here + --with-mail=dynamic) MAIL=DYNAMIC ;; + --with-mail_ssl_module) MAIL_SSL=YES ;; + # STUB +@@ -363,6 +372,11 @@ + --with-openssl=*) OPENSSL="$value" ;; + --with-openssl-opt=*) OPENSSL_OPT="$value" ;; + ++ # Zimbra customizations start here: Implement SASL Authentication (Jira Tickets: ) ++ --with-sasl=*) SASL="$value" ;; ++ --with-sasl-opt=*) SASL_OPT="$value" ;; ++ # Zimbra customizations end here ++ + --with-md5=*) + NGX_POST_CONF_MSG="$NGX_POST_CONF_MSG + $0: warning: the \"--with-md5\" option is deprecated" +@@ -524,6 +538,9 @@ + --without-http-cache disable HTTP cache + + --with-mail enable POP3/IMAP4/SMTP proxy module ++ # Zimbra customizations start here: Implement SASL Authentication (Jira Tickets: ) ++ --with-mail-sasl enable SASL support ++ # Zimbra customizations end here + --with-mail=dynamic enable dynamic POP3/IMAP4/SMTP proxy module + --with-mail_ssl_module enable ngx_mail_ssl_module + --without-mail_pop3_module disable ngx_mail_pop3_module +@@ -577,6 +594,10 @@ + --with-pcre-jit build PCRE with JIT compilation support + --without-pcre2 do not use PCRE2 library + ++ # Zimbra customizations start here: Implement SASL Authentication (Jira Tickets: ) ++ --with-sasl=DIR set path to sasl library sources ++ --with-sasl-opt=OPTIONS set additional options for sasl building ++ # Zimbra customizations end + --with-zlib=DIR set path to zlib library sources + --with-zlib-opt=OPTIONS set additional build options for zlib + --with-zlib-asm=CPU use zlib assembler sources optimized diff --git a/thirdparty/nginx/patches/nginx_auto_sources.patch b/thirdparty/nginx/patches/nginx_auto_sources.patch new file mode 100644 index 000000000..7c1506f2d --- /dev/null +++ b/thirdparty/nginx/patches/nginx_auto_sources.patch @@ -0,0 +1,45 @@ +--- nginx/auto/sources 2023-08-18 17:02:49.229066800 +0530 ++++ nginx/auto/sources 2023-09-14 18:47:11.526404800 +0530 +@@ -7,6 +7,7 @@ + + CORE_INCS="src/core" + ++# Zimbra customizations start here: Adding Nginx memcache and zm_lookup module (Jira Tickets: ) + CORE_DEPS="src/core/nginx.h \ + src/core/ngx_config.h \ + src/core/ngx_core.h \ +@@ -39,6 +40,8 @@ + src/core/ngx_module.h \ + src/core/ngx_resolver.h \ + src/core/ngx_open_file_cache.h \ ++ src/core/ngx_memcache.h \ ++ src/core/ngx_zm_lookup.h \ + src/core/ngx_crypt.h \ + src/core/ngx_proxy_protocol.h \ + src/core/ngx_syslog.h" +@@ -76,10 +79,12 @@ + src/core/ngx_module.c \ + src/core/ngx_resolver.c \ + src/core/ngx_open_file_cache.c \ ++ src/core/ngx_memcache.c \ ++ src/core/ngx_zm_lookup.c \ + src/core/ngx_crypt.c \ + src/core/ngx_proxy_protocol.c \ + src/core/ngx_syslog.c" +- ++# Zimbra customizations end + + EVENT_MODULES="ngx_events_module ngx_event_core_module" + +@@ -127,6 +132,11 @@ + FILE_AIO_SRCS="src/os/unix/ngx_file_aio_read.c" + LINUX_AIO_SRCS="src/os/unix/ngx_linux_aio_read.c" + ++# Zimbra customizations start here: Adding Nginx memcache and zm_lookup module (Jira Tickets: ) ++MEMCACHE_MODULE=ngx_memcache_module ++ZM_LOOKUP_MODULE=ngx_zm_lookup_module ++# Zimbra customizations end here ++ + UNIX_INCS="$CORE_INCS $EVENT_INCS src/os/unix" + + UNIX_DEPS="$CORE_DEPS $EVENT_DEPS \ diff --git a/thirdparty/nginx/patches/nginx_docs.patch b/thirdparty/nginx/patches/nginx_docs.patch new file mode 100644 index 000000000..ee9362714 --- /dev/null +++ b/thirdparty/nginx/patches/nginx_docs.patch @@ -0,0 +1,30688 @@ +diff -urN nginx-1.24.0/docs/dtd/change_log_conf.dtd nginx/docs/dtd/change_log_conf.dtd +--- nginx-1.24.0/docs/dtd/change_log_conf.dtd 1970-01-01 05:30:00.000000000 +0530 ++++ nginx/docs/dtd/change_log_conf.dtd 2023-03-04 13:25:23.458893200 +0530 +@@ -0,0 +1,22 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff -urN nginx-1.24.0/docs/dtd/changes.dtd nginx/docs/dtd/changes.dtd +--- nginx-1.24.0/docs/dtd/changes.dtd 1970-01-01 05:30:00.000000000 +0530 ++++ nginx/docs/dtd/changes.dtd 2023-03-04 13:25:23.459891100 +0530 +@@ -0,0 +1,22 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff -urN nginx-1.24.0/docs/GNUmakefile nginx/docs/GNUmakefile +--- nginx-1.24.0/docs/GNUmakefile 1970-01-01 05:30:00.000000000 +0530 ++++ nginx/docs/GNUmakefile 2023-03-04 13:25:23.457866400 +0530 +@@ -0,0 +1,41 @@ ++ ++VER= $(shell grep 'define NGINX_VERSION' src/core/nginx.h \ ++ | sed -e 's/^.*"\(.*\)".*/\1/') ++NGINX= nginx-$(VER) ++TEMP= tmp ++XSLS?= xslscript.pl ++ ++ ++all: changes ++ ++changes: $(TEMP)/$(NGINX)/CHANGES.ru \ ++ $(TEMP)/$(NGINX)/CHANGES ++ ++ ++$(TEMP)/$(NGINX)/CHANGES.ru: docs/dtd/changes.dtd \ ++ docs/xml/nginx/changes.xml \ ++ docs/xml/change_log_conf.xml \ ++ docs/xslt/changes.xslt ++ ++ mkdir -p $(TEMP)/$(NGINX) ++ ++ xmllint --noout --valid docs/xml/nginx/changes.xml ++ xsltproc --stringparam lang ru \ ++ -o $@ docs/xslt/changes.xslt docs/xml/nginx/changes.xml ++ ++ ++$(TEMP)/$(NGINX)/CHANGES: docs/dtd/changes.dtd \ ++ docs/xml/nginx/changes.xml \ ++ docs/xml/change_log_conf.xml \ ++ docs/xslt/changes.xslt ++ ++ mkdir -p $(TEMP)/$(NGINX) ++ ++ xmllint --noout --valid docs/xml/nginx/changes.xml ++ xsltproc --stringparam lang en \ ++ -o $@ docs/xslt/changes.xslt docs/xml/nginx/changes.xml ++ ++ ++docs/xslt/changes.xslt: docs/xsls/changes.xsls ++ ++ $(XSLS) -o $@ $< +diff -urN nginx-1.24.0/docs/html/50x.html nginx/docs/html/50x.html +--- nginx-1.24.0/docs/html/50x.html 1970-01-01 05:30:00.000000000 +0530 ++++ nginx/docs/html/50x.html 2023-09-14 18:47:11.586451000 +0530 +@@ -0,0 +1,19 @@ ++ ++ ++ ++Error ++ ++ ++ ++

An error occurred.

++

Sorry, the page you are looking for is currently unavailable.
++Please try again later.

++

If you are the system administrator of this resource then you should check ++the error log for details.

++

Faithfully yours, nginx.

++ ++ +diff -urN nginx-1.24.0/docs/html/index.html nginx/docs/html/index.html +--- nginx-1.24.0/docs/html/index.html 1970-01-01 05:30:00.000000000 +0530 ++++ nginx/docs/html/index.html 2023-09-14 18:47:11.591663200 +0530 +@@ -0,0 +1,23 @@ ++ ++ ++ ++Welcome to nginx! ++ ++ ++ ++

Welcome to nginx!

++

If you see this page, the nginx web server is successfully installed and ++working. Further configuration is required.

++ ++

For online documentation and support please refer to ++nginx.org.
++Commercial support is available at ++nginx.com.

++ ++

Thank you for using nginx.

++ ++ +diff -urN nginx-1.24.0/docs/html/zmerror_upstream_502.html nginx/docs/html/zmerror_upstream_502.html +--- nginx-1.24.0/docs/html/zmerror_upstream_502.html 1970-01-01 05:30:00.000000000 +0530 ++++ nginx/docs/html/zmerror_upstream_502.html 2023-03-04 13:25:59.800430200 +0530 +@@ -0,0 +1,29 @@ ++ ++ ++ ++Error 502 Connection to Upstream is Refused ++ ++ ++

HTTP ERROR 502

++

Problem accessing ZCS upstream server. ++ Cannot connect to the ZCS upstream server. Connection is refused.
++ Possible reasons: ++

++ Please contact your ZCS administrator to fix the problem. ++


++Powered by Nginx-Zimbra://
++
++
++
++
++
++
++
++
++ ++ ++ +diff -urN nginx-1.24.0/docs/html/zmerror_upstream_504.html nginx/docs/html/zmerror_upstream_504.html +--- nginx-1.24.0/docs/html/zmerror_upstream_504.html 1970-01-01 05:30:00.000000000 +0530 ++++ nginx/docs/html/zmerror_upstream_504.html 2023-03-04 13:25:59.801427300 +0530 +@@ -0,0 +1,30 @@ ++ ++ ++ ++Error 504 Connection to Upstream is Time Out ++ ++ ++

HTTP ERROR 504

++

Problem accessing ZCS upstream server. Reason: ++ Cannot connect to the ZCS upstream server. Connection timeout.
++ Possible reasons: ++

++ Please contact your ZCS administrator to fix the problem. ++


++Powered by Nginx-Zimbra://
++
++
++
++
++
++
++
++
++ ++ ++ ++ +diff -urN nginx-1.24.0/docs/man/nginx.8 nginx/docs/man/nginx.8 +--- nginx-1.24.0/docs/man/nginx.8 1970-01-01 05:30:00.000000000 +0530 ++++ nginx/docs/man/nginx.8 2023-03-04 13:25:23.463143700 +0530 +@@ -0,0 +1,214 @@ ++.\" ++.\" Copyright (C) 2010, 2019 Sergey A. Osokin ++.\" Copyright (C) Nginx, Inc. ++.\" All rights reserved. ++.\" ++.\" Redistribution and use in source and binary forms, with or without ++.\" modification, are permitted provided that the following conditions ++.\" are met: ++.\" 1. Redistributions of source code must retain the above copyright ++.\" notice, this list of conditions and the following disclaimer. ++.\" 2. Redistributions in binary form must reproduce the above copyright ++.\" notice, this list of conditions and the following disclaimer in the ++.\" documentation and/or other materials provided with the distribution. ++.\" ++.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ++.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ++.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ++.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ++.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ++.\" SUCH DAMAGE. ++.\" ++.\" ++.Dd November 5, 2020 ++.Dt NGINX 8 ++.Os ++.Sh NAME ++.Nm nginx ++.Nd "HTTP and reverse proxy server, mail proxy server" ++.Sh SYNOPSIS ++.Nm ++.Op Fl ?hqTtVv ++.Op Fl c Ar file ++.Op Fl e Ar file ++.Op Fl g Ar directives ++.Op Fl p Ar prefix ++.Op Fl s Ar signal ++.Sh DESCRIPTION ++.Nm ++(pronounced ++.Dq engine x ) ++is an HTTP and reverse proxy server, a mail proxy server, and a generic ++TCP/UDP proxy server. ++It is known for its high performance, stability, rich feature set, simple ++configuration, and low resource consumption. ++.Pp ++The options are as follows: ++.Bl -tag -width ".Fl d Ar directives" ++.It Fl ?\& , h ++Print help. ++.It Fl c Ar file ++Use an alternative configuration ++.Ar file . ++.It Fl e Ar file ++Use an alternative error log ++.Ar file . ++Special value ++.Cm stderr ++indicates that the standard error output should be used. ++.It Fl g Ar directives ++Set global configuration directives. ++See ++.Sx EXAMPLES ++for details. ++.It Fl p Ar prefix ++Set the prefix path. ++The default value is ++.Pa %%PREFIX%% . ++.It Fl q ++Suppress non-error messages during configuration testing. ++.It Fl s Ar signal ++Send a signal to the master process. ++The argument ++.Ar signal ++can be one of: ++.Cm stop , quit , reopen , reload . ++The following table shows the corresponding system signals: ++.Pp ++.Bl -tag -width ".Cm reopen" -compact ++.It Cm stop ++.Dv SIGTERM ++.It Cm quit ++.Dv SIGQUIT ++.It Cm reopen ++.Dv SIGUSR1 ++.It Cm reload ++.Dv SIGHUP ++.El ++.It Fl T ++Same as ++.Fl t , ++but additionally dump configuration files to standard output. ++.It Fl t ++Do not run, just test the configuration file. ++.Nm ++checks the configuration file syntax and then tries to open files ++referenced in the configuration file. ++.It Fl V ++Print the ++.Nm ++version, compiler version, and ++.Pa configure ++script parameters. ++.It Fl v ++Print the ++.Nm ++version. ++.El ++.Sh SIGNALS ++The master process of ++.Nm ++can handle the following signals: ++.Pp ++.Bl -tag -width ".Dv SIGINT , SIGTERM" -compact ++.It Dv SIGINT , SIGTERM ++Shut down quickly. ++.It Dv SIGHUP ++Reload configuration, start the new worker process with a new ++configuration, and gracefully shut down old worker processes. ++.It Dv SIGQUIT ++Shut down gracefully. ++.It Dv SIGUSR1 ++Reopen log files. ++.It Dv SIGUSR2 ++Upgrade the ++.Nm ++executable on the fly. ++.It Dv SIGWINCH ++Shut down worker processes gracefully. ++.El ++.Pp ++While there is no need to explicitly control worker processes normally, ++they support some signals too: ++.Pp ++.Bl -tag -width ".Dv SIGINT , SIGTERM" -compact ++.It Dv SIGTERM ++Shut down quickly. ++.It Dv SIGQUIT ++Shut down gracefully. ++.It Dv SIGUSR1 ++Reopen log files. ++.El ++.Sh DEBUGGING LOG ++To enable a debugging log, reconfigure ++.Nm ++to build with debugging: ++.Pp ++.Dl "./configure --with-debug ..." ++.Pp ++and then set the ++.Cm debug ++level of the ++.Va error_log : ++.Pp ++.Dl "error_log /path/to/log debug;" ++.Pp ++It is also possible to enable the debugging for a particular IP address: ++.Bd -literal -offset indent ++events { ++ debug_connection 127.0.0.1; ++} ++.Ed ++.Sh ENVIRONMENT ++The ++.Ev NGINX ++environment variable is used internally by ++.Nm ++and should not be set directly by the user. ++.Sh FILES ++.Bl -tag -width indent ++.It Pa %%PID_PATH%% ++Contains the process ID of ++.Nm . ++The contents of this file are not sensitive, so it can be world-readable. ++.It Pa %%CONF_PATH%% ++The main configuration file. ++.It Pa %%ERROR_LOG_PATH%% ++Error log file. ++.El ++.Sh EXIT STATUS ++Exit status is 0 on success, or 1 if the command fails. ++.Sh EXAMPLES ++Test configuration file ++.Pa ~/mynginx.conf ++with global directives for PID and quantity of worker processes: ++.Bd -literal -offset indent ++nginx -t -c ~/mynginx.conf \e ++ -g "pid /var/run/mynginx.pid; worker_processes 2;" ++.Ed ++.Sh SEE ALSO ++.\"Xr nginx.conf 5 ++.\"Pp ++Documentation at ++.Pa http://nginx.org/en/docs/ . ++.Pp ++For questions and technical support, please refer to ++.Pa http://nginx.org/en/support.html . ++.Sh HISTORY ++Development of ++.Nm ++started in 2002, with the first public release on October 4, 2004. ++.Sh AUTHORS ++.An -nosplit ++.An Igor Sysoev Aq Mt igor@sysoev.ru . ++.Pp ++This manual page was originally written by ++.An Sergey A. Osokin Aq Mt osa@FreeBSD.org.ru ++as a result of compiling many ++.Nm ++documents from all over the world. +diff -urN nginx-1.24.0/docs/text/LICENSE nginx/docs/text/LICENSE +--- nginx-1.24.0/docs/text/LICENSE 1970-01-01 05:30:00.000000000 +0530 ++++ nginx/docs/text/LICENSE 2023-09-14 18:47:11.595428500 +0530 +@@ -0,0 +1,26 @@ ++/* ++ * Copyright (C) 2002-2021 Igor Sysoev ++ * Copyright (C) 2011-2022 Nginx, Inc. ++ * All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ++ * SUCH DAMAGE. ++ */ +diff -urN nginx-1.24.0/docs/text/README nginx/docs/text/README +--- nginx-1.24.0/docs/text/README 1970-01-01 05:30:00.000000000 +0530 ++++ nginx/docs/text/README 2023-03-04 13:25:23.466101800 +0530 +@@ -0,0 +1,3 @@ ++ ++Documentation is available at http://nginx.org ++ +diff -urN nginx-1.24.0/docs/xml/change_log_conf.xml nginx/docs/xml/change_log_conf.xml +--- nginx-1.24.0/docs/xml/change_log_conf.xml 1970-01-01 05:30:00.000000000 +0530 ++++ nginx/docs/xml/change_log_conf.xml 2023-03-04 13:25:23.467099100 +0530 +@@ -0,0 +1,47 @@ ++ ++ ++ ++ ++ ++76 ++ ++ *) ++ ++ ++ ++ Изменения в ++ 66 ++ ++ Исправление ++ Добавление ++ Изменение ++ Безопасность ++ Изменение ++ ++ ++ ++ Changes with ++ 65 ++ ++ Bugfix ++ Feature ++ Change ++ Security ++ Workaround ++ ++ Jan ++ Feb ++ Mar ++ Apr ++ May ++ Jun ++ Jul ++ Aug ++ Sep ++ Oct ++ Nov ++ Dec ++ ++ ++ ++ +diff -urN nginx-1.24.0/docs/xml/nginx/changes.xml nginx/docs/xml/nginx/changes.xml +--- nginx-1.24.0/docs/xml/nginx/changes.xml 1970-01-01 05:30:00.000000000 +0530 ++++ nginx/docs/xml/nginx/changes.xml 2023-09-14 18:47:11.612380300 +0530 +@@ -0,0 +1,29894 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++Стабильная ветка 1.24.x. ++ ++ ++1.24.x stable branch. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++теперь протокол TLSv1.3 разрешён по умолчанию. ++ ++ ++now TLSv1.3 protocol is enabled by default. ++ ++ ++ ++ ++ ++теперь nginx выдаёт предупреждение ++при переопределении параметров listen-сокета, задающих используемые протоколы. ++ ++ ++now nginx issues a warning ++if protocol parameters of a listening socket are redefined. ++ ++ ++ ++ ++ ++теперь, если клиент использует pipelining, ++nginx закрывает соединения с ожиданием дополнительных данных (lingering close). ++ ++ ++now nginx closes connections with lingering ++if pipelining was used by the client. ++ ++ ++ ++ ++ ++поддержка byte ranges для ответов модуля ngx_http_gzip_static_module. ++ ++ ++byte ranges support in the ngx_http_gzip_static_module. ++ ++ ++ ++ ++ ++диапазоны портов в директиве listen не работали; ++ошибка появилась в 1.23.3.
++Спасибо Валентину Бартеневу. ++
++ ++port ranges in the "listen" directive did not work; ++the bug had appeared in 1.23.3.
++Thanks to Valentin Bartenev. ++
++
++ ++ ++ ++для обработки запроса мог быть выбран неверный location, ++если в конфигурации использовался ++префиксный location длиннее 255 символов. ++ ++ ++incorrect location might be chosen to process a request ++if a prefix location longer than 255 characters ++was used in the configuration. ++ ++ ++ ++ ++ ++не-ASCII символы в именах файлов на Windows ++не поддерживались модулями ngx_http_autoindex_module и ++ngx_http_dav_module, а также директивой include. ++ ++ ++non-ASCII characters in file names on Windows were not supported ++by the ngx_http_autoindex_module, the ngx_http_dav_module, ++and the "include" directive. ++ ++ ++ ++ ++ ++уровень логгирования ошибок SSL ++"data length too long", "length too short", "bad legacy version", ++"no shared signature algorithms", "bad digest length", ++"missing sigalgs extension", "encrypted length too long", ++"bad length", "bad key update", "mixed handshake and non handshake data", ++"ccs received early", "data between ccs and finished", ++"packet length too long", "too many warn alerts", "record too small", ++и "got a fin before a ccs" ++понижен с уровня crit до info. ++ ++ ++the logging level of the ++"data length too long", "length too short", "bad legacy version", ++"no shared signature algorithms", "bad digest length", ++"missing sigalgs extension", "encrypted length too long", ++"bad length", "bad key update", "mixed handshake and non handshake data", ++"ccs received early", "data between ccs and finished", ++"packet length too long", "too many warn alerts", "record too small", ++and "got a fin before a ccs" SSL errors ++has been lowered from "crit" to "info". ++ ++ ++ ++ ++ ++при использовании HTTP/2 и директивы error_page ++для перенаправления ошибок с кодом 400 ++могла происходить утечка сокетов. ++ ++ ++a socket leak might occur ++when using HTTP/2 and the "error_page" directive ++to redirect errors with code 400. ++ ++ ++ ++ ++ ++сообщения об ошибках записи в syslog ++не содержали информации о том, что ++ошибки происходили в процессе записи в syslog.
++Спасибо Safar Safarly. ++
++ ++messages about logging to syslog errors ++did not contain information ++that the errors happened while logging to syslog.
++Thanks to Safar Safarly. ++
++
++ ++ ++ ++при использовании zlib-ng ++в логах появлялись сообщения "gzip filter failed to use preallocated memory". ++ ++ ++"gzip filter failed to use preallocated memory" alerts appeared in logs ++when using zlib-ng. ++ ++ ++ ++ ++ ++в почтовом прокси-сервере. ++ ++ ++in the mail proxy server. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++при чтении заголовка протокола PROXY версии 2, содержащего ++большое количество TLV, могла возникать ошибка. ++ ++ ++an error might occur when reading PROXY protocol version 2 header ++with large number of TLVs. ++ ++ ++ ++ ++ ++при использовании SSI для обработки подзапросов, созданных другими модулями, ++в рабочем процессе мог произойти segmentation fault.
++Спасибо Ciel Zhao. ++
++ ++a segmentation fault might occur in a worker process ++if SSI was used to process subrequests created by other modules.
++Thanks to Ciel Zhao. ++
++
++ ++ ++ ++теперь, если при преобразовании в адреса имени хоста, ++указанного в директиве listen, возвращается несколько адресов, ++nginx игнорирует дубликаты среди этих адресов. ++ ++ ++when a hostname used in the "listen" directive ++resolves to multiple addresses, ++nginx now ignores duplicates within these addresses. ++ ++ ++ ++ ++ ++nginx мог нагружать процессор ++при небуферизированном проксировании, ++если использовались SSL-соединения с бэкендами. ++ ++ ++nginx might hog CPU ++during unbuffered proxying ++if SSL connections to backends were used. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++обработка специально созданного mp4-файла модулем ngx_http_mp4_module ++могла приводить к падению рабочего процесса, ++отправке клиенту части содержимого памяти рабочего процесса, ++а также потенциально могла иметь другие последствия ++(CVE-2022-41741, CVE-2022-41742). ++ ++ ++processing of a specially crafted mp4 file by the ngx_http_mp4_module ++might cause a worker process crash, ++worker process memory disclosure, ++or might have potential other impact ++(CVE-2022-41741, CVE-2022-41742). ++ ++ ++ ++ ++ ++переменные "$proxy_protocol_tlv_...". ++ ++ ++the "$proxy_protocol_tlv_..." variables. ++ ++ ++ ++ ++ ++ключи шифрования TLS session tickets теперь автоматически меняются ++при использовании разделяемой памяти в ssl_session_cache. ++ ++ ++TLS session tickets encryption keys are now automatically rotated ++when using shared memory in the "ssl_session_cache" directive. ++ ++ ++ ++ ++ ++уровень логгирования ошибок SSL "bad record type" ++понижен с уровня crit до info.
++Спасибо Murilo Andrade. ++
++ ++the logging level of the "bad record type" SSL errors ++has been lowered from "crit" to "info".
++Thanks to Murilo Andrade. ++
++
++ ++ ++ ++теперь при использовании разделяемой памяти в ssl_session_cache ++сообщения "could not allocate new session" ++логгируются на уровне warn вместо alert ++и не чаще одного раза в секунду. ++ ++ ++now when using shared memory in the "ssl_session_cache" directive ++the "could not allocate new session" errors ++are logged at the "warn" level instead of "alert" ++and not more often than once per second. ++ ++ ++ ++ ++ ++nginx/Windows не собирался с OpenSSL 3.0.x. ++ ++ ++nginx/Windows could not be built with OpenSSL 3.0.x. ++ ++ ++ ++ ++ ++в логгировании ошибок протокола PROXY.
++Спасибо Сергею Брестеру. ++
++ ++in logging of the PROXY protocol errors.
++Thanks to Sergey Brester. ++
++
++ ++ ++ ++при использовании TLSv1.3 с OpenSSL ++разделяемая память из ssl_session_cache расходовалась ++в том числе на сессии, использующие TLS session tickets. ++ ++ ++shared memory from the "ssl_session_cache" directive ++was spent on sessions using TLS session tickets ++when using TLSv1.3 with OpenSSL. ++ ++ ++ ++ ++ ++таймаут, заданный с помощью директивы ssl_session_timeout, ++не работал при использовании TLSv1.3 с OpenSSL или BoringSSL. ++ ++ ++timeout specified with the "ssl_session_timeout" directive ++did not work when using TLSv1.3 with OpenSSL or BoringSSL. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++оптимизация использования памяти ++в конфигурациях с SSL-проксированием. ++ ++ ++memory usage optimization ++in configurations with SSL proxying. ++ ++ ++ ++ ++ ++теперь с помощью параметра "ipv4=off" директивы "resolver" ++можно запретить поиск IPv4-адресов при преобразовании имён в адреса. ++ ++ ++looking up of IPv4 addresses while resolving now can be disabled ++with the "ipv4=off" parameter of the "resolver" directive. ++ ++ ++ ++ ++ ++уровень логгирования ошибок SSL "bad key share", "bad extension", ++"bad cipher" и "bad ecpoint" ++понижен с уровня crit до info. ++ ++ ++the logging level of the "bad key share", "bad extension", ++"bad cipher", and "bad ecpoint" SSL errors ++has been lowered from "crit" to "info". ++ ++ ++ ++ ++ ++при возврате диапазонов ++nginx не удалял строку заголовка "Content-Range", ++если она присутствовала в исходном ответе бэкенда. ++ ++ ++while returning byte ranges ++nginx did not remove the "Content-Range" header line ++if it was present in the original backend response. ++ ++ ++ ++ ++ ++проксированный ответ мог быть отправлен не полностью ++при переконфигурации на Linux; ++ошибка появилась в 1.17.5. ++ ++ ++a proxied response might be truncated ++during reconfiguration on Linux; ++the bug had appeared in 1.17.5. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++Изменение во внутреннем API: ++теперь строки заголовков представлены связными списками. ++ ++ ++Change in internal API: ++now header lines are represented as linked lists. ++ ++ ++ ++ ++ ++теперь nginx объединяет произвольные строки заголовков с одинаковыми именами ++при отправке на FastCGI-, SCGI- и uwsgi-бэкенды, ++в методе $r->header_in() модуля ngx_http_perl_module, ++и при доступе через переменные "$http_...", "$sent_http_...", ++"$sent_trailer_...", "$upstream_http_..." и "$upstream_trailer_...". ++ ++ ++now nginx combines arbitrary header lines with identical names ++when sending to FastCGI, SCGI, and uwsgi backends, ++in the $r->header_in() method of the ngx_http_perl_module, ++and during lookup of the "$http_...", "$sent_http_...", ++"$sent_trailer_...", "$upstream_http_...", and "$upstream_trailer_..." ++variables. ++ ++ ++ ++ ++ ++если в заголовке ответа бэкенда было несколько строк "Vary", ++при кэшировании nginx учитывал только последнюю из них. ++ ++ ++if there were multiple "Vary" header lines in the backend response, ++nginx only used the last of them when caching. ++ ++ ++ ++ ++ ++если в заголовке ответа бэкенда было несколько строк "WWW-Authenticate" ++и использовался перехват ошибок с кодом 401 от бэкенда ++или директива auth_request, ++nginx пересылал клиенту только первую из этих строк. ++ ++ ++if there were multiple "WWW-Authenticate" header lines in the backend response ++and errors with code 401 were intercepted ++or the "auth_request" directive was used, ++nginx only sent the first of the header lines to the client. ++ ++ ++ ++ ++ ++уровень логгирования ошибок SSL "application data after close notify" ++понижен с уровня crit до info. ++ ++ ++the logging level of the "application data after close notify" SSL errors ++has been lowered from "crit" to "info". ++ ++ ++ ++ ++ ++соединения могли зависать, если nginx был собран на Linux 2.6.17 и новее, ++а использовался на системах без поддержки EPOLLRDHUP, в частности, на ++системах с эмуляцией epoll; ++ошибка появилась в 1.17.5.
++Спасибо Marcus Ball. ++
++ ++connections might hang if nginx was built on Linux 2.6.17 or newer, ++but was used on systems without EPOLLRDHUP support, notably with epoll ++emulation layers; ++the bug had appeared in 1.17.5.
++Thanks to Marcus Ball. ++
++
++ ++ ++ ++nginx не кэшировал ответ, ++если строка заголовка ответа "Expires" запрещала кэширование, ++а последующая строка заголовка "Cache-Control" разрешала кэширование. ++ ++ ++nginx did not cache the response ++if the "Expires" response header line disabled caching, ++but following "Cache-Control" header line enabled caching. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++при использование EPOLLEXCLUSIVE на Linux ++распределение клиентских соединений между рабочими процессами ++было неравномерным. ++ ++ ++when using EPOLLEXCLUSIVE on Linux ++client connections were unevenly distributed ++among worker processes. ++ ++ ++ ++ ++ ++во время плавного завершения старых рабочих процессов ++nginx возвращал в ответах строку заголовка "Connection: keep-alive". ++ ++ ++nginx returned the "Connection: keep-alive" header line in responses ++during graceful shutdown of old worker processes. ++ ++ ++ ++ ++ ++в директиве ssl_session_ticket_key при использовании TLSv1.3. ++ ++ ++in the "ssl_session_ticket_key" when using TLSv1.3. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++теперь nginx по умолчанию собирается с библиотекой PCRE2. ++ ++ ++now nginx is built with the PCRE2 library by default. ++ ++ ++ ++ ++ ++теперь nginx всегда использует sendfile(SF_NODISKIO) на FreeBSD. ++ ++ ++now nginx always uses sendfile(SF_NODISKIO) on FreeBSD. ++ ++ ++ ++ ++ ++поддержка sendfile(SF_NOCACHE) на FreeBSD. ++ ++ ++support for sendfile(SF_NOCACHE) on FreeBSD. ++ ++ ++ ++ ++ ++переменная $ssl_curve. ++ ++ ++the $ssl_curve variable. ++ ++ ++ ++ ++ ++при использовании HTTP/2 без SSL вместе с директивами sendfile и aio ++соединения могли зависать. ++ ++ ++connections might hang ++when using HTTP/2 without SSL with the "sendfile" and "aio" directives. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++поддержка NPN вместо ALPN для установления HTTP/2-соединений ++упразднена. ++ ++ ++support for NPN instead of ALPN to establish HTTP/2 connections ++has been removed. ++ ++ ++ ++ ++ ++теперь nginx закрывает SSL соединение, если клиент использует ALPN, ++но nginx не поддерживает ни один из присланных клиентом протоколов. ++ ++ ++now nginx rejects SSL connections if ALPN is used by the client, ++but no supported protocols can be negotiated. ++ ++ ++ ++ ++ ++в директиве sendfile_max_chunk значение по умолчанию ++изменено на 2 мегабайта. ++ ++ ++the default value of the "sendfile_max_chunk" directive ++was changed to 2 megabytes. ++ ++ ++ ++ ++ ++директива proxy_half_close в модуле stream. ++ ++ ++the "proxy_half_close" directive in the stream module. ++ ++ ++ ++ ++ ++директива ssl_alpn в модуле stream. ++ ++ ++the "ssl_alpn" directive in the stream module. ++ ++ ++ ++ ++ ++переменная $ssl_alpn_protocol. ++ ++ ++the $ssl_alpn_protocol variable. ++ ++ ++ ++ ++ ++поддержка SSL_sendfile() при использовании OpenSSL 3.0. ++ ++ ++support for SSL_sendfile() when using OpenSSL 3.0. ++ ++ ++ ++ ++ ++директива mp4_start_key_frame в модуле ngx_http_mp4_module.
++Спасибо Tracey Jaquith. ++
++ ++the "mp4_start_key_frame" directive in the ngx_http_mp4_module.
++Thanks to Tracey Jaquith. ++
++
++ ++ ++ ++в переменной $content_length при использовании chunked transfer encoding. ++ ++ ++in the $content_length variable when using chunked transfer encoding. ++ ++ ++ ++ ++ ++при получении ответа некорректной длины от проксируемого бэкенда ++nginx мог тем не менее закэшировать соединение.
++Спасибо Awdhesh Mathpal. ++
++ ++after receiving a response with incorrect length from a proxied backend ++nginx might nevertheless cache the connection.
++Thanks to Awdhesh Mathpal. ++
++
++ ++ ++ ++некорректные заголовки от бэкендов ++логгировались на уровне info вместо error; ++ошибка появилась в 1.21.1. ++ ++ ++invalid headers from backends ++were logged at the "info" level instead of "error"; ++the bug had appeared in 1.21.1. ++ ++ ++ ++ ++ ++при использовании HTTP/2 и директивы aio_write ++запросы могли зависать. ++ ++ ++requests might hang ++when using HTTP/2 and the "aio_write" directive. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++оптимизация чтения тела запроса ++при использовании HTTP/2. ++ ++ ++optimization of client request body reading ++when using HTTP/2. ++ ++ ++ ++ ++ ++во внутреннем API для обработки тела запроса ++при использовании HTTP/2 и буферизации обрабатываемых данных. ++ ++ ++in request body filters internal API ++when using HTTP/2 and buffering of the data being processed. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++теперь nginx возвращает ошибку, ++если в запросе по протоколу HTTP/1.0 присутствует ++строка заголовка "Transfer-Encoding". ++ ++ ++now nginx rejects HTTP/1.0 requests ++with the "Transfer-Encoding" header line. ++ ++ ++ ++ ++ ++экспортные шифры больше не поддерживаются. ++ ++ ++export ciphers are no longer supported. ++ ++ ++ ++ ++ ++совместимость с OpenSSL 3.0. ++ ++ ++OpenSSL 3.0 compatibility. ++ ++ ++ ++ ++ ++теперь серверу аутентификации почтового прокси-сервера ++передаются строки заголовка "Auth-SSL-Protocol" и "Auth-SSL-Cipher".
++Спасибо Rob Mueller. ++
++ ++the "Auth-SSL-Protocol" and "Auth-SSL-Cipher" header lines ++are now passed to the mail proxy authentication server.
++Thanks to Rob Mueller. ++
++
++ ++ ++ ++API для обработки тела запроса ++теперь позволяет буферизировать обрабатываемые данные. ++ ++ ++request body filters API ++now permits buffering of the data being processed. ++ ++ ++ ++ ++ ++SSL-соединения к бэкендам в модуле stream ++могли зависать после SSL handshake. ++ ++ ++backend SSL connections in the stream module ++might hang after an SSL handshake. ++ ++ ++ ++ ++ ++уровень безопасности, доступный в OpenSSL 1.1.0 и новее, ++не учитывался при загрузке сертификатов сервера, ++если был задан через "@SECLEVEL=N" в директиве ssl_ciphers. ++ ++ ++the security level, which is available in OpenSSL 1.1.0 or newer, ++did not affect loading of the server certificates ++when set with "@SECLEVEL=N" in the "ssl_ciphers" directive. ++ ++ ++ ++ ++ ++SSL-соединения с gRPC-бэкендами могли зависать, ++если использовались методы select, poll или /dev/poll. ++ ++ ++SSL connections with gRPC backends might hang ++if select, poll, or /dev/poll methods were used. ++ ++ ++ ++ ++ ++при использовании HTTP/2 ++тело запроса всегда записывалось на диск, ++если в запросе не было строки заголовка "Content-Length". ++ ++ ++when using HTTP/2 ++client request body was always written to disk ++if the "Content-Length" header line was not present in the request. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++теперь nginx для метода CONNECT всегда возвращает ошибку. ++ ++ ++now nginx always returns an error for the CONNECT method. ++ ++ ++ ++ ++ ++теперь nginx всегда возвращает ошибку, ++если в запросе одновременно присутствуют строки заголовка "Content-Length" ++и "Transfer-Encoding". ++ ++ ++now nginx always returns an error ++if both "Content-Length" and "Transfer-Encoding" header lines ++are present in the request. ++ ++ ++ ++ ++ ++теперь nginx всегда возвращает ошибку, ++если в строке запроса используются пробелы или управляющие символы. ++ ++ ++now nginx always returns an error ++if spaces or control characters are used in the request line. ++ ++ ++ ++ ++ ++теперь nginx всегда возвращает ошибку, ++если в имени заголовка используются пробелы или управляющие символы. ++ ++ ++now nginx always returns an error ++if spaces or control characters are used in a header name. ++ ++ ++ ++ ++ ++теперь nginx всегда возвращает ошибку, ++если в строке "Host" заголовка запроса ++используются пробелы или управляющие символы. ++ ++ ++now nginx always returns an error ++if spaces or control characters ++are used in the "Host" request header line. ++ ++ ++ ++ ++ ++оптимизация тестирования конфигурации ++при использовании большого количества listen-сокетов. ++ ++ ++optimization of configuration testing ++when using many listening sockets. ++ ++ ++ ++ ++ ++nginx не экранировал ++символы """, "<", ">", "\", "^", "`", "{", "|", и "}" ++при проксировании с изменением URI запроса. ++ ++ ++nginx did not escape ++""", "<", ">", "\", "^", "`", "{", "|", and "}" characters ++when proxying with changed URI. ++ ++ ++ ++ ++ ++SSL-переменные могли быть пустыми при записи в лог; ++ошибка появилась в 1.19.5. ++ ++ ++SSL variables might be empty when used in logs; ++the bug had appeared in 1.19.5. ++ ++ ++ ++ ++ ++keepalive-соединения с gRPC-бэкендами могли не закрываться ++после получения GOAWAY-фрейма. ++ ++ ++keepalive connections with gRPC backends might not be closed ++after receiving a GOAWAY frame. ++ ++ ++ ++ ++ ++уменьшено потребление памяти для долгоживущих запросов ++при проксировании с использованием более 64 буферов. ++ ++ ++reduced memory consumption for long-lived requests ++when proxying with more than 64 buffers. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++при использовании директивы resolver ++во время обработки ответа DNS-сервера ++могла происходить перезапись одного байта памяти, ++что позволяло атакующему, ++имеющему возможность подделывать UDP-пакеты от DNS-сервера, ++вызвать падение рабочего процесса ++или, потенциально, выполнение произвольного кода (CVE-2021-23017). ++ ++ ++1-byte memory overwrite might occur ++during DNS server response processing ++if the "resolver" directive was used, ++allowing an attacker ++who is able to forge UDP packets from the DNS server ++to cause worker process crash ++or, potentially, arbitrary code execution (CVE-2021-23017). ++ ++ ++ ++ ++ ++директивы proxy_ssl_certificate, proxy_ssl_certificate_key, ++grpc_ssl_certificate, grpc_ssl_certificate_key, ++uwsgi_ssl_certificate и uwsgi_ssl_certificate_key ++поддерживают переменные. ++ ++ ++variables support ++in the "proxy_ssl_certificate", "proxy_ssl_certificate_key" ++"grpc_ssl_certificate", "grpc_ssl_certificate_key", ++"uwsgi_ssl_certificate", and "uwsgi_ssl_certificate_key" directives. ++ ++ ++ ++ ++ ++директива max_errors в почтовом прокси-сервере. ++ ++ ++the "max_errors" directive in the mail proxy module. ++ ++ ++ ++ ++ ++почтовый прокси-сервер поддерживает POP3 и IMAP pipelining. ++ ++ ++the mail proxy module supports POP3 and IMAP pipelining. ++ ++ ++ ++ ++ ++параметр fastopen директивы listen в модуле stream.
++Спасибо Anbang Wen. ++
++ ++the "fastopen" parameter of the "listen" directive in the stream module.
++Thanks to Anbang Wen. ++
++
++ ++ ++ ++специальные символы не экранировались ++при автоматическом перенаправлении с добавлением завершающего слэша. ++ ++ ++special characters were not escaped ++during automatic redirect with appended trailing slash. ++ ++ ++ ++ ++ ++при использовании SMTP pipelining ++соединения с клиентами в почтовом прокси-сервере ++могли неожиданно закрываться. ++ ++ ++connections with clients in the mail proxy module ++might be closed unexpectedly ++when using SMTP pipelining. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++в директиве keepalive_requests значение по умолчанию изменено на 1000. ++ ++ ++the default value of the "keepalive_requests" directive was changed to 1000. ++ ++ ++ ++ ++ ++директива keepalive_time. ++ ++ ++the "keepalive_time" directive. ++ ++ ++ ++ ++ ++переменная $connection_time. ++ ++ ++the $connection_time variable. ++ ++ ++ ++ ++ ++при использовании zlib-ng ++в логах появлялись сообщения "gzip filter failed to use preallocated memory". ++ ++ ++"gzip filter failed to use preallocated memory" alerts appeared in logs ++when using zlib-ng. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++nginx не собирался с почтовым прокси-сервером, ++но без модуля ngx_mail_ssl_module; ++ошибка появилась в 1.19.8. ++ ++ ++nginx could not be built with the mail proxy module, ++but without the ngx_mail_ssl_module; ++the bug had appeared in 1.19.8. ++ ++ ++ ++ ++ ++при работе с gRPC-бэкендами могли возникать ошибки ++"upstream sent response body larger than indicated content length"; ++ошибка появилась в 1.19.1. ++ ++ ++"upstream sent response body larger than indicated content length" ++errors might occur when working with gRPC backends; ++the bug had appeared in 1.19.1. ++ ++ ++ ++ ++ ++если клиент закрывал соединение в момент отбрасывания тела запроса, ++nginx мог не закрыть соединение до истечения keepalive-таймаута. ++ ++ ++nginx might not close a connection till keepalive timeout expiration ++if the connection was closed by the client while discarding the request body. ++ ++ ++ ++ ++ ++при ожидании задержки limit_req или auth_delay, а также при работе с бэкендами ++nginx мог не обнаружить, что соединение уже закрыто клиентом. ++ ++ ++nginx might not detect that a connection was already closed by the client ++when waiting for auth_delay or limit_req delay, or when working with backends. ++ ++ ++ ++ ++ ++в методе обработки соединений eventport. ++ ++ ++in the eventport method. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++в директиве proxy_cookie_flags теперь ++флаги можно задавать с помощью переменных. ++ ++ ++flags in the "proxy_cookie_flags" directive ++can now contain variables. ++ ++ ++ ++ ++ ++параметр proxy_protocol в директиве listen, ++директивы proxy_protocol и set_real_ip_from ++в почтовом прокси-сервере. ++ ++ ++the "proxy_protocol" parameter of the "listen" directive, ++the "proxy_protocol" and "set_real_ip_from" directives ++in mail proxy. ++ ++ ++ ++ ++ ++HTTP/2-соединения сразу закрывались ++при использовании "keepalive_timeout 0"; ++ошибка появилась в 1.19.7. ++ ++ ++HTTP/2 connections were immediately closed ++when using "keepalive_timeout 0"; ++the bug had appeared in 1.19.7. ++ ++ ++ ++ ++ ++некоторые ошибки логгировались как неизвестные, ++если nginx был собран с glibc 2.32. ++ ++ ++some errors were logged as unknown ++if nginx was built with glibc 2.32. ++ ++ ++ ++ ++ ++в методе обработки соединений eventport. ++ ++ ++in the eventport method. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++обработка соединений в HTTP/2 была изменена ++и теперь более соответствует HTTP/1.x; ++директивы http2_recv_timeout, http2_idle_timeout ++и http2_max_requests упразднены, ++вместо них следует использовать директивы ++keepalive_timeout и keepalive_requests. ++ ++ ++connections handling in HTTP/2 has been changed ++to better match HTTP/1.x; ++the "http2_recv_timeout", "http2_idle_timeout", ++and "http2_max_requests" directives have been removed, ++the "keepalive_timeout" and "keepalive_requests" directives ++should be used instead. ++ ++ ++ ++ ++ ++директивы http2_max_field_size и http2_max_header_size упразднены, ++вместо них следует использовать директиву large_client_header_buffers. ++ ++ ++the "http2_max_field_size" and "http2_max_header_size" directives ++have been removed, ++the "large_client_header_buffers" directive should be used instead. ++ ++ ++ ++ ++ ++теперь при исчерпании свободных соединений ++nginx закрывает не только keepalive-соединения, ++но и соединения в lingering close. ++ ++ ++now, if free worker connections are exhausted, ++nginx starts closing not only keepalive connections, ++but also connections in lingering close. ++ ++ ++ ++ ++ ++в логах могли появляться сообщения "zero size buf in output", ++если бэкенд возвращал некорректный ответ ++при небуферизированном проксировании; ++ошибка появилась в 1.19.1. ++ ++ ++"zero size buf in output" alerts might appear in logs ++if an upstream server returned an incorrect response ++during unbuffered proxying; ++the bug had appeared in 1.19.1. ++ ++ ++ ++ ++ ++при использовании директивы return ++вместе с image_filter или xslt_stylesheet ++HEAD-запросы обрабатывались некорректно. ++ ++ ++HEAD requests were handled incorrectly ++if the "return" directive was used ++with the "image_filter" or "xslt_stylesheet" directives. ++ ++ ++ ++ ++ ++в директиве add_trailer. ++ ++ ++in the "add_trailer" directive. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ошибки "no live upstreams", ++если server в блоке upstream был помечен как down. ++ ++ ++"no live upstreams" errors ++if a "server" inside "upstream" block was marked as "down". ++ ++ ++ ++ ++ ++при использовании HTTPS в рабочем процессе мог произойти segmentation fault; ++ошибка появилась в 1.19.5. ++ ++ ++a segmentation fault might occur in a worker process if HTTPS was used; ++the bug had appeared in 1.19.5. ++ ++ ++ ++ ++ ++nginx возвращал ошибку 400 на запросы вида ++"GET http://example.com?args HTTP/1.0". ++ ++ ++nginx returned the 400 response on requests like ++"GET http://example.com?args HTTP/1.0". ++ ++ ++ ++ ++ ++в модулях ngx_http_flv_module и ngx_http_mp4_module.
++Спасибо Chris Newton. ++
++ ++in the ngx_http_flv_module and ngx_http_mp4_module.
++Thanks to Chris Newton. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++ключ -e. ++ ++ ++the -e switch. ++ ++ ++ ++ ++ ++при сборке дополнительных модулей ++теперь можно указывать одни и те же исходные файлы в разных модулях. ++ ++ ++the same source files can now be specified in different modules ++while building addon modules. ++ ++ ++ ++ ++ ++SSL shutdown не работал ++при закрытии соединений с ожиданием дополнительных данных (lingering close). ++ ++ ++SSL shutdown did not work ++when lingering close was used. ++ ++ ++ ++ ++ ++при работе с gRPC-бэкендами ++могли возникать ошибки "upstream sent frame for closed stream". ++ ++ ++"upstream sent frame for closed stream" errors might occur ++when working with gRPC backends. ++ ++ ++ ++ ++ ++во внутреннем API для обработки тела запроса. ++ ++ ++in request body filters internal API. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директивы ssl_conf_command, proxy_ssl_conf_command, grpc_ssl_conf_command ++и uwsgi_ssl_conf_command. ++ ++ ++the "ssl_conf_command", "proxy_ssl_conf_command", "grpc_ssl_conf_command", ++and "uwsgi_ssl_conf_command" directives. ++ ++ ++ ++ ++ ++директива ssl_reject_handshake. ++ ++ ++the "ssl_reject_handshake" directive. ++ ++ ++ ++ ++ ++директива proxy_smtp_auth в почтовом прокси-сервере. ++ ++ ++the "proxy_smtp_auth" directive in mail proxy. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++модуль ngx_stream_set_module. ++ ++ ++the ngx_stream_set_module. ++ ++ ++ ++ ++ ++директива proxy_cookie_flags. ++ ++ ++the "proxy_cookie_flags" directive. ++ ++ ++ ++ ++ ++директива userid_flags. ++ ++ ++the "userid_flags" directive. ++ ++ ++ ++ ++ ++расширение управления кэшированием stale-if-error ++ошибочно применялось, если бэкенд возвращал ответ ++с кодом 500, 502, 503, 504, 403, 404 или 429. ++ ++ ++the "stale-if-error" cache control extension ++was erroneously applied if backend returned a response ++with status code 500, 502, 503, 504, 403, 404, or 429. ++ ++ ++ ++ ++ ++если использовалось кэширование ++и бэкенд возвращал ответы с строкой заголовка Vary, ++в логах могли появляться сообщения "[crit] cache file ... has too long header". ++ ++ ++"[crit] cache file ... has too long header" messages might appear in logs ++if caching was used ++and the backend returned responses with the "Vary" header line. ++ ++ ++ ++ ++ ++при использовании OpenSSL 1.1.1 ++в логах могли появляться сообщения "[crit] SSL_write() failed". ++ ++ ++"[crit] SSL_write() failed" messages might appear in logs ++when using OpenSSL 1.1.1. ++ ++ ++ ++ ++ ++в логах могли появляться сообщения ++"SSL_shutdown() failed (SSL: ... bad write retry)"; ++ошибка появилась в 1.19.2. ++ ++ ++"SSL_shutdown() failed (SSL: ... bad write retry)" ++messages might appear in logs; ++the bug had appeared in 1.19.2. ++ ++ ++ ++ ++ ++при использовании HTTP/2 ++в рабочем процессе мог произойти segmentation fault, ++если ошибки с кодом 400 с помощью директивы error_page ++перенаправлялись в проксируемый location. ++ ++ ++a segmentation fault might occur in a worker process ++when using HTTP/2 ++if errors with code 400 were redirected to a proxied location ++using the "error_page" directive. ++ ++ ++ ++ ++ ++утечки сокетов при использовании HTTP/2 и подзапросов в модуле njs. ++ ++ ++socket leak when using HTTP/2 and subrequests in the njs module. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++теперь nginx начинает закрывать keepalive-соединения, ++не дожидаясь исчерпания всех свободных соединений, ++а также пишет об этом предупреждение в лог ошибок. ++ ++ ++now nginx starts closing keepalive connections ++before all free worker connections are exhausted, ++and logs a warning about this to the error log. ++ ++ ++ ++ ++ ++оптимизация чтения тела запроса ++при использовании chunked transfer encoding. ++ ++ ++optimization of client request body reading ++when using chunked transfer encoding. ++ ++ ++ ++ ++ ++утечки памяти при использовании директивы ssl_ocsp. ++ ++ ++memory leak if the "ssl_ocsp" directive was used. ++ ++ ++ ++ ++ ++в логах могли появляться сообщения "zero size buf in output", ++если FastCGI-сервер возвращал некорректный ответ; ++ошибка появилась в 1.19.1. ++ ++ ++"zero size buf in output" alerts might appear in logs ++if a FastCGI server returned an incorrect response; ++the bug had appeared in 1.19.1. ++ ++ ++ ++ ++ ++в рабочем процессе мог произойти segmentation fault, ++если размеры large_client_header_buffers отличались ++в разных виртуальных серверах. ++ ++ ++a segmentation fault might occur in a worker process ++if different large_client_header_buffers sizes were used ++in different virtual servers. ++ ++ ++ ++ ++ ++SSL shutdown мог не работать. ++ ++ ++SSL shutdown might not work. ++ ++ ++ ++ ++ ++в логах могли появляться сообщения ++"SSL_shutdown() failed (SSL: ... bad write retry)". ++ ++ ++"SSL_shutdown() failed (SSL: ... bad write retry)" ++messages might appear in logs. ++ ++ ++ ++ ++ ++в модуле ngx_http_slice_module. ++ ++ ++in the ngx_http_slice_module. ++ ++ ++ ++ ++ ++в модуле ngx_http_xslt_filter_module. ++ ++ ++in the ngx_http_xslt_filter_module. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директивы lingering_close, lingering_time и lingering_timeout ++теперь работают при использовании HTTP/2. ++ ++ ++the "lingering_close", "lingering_time", and "lingering_timeout" directives ++now work when using HTTP/2. ++ ++ ++ ++ ++ ++теперь лишние данные, присланные бэкендом, всегда отбрасываются. ++ ++ ++now extra data sent by a backend are always discarded. ++ ++ ++ ++ ++ ++теперь при получении слишком короткого ответа от FastCGI-сервера ++nginx пытается отправить клиенту доступную часть ответа, ++после чего закрывает соединение с клиентом. ++ ++ ++now after receiving a too short response from a FastCGI server ++nginx tries to send the available part of the response to the client, ++and then closes the client connection. ++ ++ ++ ++ ++ ++теперь при получении ответа некорректной длины от gRPC-бэкенда ++nginx прекращает обработку ответа с ошибкой. ++ ++ ++now after receiving a response with incorrect length from a gRPC backend ++nginx stops response processing with an error. ++ ++ ++ ++ ++ ++параметр min_free в директивах proxy_cache_path, fastcgi_cache_path, ++scgi_cache_path и uwsgi_cache_path.
++Спасибо Adam Bambuch. ++
++ ++the "min_free" parameter of the "proxy_cache_path", "fastcgi_cache_path", ++"scgi_cache_path", and "uwsgi_cache_path" directives.
++Thanks to Adam Bambuch. ++
++
++ ++ ++ ++nginx не удалял unix domain listen-сокеты ++при плавном завершении по сигналу SIGQUIT. ++ ++ ++nginx did not delete unix domain listen sockets ++during graceful shutdown on the SIGQUIT signal. ++ ++ ++ ++ ++ ++UDP-пакеты нулевого размера не проксировались. ++ ++ ++zero length UDP datagrams were not proxied. ++ ++ ++ ++ ++ ++проксирование на uwsgi-бэкенды с использованием SSL могло не работать.
++Спасибо Guanzhong Chen. ++
++ ++proxying to uwsgi backends using SSL might not work.
++Thanks to Guanzhong Chen. ++
++
++ ++ ++ ++в обработке ошибок при использовании директивы ssl_ocsp. ++ ++ ++in error handling when using the "ssl_ocsp" directive. ++ ++ ++ ++ ++ ++при использовании файловых систем XFS и NFS ++размер кэша на диске мог считаться некорректно. ++ ++ ++on XFS and NFS file systems ++disk cache size might be calculated incorrectly. ++ ++ ++ ++ ++ ++если сервер memcached возвращал некорректный ответ, ++в логах могли появляться сообщения "negative size buf in writer". ++ ++ ++"negative size buf in writer" alerts might appear in logs ++if a memcached server returned a malformed response. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++проверка клиентских сертификатов с помощью OCSP. ++ ++ ++client certificate validation with OCSP. ++ ++ ++ ++ ++ ++при работе с gRPC-бэкендами ++могли возникать ошибки "upstream sent frame for closed stream". ++ ++ ++"upstream sent frame for closed stream" errors might occur ++when working with gRPC backends. ++ ++ ++ ++ ++ ++OCSP stapling мог не работать, ++если не была указана директива resolver. ++ ++ ++OCSP stapling might not work ++if the "resolver" directive was not specified. ++ ++ ++ ++ ++ ++соединения с некорректным HTTP/2 preface не логгировались. ++ ++ ++connections with incorrect HTTP/2 preface were not logged. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директива auth_delay. ++ ++ ++the "auth_delay" directive. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++теперь nginx не разрешает ++несколько строк "Host" в заголовке запроса. ++ ++ ++now nginx does not allow ++several "Host" request header lines. ++ ++ ++ ++ ++ ++nginx игнорировал дополнительные ++строки "Transfer-Encoding" в заголовке запроса. ++ ++ ++nginx ignored additional ++"Transfer-Encoding" request header lines. ++ ++ ++ ++ ++ ++утечки сокетов при использовании HTTP/2. ++ ++ ++socket leak when using HTTP/2. ++ ++ ++ ++ ++ ++в рабочем процессе мог произойти segmentation fault, ++если использовался OCSP stapling. ++ ++ ++a segmentation fault might occur in a worker process ++if OCSP stapling was used. ++ ++ ++ ++ ++ ++в модуле ngx_http_mp4_module. ++ ++ ++in the ngx_http_mp4_module. ++ ++ ++ ++ ++ ++при перенаправлении ошибок с кодом 494 с помощью директивы error_page ++nginx возвращал ответ с кодом 494 вместо 400. ++ ++ ++nginx used status code 494 instead of 400 ++if errors with code 494 were redirected with the "error_page" directive. ++ ++ ++ ++ ++ ++утечки сокетов при использовании подзапросов в модуле njs и директивы aio. ++ ++ ++socket leak when using subrequests in the njs module and the "aio" directive. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директива grpc_pass поддерживает переменные. ++ ++ ++variables support in the "grpc_pass" directive. ++ ++ ++ ++ ++ ++при обработке pipelined-запросов по SSL-соединению мог произойти таймаут; ++ошибка появилась в 1.17.5. ++ ++ ++a timeout might occur while handling pipelined requests in an SSL connection; ++the bug had appeared in 1.17.5. ++ ++ ++ ++ ++ ++в директиве debug_points при использовании HTTP/2.
++Спасибо Даниилу Бондареву. ++
++ ++in the "debug_points" directive when using HTTP/2.
++Thanks to Daniil Bondarev. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++на старте или во время переконфигурации мог произойти segmentation fault, ++если в конфигурации использовалась ++директива rewrite с пустой строкой замены. ++ ++ ++a segmentation fault might occur on start or during reconfiguration ++if the "rewrite" directive with an empty replacement string ++was used in the configuration. ++ ++ ++ ++ ++ ++в рабочем процессе мог произойти segmentation fault, ++если директива break использовалась совместно с директивой alias ++или директивой proxy_pass с URI. ++ ++ ++a segmentation fault might occur in a worker process ++if the "break" directive was used with the "alias" directive ++or with the "proxy_pass" directive with a URI. ++ ++ ++ ++ ++ ++строка Location заголовка ответа могла содержать мусор, ++если URI запроса был изменён на URI, содержащий нулевой символ. ++ ++ ++the "Location" response header line might contain garbage ++if the request URI was rewritten to the one containing a null character. ++ ++ ++ ++ ++ ++при возврате перенаправлений с помощью директивы error_page ++запросы с телом обрабатывались некорректно; ++ошибка появилась в 0.7.12. ++ ++ ++requests with bodies were handled incorrectly ++when returning redirections with the "error_page" directive; ++the bug had appeared in 0.7.12. ++ ++ ++ ++ ++ ++утечки сокетов при использовании HTTP/2. ++ ++ ++socket leak when using HTTP/2. ++ ++ ++ ++ ++ ++при обработке pipelined-запросов по SSL-соединению мог произойти таймаут; ++ошибка появилась в 1.17.5. ++ ++ ++a timeout might occur while handling pipelined requests in an SSL connection; ++the bug had appeared in 1.17.5. ++ ++ ++ ++ ++ ++в модуле ngx_http_dav_module. ++ ++ ++in the ngx_http_dav_module. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++переменные $proxy_protocol_server_addr и $proxy_protocol_server_port. ++ ++ ++the $proxy_protocol_server_addr and $proxy_protocol_server_port variables. ++ ++ ++ ++ ++ ++директива limit_conn_dry_run. ++ ++ ++the "limit_conn_dry_run" directive. ++ ++ ++ ++ ++ ++переменные $limit_req_status и $limit_conn_status. ++ ++ ++the $limit_req_status and $limit_conn_status variables. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++теперь nginx использует вызов ioctl(FIONREAD), если он доступен, ++чтобы избежать чтения из быстрого соединения в течение долгого времени. ++ ++ ++now nginx uses ioctl(FIONREAD), if available, ++to avoid reading from a fast connection for a long time. ++ ++ ++ ++ ++ ++неполные закодированные символы в конце URI запроса игнорировались. ++ ++ ++incomplete escaped characters at the end of the request URI were ignored. ++ ++ ++ ++ ++ ++"/." и "/.." в конце URI запроса не нормализовывались. ++ ++ ++"/." and "/.." at the end of the request URI were not normalized. ++ ++ ++ ++ ++ ++в директиве merge_slashes. ++ ++ ++in the "merge_slashes" directive. ++ ++ ++ ++ ++ ++в директиве ignore_invalid_headers.
++Спасибо Alan Kemp. ++
++ ++in the "ignore_invalid_headers" directive.
++Thanks to Alan Kemp. ++
++
++ ++ ++ ++nginx не собирался с MinGW-w64 gcc 8.1 и новее. ++ ++ ++nginx could not be built with MinGW-w64 gcc 8.1 or newer. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++улучшено детектирование некорректного поведения клиентов в HTTP/2. ++ ++ ++better detection of incorrect client behavior in HTTP/2. ++ ++ ++ ++ ++ ++в обработке непрочитанного тела запроса ++при возврате ошибок в HTTP/2. ++ ++ ++in handling of not fully read client request body ++when returning errors in HTTP/2. ++ ++ ++ ++ ++ ++директива worker_shutdown_timeout могла не работать ++при использовании HTTP/2. ++ ++ ++the "worker_shutdown_timeout" directive might not work ++when using HTTP/2. ++ ++ ++ ++ ++ ++при использовании HTTP/2 и директивы proxy_request_buffering ++в рабочем процессе мог произойти segmentation fault. ++ ++ ++a segmentation fault might occur in a worker process ++when using HTTP/2 and the "proxy_request_buffering" directive. ++ ++ ++ ++ ++ ++на Windows при использовании SSL ++уровень записи в лог ошибки ECONNABORTED был "crit" вместо "error". ++ ++ ++the ECONNABORTED error log level was "crit" instead of "error" ++on Windows when using SSL. ++ ++ ++ ++ ++ ++nginx игнорировал лишние данные при использовании chunked transfer encoding. ++ ++ ++nginx ignored extra data when using chunked transfer encoding. ++ ++ ++ ++ ++ ++если использовалась директива return и ++при чтении тела запроса возникала ошибка, ++nginx всегда возвращал ошибку 500. ++ ++ ++nginx always returned the 500 error ++if the "return" directive was used ++and an error occurred during reading client request body. ++ ++ ++ ++ ++ ++в обработке ошибок выделения памяти. ++ ++ ++in memory allocation error handling. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++при использовании HTTP/2 клиент мог вызвать ++чрезмерное потребление памяти и ресурсов процессора ++(CVE-2019-9511, CVE-2019-9513, CVE-2019-9516). ++ ++ ++when using HTTP/2 a client might cause ++excessive memory consumption and CPU usage ++(CVE-2019-9511, CVE-2019-9513, CVE-2019-9516). ++ ++ ++ ++ ++ ++при использовании сжатия в логах могли появляться сообщения "zero size buf"; ++ошибка появилась в 1.17.2. ++ ++ ++"zero size buf" alerts might appear in logs when using gzipping; ++the bug had appeared in 1.17.2. ++ ++ ++ ++ ++ ++при использовании директивы resolver в SMTP прокси-сервере ++в рабочем процессе мог произойти segmentation fault. ++ ++ ++a segmentation fault might occur in a worker process ++if the "resolver" directive was used in SMTP proxy. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++минимальная поддерживаемая версия zlib—1.2.0.4.
++Спасибо Илье Леошкевичу. ++
++ ++minimum supported zlib version is 1.2.0.4.
++Thanks to Ilya Leoshkevich. ++
++
++ ++ ++ ++метод $r->internal_redirect() встроенного перла ++теперь ожидает закодированный URI. ++ ++ ++the $r->internal_redirect() embedded perl method ++now expects escaped URIs. ++ ++ ++ ++ ++ ++теперь с помощью метода $r->internal_redirect() встроенного перла ++можно перейти в именованный location. ++ ++ ++it is now possible to switch to a named location ++using the $r->internal_redirect() embedded perl method. ++ ++ ++ ++ ++ ++в обработке ошибок во встроенном перле. ++ ++ ++in error handling in embedded perl. ++ ++ ++ ++ ++ ++на старте или во время переконфигурации мог произойти segmentation fault, ++если в конфигурации использовалось значение hash bucket size больше 64 килобайт. ++ ++ ++a segmentation fault might occur on start or during reconfiguration ++if hash bucket size larger than 64 kilobytes was used in the configuration. ++ ++ ++ ++ ++ ++при использовании методов обработки соединений select, poll и /dev/poll ++nginx мог нагружать процессор во время небуферизованного проксирования ++и при проксировании WebSocket-соединений. ++ ++ ++nginx might hog CPU during unbuffered proxying ++and when proxying WebSocket connections ++if the select, poll, or /dev/poll methods were used. ++ ++ ++ ++ ++ ++в модуле ngx_http_xslt_filter_module. ++ ++ ++in the ngx_http_xslt_filter_module. ++ ++ ++ ++ ++ ++в модуле ngx_http_ssi_filter_module. ++ ++ ++in the ngx_http_ssi_filter_module. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++директива limit_req_dry_run. ++ ++ ++the "limit_req_dry_run" directive. ++ ++ ++ ++ ++ ++при использовании директивы hash в блоке upstream ++пустой ключ хэширования теперь приводит к переключению ++на round-robin балансировку.
++Спасибо Niklas Keller. ++
++ ++when using the "hash" directive inside the "upstream" block ++an empty hash key now triggers round-robin balancing.
++Thanks to Niklas Keller. ++
++
++ ++ ++ ++в рабочем процессе мог произойти segmentation fault, ++если использовалось кэширование и директива image_filter, ++а ошибки с кодом 415 перенаправлялись с помощью директивы error_page; ++ошибка появилась в 1.11.10. ++ ++ ++a segmentation fault might occur in a worker process ++if caching was used along with the "image_filter" directive, ++and errors with code 415 were redirected with the "error_page" directive; ++the bug had appeared in 1.11.10. ++ ++ ++ ++ ++ ++в рабочем процессе мог произойти segmentation fault, ++если использовался встроенный перл; ++ошибка появилась в 1.7.3. ++ ++ ++a segmentation fault might occur in a worker process ++if embedded perl was used; ++the bug had appeared in 1.7.3. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++директивы limit_rate и limit_rate_after поддерживают переменные. ++ ++ ++variables support in the "limit_rate" and "limit_rate_after" directives. ++ ++ ++ ++ ++ ++директивы proxy_upload_rate и proxy_download_rate в модуле stream ++поддерживают переменные. ++ ++ ++variables support ++in the "proxy_upload_rate" and "proxy_download_rate" directives ++in the stream module. ++ ++ ++ ++ ++ ++минимальная поддерживаемая версия OpenSSL—0.9.8. ++ ++ ++minimum supported OpenSSL version is 0.9.8. ++ ++ ++ ++ ++ ++теперь postpone-фильтр собирается всегда. ++ ++ ++now the postpone filter is always built. ++ ++ ++ ++ ++ ++директива include не работала в блоках if и limit_except. ++ ++ ++the "include" directive did not work inside the "if" and "limit_except" blocks. ++ ++ ++ ++ ++ ++в обработке byte ranges. ++ ++ ++in byte ranges processing. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++в рабочем процессе мог произойти segmentation fault, ++если в директивах ssl_certificate или ssl_certificate_key ++использовались переменные ++и был включён OCSP stapling. ++ ++ ++a segmentation fault might occur in a worker process ++if variables were used ++in the "ssl_certificate" or "ssl_certificate_key" directives ++and OCSP stapling was enabled. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++в директиве ssl_stapling_file на Windows. ++ ++ ++in the "ssl_stapling_file" directive on Windows. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++теперь при использовании имени хоста в директиве listen ++nginx создаёт listen-сокеты для всех адресов, ++соответствующих этому имени ++(ранее использовался только первый адрес). ++ ++ ++when using a hostname in the "listen" directive ++nginx now creates listening sockets ++for all addresses the hostname resolves to ++(previously, only the first address was used). ++ ++ ++ ++ ++ ++диапазоны портов в директиве listen. ++ ++ ++port ranges in the "listen" directive. ++ ++ ++ ++ ++ ++возможность загрузки SSL-сертификатов и секретных ключей из переменных. ++ ++ ++loading of SSL certificates and secret keys from variables. ++ ++ ++ ++ ++ ++переменная $ssl_server_name могла быть пустой ++при использовании OpenSSL 1.1.1. ++ ++ ++the $ssl_server_name variable might be empty ++when using OpenSSL 1.1.1. ++ ++ ++ ++ ++ ++nginx/Windows не собирался с Visual Studio 2015 и новее; ++ошибка появилась в 1.15.9. ++ ++ ++nginx/Windows could not be built with Visual Studio 2015 or newer; ++the bug had appeared in 1.15.9. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директивы ssl_certificate и ssl_certificate_key ++поддерживают переменные. ++ ++ ++variables support ++in the "ssl_certificate" and "ssl_certificate_key" directives. ++ ++ ++ ++ ++ ++метод poll теперь доступен на Windows ++при использовании Windows Vista и новее. ++ ++ ++the "poll" method is now available on Windows ++when using Windows Vista or newer. ++ ++ ++ ++ ++ ++если при использовании метода select на Windows ++происходила ошибка при установлении соединения с бэкендом, ++nginx ожидал истечения таймаута на установление соединения. ++ ++ ++if the "select" method was used on Windows ++and an error occurred while establishing a backend connection, ++nginx waited for the connection establishment timeout to expire. ++ ++ ++ ++ ++ ++директивы proxy_upload_rate и proxy_download_rate ++в модуле stream ++работали некорректно при проксировании UDP-пакетов. ++ ++ ++the "proxy_upload_rate" and "proxy_download_rate" directives ++in the stream module ++worked incorrectly when proxying UDP datagrams. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++переменная $upstream_bytes_sent.
++Спасибо Piotr Sikora. ++
++ ++the $upstream_bytes_sent variable.
++Thanks to Piotr Sikora. ++
++
++ ++ ++ ++новые директивы в скриптах подсветки синтаксиса для vim.
++Спасибо Геннадию Махомеду. ++
++ ++new directives in vim syntax highlighting scripts.
++Thanks to Gena Makhomed. ++
++
++ ++ ++ ++в директиве proxy_cache_background_update. ++ ++ ++in the "proxy_cache_background_update" directive. ++ ++ ++ ++ ++ ++в директиве geo при использовании unix domain listen-сокетов. ++ ++ ++in the "geo" directive when using unix domain listen sockets. ++ ++ ++ ++ ++ ++при использовании директивы ssl_early_data с OpenSSL ++в логах могли появляться сообщения ++"ignoring stale global SSL error ... bad length". ++ ++ ++the "ignoring stale global SSL error ... bad length" ++alerts might appear in logs ++when using the "ssl_early_data" directive with OpenSSL. ++ ++ ++ ++ ++ ++в nginx/Windows. ++ ++ ++in nginx/Windows. ++ ++ ++ ++ ++ ++в модуле ngx_http_autoindex_module на 32-битных платформах. ++ ++ ++in the ngx_http_autoindex_module on 32-bit platforms. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++директива proxy_requests в модуле stream. ++ ++ ++the "proxy_requests" directive in the stream module. ++ ++ ++ ++ ++ ++параметр "delay" директивы "limit_req".
++Спасибо Владиславу Шабанову и Петру Щучкину. ++
++ ++the "delay" parameter of the "limit_req" directive.
++Thanks to Vladislav Shabanov and Peter Shchuchkin. ++
++
++ ++ ++ ++утечки памяти в случае ошибок при переконфигурации. ++ ++ ++memory leak on errors during reconfiguration. ++ ++ ++ ++ ++ ++в переменных $upstream_response_time, $upstream_connect_time и ++$upstream_header_time. ++ ++ ++in the $upstream_response_time, $upstream_connect_time, and ++$upstream_header_time variables. ++ ++ ++ ++ ++ ++в рабочем процессе мог произойти segmentation fault, ++если использовался модуль ngx_http_mp4_module на 32-битных платформах. ++ ++ ++a segmentation fault might occur in a worker process ++if the ngx_http_mp4_module was used on 32-bit platforms. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++при использовании HTTP/2 клиент мог вызвать ++чрезмерное потреблению памяти (CVE-2018-16843) ++и ресурсов процессора (CVE-2018-16844). ++ ++ ++when using HTTP/2 a client might cause ++excessive memory consumption (CVE-2018-16843) ++and CPU usage (CVE-2018-16844). ++ ++ ++ ++ ++ ++при обработке специально созданного mp4-файла модулем ngx_http_mp4_module ++содержимое памяти рабочего процесса могло быть отправлено клиенту ++(CVE-2018-16845). ++ ++ ++processing of a specially crafted mp4 file with the ngx_http_mp4_module ++might result in worker process memory disclosure ++(CVE-2018-16845). ++ ++ ++ ++ ++ ++директивы proxy_socket_keepalive, fastcgi_socket_keepalive, ++grpc_socket_keepalive, memcached_socket_keepalive, ++scgi_socket_keepalive и uwsgi_socket_keepalive. ++ ++ ++the "proxy_socket_keepalive", "fastcgi_socket_keepalive", ++"grpc_socket_keepalive", "memcached_socket_keepalive", ++"scgi_socket_keepalive", and "uwsgi_socket_keepalive" directives. ++ ++ ++ ++ ++ ++если nginx был собран с OpenSSL 1.1.0, а использовался с OpenSSL 1.1.1, ++протокол TLS 1.3 всегда был разрешён. ++ ++ ++if nginx was built with OpenSSL 1.1.0 and used with OpenSSL 1.1.1, ++the TLS 1.3 protocol was always enabled. ++ ++ ++ ++ ++ ++при работе с gRPC-бэкендами могло расходоваться большое количество памяти. ++ ++ ++working with gRPC backends might result in excessive memory consumption. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++при использовании OpenSSL 1.1.0h и новее ++в рабочем процессе мог произойти segmentation fault; ++ошибка появилась в 1.15.4. ++ ++ ++a segmentation fault might occur in a worker process ++when using OpenSSL 1.1.0h or newer; ++the bug had appeared in 1.15.4. ++ ++ ++ ++ ++ ++незначительных потенциальных ошибок. ++ ++ ++of minor potential bugs. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++теперь директиву ssl_early_data можно использовать с OpenSSL. ++ ++ ++now the "ssl_early_data" directive can be used with OpenSSL. ++ ++ ++ ++ ++ ++в модуле ngx_http_uwsgi_module.
++Спасибо Chris Caputo. ++
++ ++in the ngx_http_uwsgi_module.
++Thanks to Chris Caputo. ++
++
++ ++ ++ ++соединения к некоторым gRPC-бэкендам могли не кэшироваться ++при использовании директивы keepalive. ++ ++ ++connections with some gRPC backends might not be cached ++when using the "keepalive" directive. ++ ++ ++ ++ ++ ++при использовании директивы error_page для перенаправления ошибок, ++возникающих на ранних этапах обработки запроса, ++в частности ошибок с кодом 400, ++могла происходить утечка сокетов. ++ ++ ++a socket leak might occur ++when using the "error_page" directive ++to redirect early request processing errors, ++notably errors with code 400. ++ ++ ++ ++ ++ ++директива return при возврате ошибок не изменяла код ответа, ++если запрос был перенаправлен с помощью директивы error_page. ++ ++ ++the "return" directive did not change the response code when returning errors ++if the request was redirected by the "error_page" directive. ++ ++ ++ ++ ++ ++стандартные сообщения об ошибках и ответы модуля ngx_http_autoindex_module ++содержали атрибут bgcolor, что могло приводить к их некорректному отображению ++при использовании пользовательских настроек цветов в браузерах.
++Спасибо Nova DasSarma. ++
++ ++standard error pages and responses of the ngx_http_autoindex_module module ++used the "bgcolor" attribute, and might be displayed incorrectly when using ++custom color settings in browsers.
++Thanks to Nova DasSarma. ++
++
++ ++ ++ ++уровень логгирования ошибок SSL "no suitable key share" и ++"no suitable signature algorithm" ++понижен с уровня crit до info. ++ ++ ++the logging level of the "no suitable key share" and ++"no suitable signature algorithm" SSL errors ++has been lowered from "crit" to "info". ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++теперь TLSv1.3 можно использовать с BoringSSL. ++ ++ ++now TLSv1.3 can be used with BoringSSL. ++ ++ ++ ++ ++ ++директива ssl_early_data, ++сейчас доступна при использовании BoringSSL. ++ ++ ++the "ssl_early_data" directive, ++currently available with BoringSSL. ++ ++ ++ ++ ++ ++директивы keepalive_timeout и keepalive_requests ++в блоке upstream. ++ ++ ++the "keepalive_timeout" and "keepalive_requests" directives ++in the "upstream" block. ++ ++ ++ ++ ++ ++модуль ngx_http_dav_module ++при копировании файла поверх существующего файла с помощью метода COPY ++не обнулял целевой файл. ++ ++ ++the ngx_http_dav_module ++did not truncate destination file when copying a file over an existing one ++with the COPY method. ++ ++ ++ ++ ++ ++модуль ngx_http_dav_module ++при перемещении файла между файловыми системами с помощью метода MOVE ++устанавливал нулевые права доступа на результирующий файл ++и не сохранял время изменения файла. ++ ++ ++the ngx_http_dav_module ++used zero access rights on the destination file ++and did not preserve file modification time ++when moving a file between different file systems with the MOVE method. ++ ++ ++ ++ ++ ++модуль ngx_http_dav_module ++при копировании файла с помощью метода COPY ++для результирующего файла использовал права доступа по умолчанию. ++ ++ ++the ngx_http_dav_module ++used default access rights ++when copying a file with the COPY method. ++ ++ ++ ++ ++ ++некоторые клиенты могли не работать при использовании HTTP/2; ++ошибка появилась в 1.13.5. ++ ++ ++some clients might not work when using HTTP/2; ++the bug had appeared in 1.13.5. ++ ++ ++ ++ ++ ++nginx не собирался с LibreSSL 2.8.0. ++ ++ ++nginx could not be built with LibreSSL 2.8.0. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++переменная $ssl_preread_protocol ++в модуле ngx_stream_ssl_preread_module. ++ ++ ++the $ssl_preread_protocol variable ++in the ngx_stream_ssl_preread_module. ++ ++ ++ ++ ++ ++теперь при использовании директивы reset_timedout_connection ++nginx сбрасывает соединения, закрываемые с кодом 444. ++ ++ ++now when using the "reset_timedout_connection" directive ++nginx will reset connections being closed with the 444 code. ++ ++ ++ ++ ++ ++уровень логгирования ошибок SSL "http request", "https proxy request", ++"unsupported protocol" и "version too low" ++понижен с уровня crit до info. ++ ++ ++a logging level of the "http request", "https proxy request", ++"unsupported protocol", and "version too low" SSL errors ++has been lowered from "crit" to "info". ++ ++ ++ ++ ++ ++запросы к DNS-серверу не отправлялись повторно, ++если при первой попытке отправки происходила ошибка. ++ ++ ++DNS requests were not resent ++if initial sending of a request failed. ++ ++ ++ ++ ++ ++параметр reuseport директивы listen игнорировался, ++если количество рабочих процессов было задано после директивы listen. ++ ++ ++the "reuseport" parameter of the "listen" directive was ignored ++if the number of worker processes was specified after the "listen" directive. ++ ++ ++ ++ ++ ++при использовании OpenSSL 1.1.0 и новее ++директиву ssl_prefer_server_ciphers нельзя было выключить ++в виртуальном сервере, если она была включена в сервере по умолчанию. ++ ++ ++when using OpenSSL 1.1.0 or newer ++it was not possible to switch off "ssl_prefer_server_ciphers" in ++a virtual server if it was switched on in the default server. ++ ++ ++ ++ ++ ++повторное использование SSL-сессий к бэкендам ++не работало с протоколом TLS 1.3. ++ ++ ++SSL session reuse with upstream servers ++did not work with the TLS 1.3 protocol. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директива random в блоке upstream. ++ ++ ++the "random" directive inside the "upstream" block. ++ ++ ++ ++ ++ ++улучшена производительность при использовании директив hash и ip_hash ++совместно с директивой zone. ++ ++ ++improved performance when using the "hash" and "ip_hash" directives ++with the "zone" directive. ++ ++ ++ ++ ++ ++параметр reuseport директивы listen ++теперь использует SO_REUSEPORT_LB на FreeBSD 12. ++ ++ ++the "reuseport" parameter of the "listen" directive ++now uses SO_REUSEPORT_LB on FreeBSD 12. ++ ++ ++ ++ ++ ++HTTP/2 server push не работал, если SSL терминировался прокси-сервером ++перед nginx'ом. ++ ++ ++HTTP/2 server push did not work if SSL was terminated by a proxy server ++in front of nginx. ++ ++ ++ ++ ++ ++директива tcp_nopush всегда использовалась для соединений к бэкендам. ++ ++ ++the "tcp_nopush" directive was always used on backend connections. ++ ++ ++ ++ ++ ++при отправке сохранённого на диск тела запроса на gRPC-бэкенд ++могли возникать ошибки. ++ ++ ++sending a disk-buffered request body to a gRPC backend ++might fail. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директива "ssl" теперь считается устаревшей; ++вместо неё следует использовать параметр ssl директивы listen. ++ ++ ++the "ssl" directive is deprecated; ++the "ssl" parameter of the "listen" directive should be used instead. ++ ++ ++ ++ ++ ++теперь при использовании директивы listen с параметром ssl ++nginx определяет отсутствие SSL-сертификатов при тестировании конфигурации. ++ ++ ++now nginx detects missing SSL certificates during configuration testing ++when using the "ssl" parameter of the "listen" directive. ++ ++ ++ ++ ++ ++теперь модуль stream умеет обрабатывать ++несколько входящих UDP-пакетов от клиента в рамках одной сессии. ++ ++ ++now the stream module can handle ++multiple incoming UDP datagrams from a client within a single session. ++ ++ ++ ++ ++ ++в директиве proxy_cache_valid ++можно было указать некорректный код ответа. ++ ++ ++it was possible to specify an incorrect response code ++in the "proxy_cache_valid" directive. ++ ++ ++ ++ ++ ++nginx не собирался gcc 8.1. ++ ++ ++nginx could not be built by gcc 8.1. ++ ++ ++ ++ ++ ++логгирование в syslog останавливалось при изменении локального IP-адреса. ++ ++ ++logging to syslog stopped on local IP address changes. ++ ++ ++ ++ ++ ++nginx не собирался компилятором clang, если был установлен CUDA SDK; ++ошибка появилась в 1.13.8. ++ ++ ++nginx could not be built by clang with CUDA SDK installed; ++the bug had appeared in 1.13.8. ++ ++ ++ ++ ++ ++при использовании unix domain listen-сокетов на FreeBSD ++в процессе обновления исполняемого файла ++в логе могли появляться сообщения "getsockopt(TCP_FASTOPEN) ... failed". ++ ++ ++"getsockopt(TCP_FASTOPEN) ... failed" messages might appear in logs ++during binary upgrade ++when using unix domain listen sockets on FreeBSD. ++ ++ ++ ++ ++ ++nginx не собирался на Fedora 28 Linux. ++ ++ ++nginx could not be built on Fedora 28 Linux. ++ ++ ++ ++ ++ ++при использовании директивы limit_req ++заданная скорость обработки запросов могла не соблюдаться. ++ ++ ++request processing rate might exceed configured rate ++when using the "limit_req" directive. ++ ++ ++ ++ ++ ++в обработке адресов клиентов при использовании unix domain listen-сокетов ++для работы с датаграммами на Linux. ++ ++ ++in handling of client addresses when using unix domain listen sockets ++to work with datagrams on Linux. ++ ++ ++ ++ ++ ++в обработке ошибок выделения памяти. ++ ++ ++in memory allocation error handling. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++при возврате большого ответа ++соединения с gRPC-бэкендами могли неожиданно закрываться. ++ ++ ++connections with gRPC backends might be closed unexpectedly ++when returning a large response. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++параметр proxy_protocol директивы listen ++теперь поддерживает протокол PROXY версии 2. ++ ++ ++the "proxy_protocol" parameter of the "listen" directive ++now supports the PROXY protocol version 2. ++ ++ ++ ++ ++ ++nginx не собирался с OpenSSL 1.1.1 статически на Linux. ++ ++ ++nginx could not be built with OpenSSL 1.1.1 statically on Linux. ++ ++ ++ ++ ++ ++в параметрах http_404, http_500 и им подобных ++директивы proxy_next_upstream. ++ ++ ++in the "http_404", "http_500", etc. parameters ++of the "proxy_next_upstream" directive. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++теперь параметр set в SSI-директиве include ++позволяет сохранять в переменную любые ответы; ++максимальный размер ответа задаётся директивой subrequest_output_buffer_size. ++ ++ ++the "set" parameter of the "include" SSI directive now allows ++writing arbitrary responses to a variable; ++the "subrequest_output_buffer_size" directive defines maximum response size. ++ ++ ++ ++ ++ ++теперь nginx использует вызов clock_gettime(CLOCK_MONOTONIC), если он доступен, ++что позволяет избежать некорректного срабатывания таймаутов ++при изменениях системного времени. ++ ++ ++now nginx uses clock_gettime(CLOCK_MONOTONIC) if available, ++to avoid timeouts being incorrectly triggered ++on system time changes. ++ ++ ++ ++ ++ ++параметр "escape=none" директивы log_format.
++Спасибо Johannes Baiter и Calin Don. ++
++ ++the "escape=none" parameter of the "log_format" directive.
++Thanks to Johannes Baiter and Calin Don. ++
++
++ ++ ++ ++переменная $ssl_preread_alpn_protocols ++в модуле ngx_stream_ssl_preread_module. ++ ++ ++the $ssl_preread_alpn_protocols variable ++in the ngx_stream_ssl_preread_module. ++ ++ ++ ++ ++ ++модуль ngx_http_grpc_module. ++ ++ ++the ngx_http_grpc_module. ++ ++ ++ ++ ++ ++в обработке ошибок выделения памяти в директиве geo. ++ ++ ++in memory allocation error handling in the "geo" directive. ++ ++ ++ ++ ++ ++при использовании переменных в директиве auth_basic_user_file ++в лог мог выводиться символ '\0'.
++Спасибо Вадиму Филимонову. ++
++ ++when using variables in the "auth_basic_user_file" directive ++a null character might appear in logs.
++Thanks to Vadim Filimonov. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++поддержка HTTP/2 server push; ++директивы http2_push и http2_push_preload. ++ ++ ++HTTP/2 server push support; ++the "http2_push" and "http2_push_preload" directives. ++ ++ ++ ++ ++ ++при использовании кэша ++в логах могли появляться сообщения "header already sent"; ++ошибка появилась в 1.9.13. ++ ++ ++"header already sent" alerts might appear in logs ++when using cache; ++the bug had appeared in 1.9.13. ++ ++ ++ ++ ++ ++при использовании директивы ssl_verify_client ++в рабочем процессе мог произойти segmentation fault, ++если в виртуальном сервере не был указан SSL-сертификат. ++ ++ ++a segmentation fault might occur in a worker process ++if the "ssl_verify_client" directive was used ++and no SSL certificate was specified in a virtual server. ++ ++ ++ ++ ++ ++в модуле ngx_http_v2_module. ++ ++ ++in the ngx_http_v2_module. ++ ++ ++ ++ ++ ++в модуле ngx_http_dav_module. ++ ++ ++in the ngx_http_dav_module. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++теперь при использовании параметра transparent директив proxy_bind, ++fastcgi_bind, memcached_bind, scgi_bind и uwsgi_bind ++nginx автоматически сохраняет capability CAP_NET_RAW в рабочих процессах. ++ ++ ++now nginx automatically preserves the CAP_NET_RAW capability in worker processes ++when using the "transparent" parameter of the "proxy_bind", ++"fastcgi_bind", "memcached_bind", "scgi_bind", and "uwsgi_bind" directives. ++ ++ ++ ++ ++ ++улучшения в определении размера строки кэша процессора.
++Спасибо Debayan Ghosh. ++
++ ++improved CPU cache line size detection.
++Thanks to Debayan Ghosh. ++
++
++ ++ ++ ++новые директивы в скриптах подсветки синтаксиса для vim.
++Спасибо Геннадию Махомеду. ++
++ ++new directives in vim syntax highlighting scripts.
++Thanks to Gena Makhomed. ++
++
++ ++ ++ ++процедура обновления исполняемого файла не работала, ++если после завершения родительского процесса ++новым родительским процессом nginx'а становился процесс с PID, отличным от 1. ++ ++ ++binary upgrade refused to work ++if nginx was re-parented to a process with PID different from 1 ++after its parent process has finished. ++ ++ ++ ++ ++ ++модуль ngx_http_autoindex_module неправильно обрабатывал запросы с телом. ++ ++ ++the ngx_http_autoindex_module incorrectly handled requests with bodies. ++ ++ ++ ++ ++ ++в директиве proxy_limit_rate при использовании с директивой keepalive. ++ ++ ++in the "proxy_limit_rate" directive when used with the "keepalive" directive. ++ ++ ++ ++ ++ ++при использовании "proxy_buffering off" часть ответа могла буферизироваться, ++если клиентское соединение использовало SSL.
++Спасибо Patryk Lesiewicz. ++
++ ++some parts of a response might be buffered when using "proxy_buffering off" ++if the client connection used SSL.
++Thanks to Patryk Lesiewicz. ++
++
++ ++ ++ ++в директиве proxy_cache_background_update. ++ ++ ++in the "proxy_cache_background_update" directive. ++ ++ ++ ++ ++ ++переменную вида "${name}" с именем в фигурных скобках ++нельзя было использовать в начале параметра ++не заключив весь параметр в кавычки. ++ ++ ++it was not possible to start a parameter ++with a variable in the "${name}" form with the name in curly brackets ++without enclosing the parameter into single or double quotes. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++в переменной $upstream_status. ++ ++ ++in the $upstream_status variable. ++ ++ ++ ++ ++ ++в рабочем процессе мог произойти segmentation fault, ++если бэкенд возвращал ответ "101 Switching Protocols" на подзапрос. ++ ++ ++a segmentation fault might occur in a worker process ++if a backend returned a "101 Switching Protocols" response to a subrequest. ++ ++ ++ ++ ++ ++если при переконфигурации изменялся размер зоны разделяемой памяти ++и переконфигурация завершалась неудачно, ++то в главном процессе происходил segmentation fault. ++ ++ ++a segmentation fault occurred in a master process ++if a shared memory zone size was changed during a reconfiguration ++and the reconfiguration failed. ++ ++ ++ ++ ++ ++в модуле ngx_http_fastcgi_module. ++ ++ ++in the ngx_http_fastcgi_module. ++ ++ ++ ++ ++ ++nginx возвращал ошибку 500, ++если в директиве xslt_stylesheet ++были заданы параметры без использования переменных. ++ ++ ++nginx returned the 500 error ++if parameters without variables were specified ++in the "xslt_stylesheet" directive. ++ ++ ++ ++ ++ ++при использовании варианта библиотеки zlib от Intel ++в лог писались сообщения "gzip filter failed to use preallocated memory". ++ ++ ++"gzip filter failed to use preallocated memory" alerts appeared in logs ++when using a zlib library variant from Intel. ++ ++ ++ ++ ++ ++директива worker_shutdown_timeout не работала ++при использовании почтового прокси-сервера ++и при проксировании WebSocket-соединений. ++ ++ ++the "worker_shutdown_timeout" directive did not work ++when using mail proxy and when proxying WebSocket connections. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++при использовании директивы ssl_preread ++в модуле stream не работало переключение на следующий бэкенд. ++ ++ ++switching to the next upstream server in the stream module did not work ++when using the "ssl_preread" directive. ++ ++ ++ ++ ++ ++в модуле ngx_http_v2_module.
++Спасибо Piotr Sikora. ++
++ ++in the ngx_http_v2_module.
++Thanks to Piotr Sikora. ++
++
++ ++ ++ ++nginx не поддерживал даты после 2038 года ++на 32-битных платформах с 64-битным time_t. ++ ++ ++nginx did not support dates after the year 2038 ++on 32-bit platforms with 64-bit time_t. ++ ++ ++ ++ ++ ++в обработке дат до 1970 года и после 10000 года. ++ ++ ++in handling of dates prior to the year 1970 and after the year 10000. ++ ++ ++ ++ ++ ++в модуле stream таймауты ожидания UDP-пакетов от бэкендов ++не логгировались или логгировались на уровне info вместо error. ++ ++ ++in the stream module timeouts waiting for UDP datagrams from upstream servers ++were not logged or logged at the "info" level instead of "error". ++ ++ ++ ++ ++ ++при использовании HTTP/2 nginx мог вернуть ошибку 400, ++не указав в логе причину. ++ ++ ++when using HTTP/2 nginx might return the 400 response ++without logging the reason. ++ ++ ++ ++ ++ ++в обработке повреждённых файлов кэша. ++ ++ ++in processing of corrupted cache files. ++ ++ ++ ++ ++ ++при кэшировании ошибок, перехваченных error_page, ++не учитывались заголовки управления кэшированием. ++ ++ ++cache control headers were ignored ++when caching errors intercepted by error_page. ++ ++ ++ ++ ++ ++при использовании HTTP/2 тело запроса могло быть повреждено. ++ ++ ++when using HTTP/2 client request body might be corrupted. ++ ++ ++ ++ ++ ++в обработке адресов клиентов при использовании unix domain сокетов. ++ ++ ++in handling of client addresses when using unix domain sockets. ++ ++ ++ ++ ++ ++при использовании директивы "hash ... consistent" в блоке upstream ++nginx нагружал процессор, если использовались большие веса ++и все или почти все бэкенды были недоступны. ++ ++ ++nginx hogged CPU ++when using the "hash ... consistent" directive in the upstream block ++if large weights were used and all or most of the servers were unavailable. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++переменная $ssl_client_escaped_cert. ++ ++ ++the $ssl_client_escaped_cert variable. ++ ++ ++ ++ ++ ++директива ssl_session_ticket_key и параметр include директивы geo ++не работали на Windows. ++ ++ ++the "ssl_session_ticket_key" directive and ++the "include" parameter of the "geo" directive did not work on Windows. ++ ++ ++ ++ ++ ++на 32-битных платформах ++при запросе более 4 гигабайт с помощью нескольких диапазонов ++возвращалась некорректная длина ответа. ++ ++ ++incorrect response length was returned ++on 32-bit platforms when requesting more than 4 gigabytes ++with multiple ranges. ++ ++ ++ ++ ++ ++директива "expires modified" и ++обработка строки If-Range заголовка запроса ++не учитывали время последнего изменения ответа, ++если использовалось проксирование без кэширования. ++ ++ ++the "expires modified" directive and ++processing of the "If-Range" request header line ++did not use the response last modification time ++if proxying without caching was used. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++модуль ngx_http_mirror_module. ++ ++ ++the ngx_http_mirror_module. ++ ++ ++ ++ ++ ++клиентские соединения могли сбрасываться при тестировании конфигурации, ++если использовался параметр reuseport директивы listen на Linux. ++ ++ ++client connections might be dropped during configuration testing ++when using the "reuseport" parameter of the "listen" directive on Linux. ++ ++ ++ ++ ++ ++тело запроса могло быть недоступно в подзапросах, ++если оно было сохранено в файл и использовалось проксирование. ++ ++ ++request body might not be available in subrequests ++if it was saved to a file and proxying was used. ++ ++ ++ ++ ++ ++очистка кэша по max_size не работала на Windows. ++ ++ ++cleaning cache based on the "max_size" parameter did not work on Windows. ++ ++ ++ ++ ++ ++любое выделение разделяемой памяти на Windows требовало 4096 байт памяти. ++ ++ ++any shared memory allocation required 4096 bytes on Windows. ++ ++ ++ ++ ++ ++при использовании директивы zone в блоке upstream на Windows ++рабочий процесс мог завершаться аварийно. ++ ++ ++nginx worker might be terminated abnormally ++when using the "zone" directive inside the "upstream" block on Windows. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++специально созданный запрос мог вызвать целочисленное переполнение ++в range-фильтре и последующую некорректную обработку запрошенных диапазонов, ++что потенциально могло привести к утечке конфиденциальной информации ++(CVE-2017-7529). ++ ++ ++a specially crafted request might result in an integer overflow ++and incorrect processing of ranges in the range filter, ++potentially resulting in sensitive information leak ++(CVE-2017-7529). ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++теперь при запросе диапазона, начинающегося с 0, из пустого файла ++nginx возвращает ответ 200 вместо 416. ++ ++ ++nginx now returns 200 instead of 416 ++when a range starting with 0 is requested from an empty file. ++ ++ ++ ++ ++ ++директива add_trailer.
++Спасибо Piotr Sikora. ++
++ ++the "add_trailer" directive.
++Thanks to Piotr Sikora. ++
++
++ ++ ++ ++nginx не собирался под Cygwin и NetBSD; ++ошибка появилась в 1.13.0. ++ ++ ++nginx could not be built on Cygwin and NetBSD; ++the bug had appeared in 1.13.0. ++ ++ ++ ++ ++ ++nginx не собирался под MSYS2 / MinGW 64-bit.
++Спасибо Orgad Shaneh. ++
++ ++nginx could not be built under MSYS2 / MinGW 64-bit.
++Thanks to Orgad Shaneh. ++
++
++ ++ ++ ++при использовании SSI с большим количеством подзапросов ++и proxy_pass с переменными ++в рабочем процессе мог произойти segmentation fault. ++ ++ ++a segmentation fault might occur in a worker process ++when using SSI with many includes ++and proxy_pass with variables. ++ ++ ++ ++ ++ ++в модуле ngx_http_v2_module.
++Спасибо Piotr Sikora. ++
++ ++in the ngx_http_v2_module.
++Thanks to Piotr Sikora. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++теперь в качестве параметра директивы set_real_ip_from ++можно указывать имя хоста. ++ ++ ++now a hostname can be used ++as the "set_real_ip_from" directive parameter. ++ ++ ++ ++ ++ ++улучшения в скриптах подсветки синтаксиса для vim. ++ ++ ++vim syntax highlighting scripts improvements. ++ ++ ++ ++ ++ ++директива worker_cpu_affinity теперь работает на DragonFly BSD.
++Спасибо Sepherosa Ziehau. ++
++ ++the "worker_cpu_affinity" directive now works on DragonFly BSD.
++Thanks to Sepherosa Ziehau. ++
++
++ ++ ++ ++SSL renegotiation в соединениях к бэкендам ++не работал при использовании OpenSSL до 1.1.0. ++ ++ ++SSL renegotiation on backend connections ++did not work when using OpenSSL before 1.1.0. ++ ++ ++ ++ ++ ++nginx не собирался с Oracle Developer Studio 12.5. ++ ++ ++nginx could not be built with Oracle Developer Studio 12.5. ++ ++ ++ ++ ++ ++теперь cache manager пропускает заблокированные записи ++при очистке кэша по max_size. ++ ++ ++now cache manager ignores long locked cache entries ++when cleaning cache based on the "max_size" parameter. ++ ++ ++ ++ ++ ++клиентские SSL-соединения сразу закрывались, если использовался ++отложенный accept и параметр proxy_protocol директивы listen. ++ ++ ++client SSL connections were immediately closed if deferred accept ++and the "proxy_protocol" parameter of the "listen" directive were used. ++ ++ ++ ++ ++ ++в директиве proxy_cache_background_update. ++ ++ ++in the "proxy_cache_background_update" directive. ++ ++ ++ ++ ++ ++теперь директива tcp_nodelay ++устанавливает опцию TCP_NODELAY перед SSL handshake. ++ ++ ++now the "tcp_nodelay" directive ++sets the TCP_NODELAY option before an SSL handshake. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++теперь SSL renegotiation допускается в соединениях к бэкендам. ++ ++ ++SSL renegotiation is now allowed on backend connections. ++ ++ ++ ++ ++ ++параметры rcvbuf и sndbuf директив listen ++в почтовом прокси-сервере и модуле stream. ++ ++ ++the "rcvbuf" and "sndbuf" parameters of the "listen" directives ++of the mail proxy and stream modules. ++ ++ ++ ++ ++ ++директивы return и error_page теперь могут использоваться для возврата ++перенаправлений с кодом 308.
++Спасибо Simon Leblanc. ++
++ ++the "return" and "error_page" directives can now be used to return 308 ++redirections.
++Thanks to Simon Leblanc. ++
++
++ ++ ++ ++параметр TLSv1.3 в директиве ssl_protocols. ++ ++ ++the "TLSv1.3" parameter of the "ssl_protocols" directive. ++ ++ ++ ++ ++ ++при логгировании сигналов теперь указывается PID отправившего сигнал процесса. ++ ++ ++when logging signals nginx now logs PID of the process which sent the signal. ++ ++ ++ ++ ++ ++в обработке ошибок выделения памяти. ++ ++ ++in memory allocation error handling. ++ ++ ++ ++ ++ ++если сервер в модуле stream слушал на wildcard-адресе, ++исходящий адрес ответного UDP-пакета ++мог отличаться от адреса назначения исходного пакета. ++ ++ ++if a server in the stream module listened on a wildcard address, ++the source address of a response UDP datagram could differ ++from the original datagram destination address. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++параметр http_429 в директивах proxy_next_upstream, fastcgi_next_upstream, ++scgi_next_upstream и uwsgi_next_upstream.
++Спасибо Piotr Sikora. ++
++ ++the "http_429" parameter of the "proxy_next_upstream", "fastcgi_next_upstream", ++"scgi_next_upstream", and "uwsgi_next_upstream" directives.
++Thanks to Piotr Sikora. ++
++
++ ++ ++ ++в обработке ошибок выделения памяти. ++ ++ ++in memory allocation error handling. ++ ++ ++ ++ ++ ++при использовании директив sendfile и timer_resolution на Linux ++запросы могли зависать. ++ ++ ++requests might hang ++when using the "sendfile" and "timer_resolution" directives on Linux. ++ ++ ++ ++ ++ ++при использовании с подзапросами директив sendfile и aio_write ++запросы могли зависать. ++ ++ ++requests might hang ++when using the "sendfile" and "aio_write" directives with subrequests. ++ ++ ++ ++ ++ ++в модуле ngx_http_v2_module.
++Спасибо Piotr Sikora. ++
++ ++in the ngx_http_v2_module.
++Thanks to Piotr Sikora. ++
++
++ ++ ++ ++при использовании HTTP/2 в рабочем процессе мог произойти segmentation fault. ++ ++ ++a segmentation fault might occur in a worker process when using HTTP/2. ++ ++ ++ ++ ++ ++запросы могли зависать ++при использовании с подзапросами директив limit_rate, sendfile_max_chunk, ++limit_req или метода $r->sleep() встроенного перла. ++ ++ ++requests might hang ++when using the "limit_rate", "sendfile_max_chunk", "limit_req" directives, ++or the $r->sleep() embedded perl method with subrequests. ++ ++ ++ ++ ++ ++в модуле ngx_http_slice_module. ++ ++ ++in the ngx_http_slice_module. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++nginx мог нагружать процессор; ++ошибка появилась в 1.11.11. ++ ++ ++nginx might hog CPU; ++the bug had appeared in 1.11.11. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директива worker_shutdown_timeout. ++ ++ ++the "worker_shutdown_timeout" directive. ++ ++ ++ ++ ++ ++улучшения в скриптах подсветки синтаксиса для vim.
++Спасибо Wei-Ko Kao. ++
++ ++vim syntax highlighting scripts improvements.
++Thanks to Wei-Ko Kao. ++
++
++ ++ ++ ++при попытке установить переменную $limit_rate в пустую строку ++в рабочем процессе мог произойти segmentation fault. ++ ++ ++a segmentation fault might occur in a worker process ++if the $limit_rate variable was set to an empty string. ++ ++ ++ ++ ++ ++директивы proxy_cache_background_update, fastcgi_cache_background_update, ++scgi_cache_background_update и uwsgi_cache_background_update ++могли работать некорректно, если использовалась директива if. ++ ++ ++the "proxy_cache_background_update", "fastcgi_cache_background_update", ++"scgi_cache_background_update", and "uwsgi_cache_background_update" directives ++might work incorrectly if the "if" directive was used. ++ ++ ++ ++ ++ ++в рабочем процессе мог произойти segmentation fault, ++если количество large_client_header_buffers в виртуальном сервере ++отличалось от такового в сервере по умолчанию. ++ ++ ++a segmentation fault might occur in a worker process ++if number of large_client_header_buffers in a virtual server ++was different from the one in the default server. ++ ++ ++ ++ ++ ++в почтовом прокси-сервере. ++ ++ ++in the mail proxy server. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++формат заголовка кэша был изменен, ++ранее закэшированные ответы будут загружены заново. ++ ++ ++cache header format has been changed, ++previously cached responses will be invalidated. ++ ++ ++ ++ ++ ++поддержка расширений stale-while-revalidate и stale-if-error ++в строке "Cache-Control" в заголовке ответа бэкенда. ++ ++ ++support of "stale-while-revalidate" and "stale-if-error" extensions ++in the "Cache-Control" backend response header line. ++ ++ ++ ++ ++ ++директивы proxy_cache_background_update, fastcgi_cache_background_update, ++scgi_cache_background_update и uwsgi_cache_background_update. ++ ++ ++the "proxy_cache_background_update", "fastcgi_cache_background_update", ++"scgi_cache_background_update", and "uwsgi_cache_background_update" directives. ++ ++ ++ ++ ++ ++теперь nginx может кэшировать ответы ++со строкой Vary заголовка длиной до 128 символов ++(вместо 42 символов в предыдущих версиях). ++ ++ ++nginx is now able to cache responses ++with the "Vary" header line up to 128 characters long ++(instead of 42 characters in previous versions). ++ ++ ++ ++ ++ ++параметр build директивы server_tokens.
++Спасибо Tom Thorogood. ++
++ ++the "build" parameter of the "server_tokens" directive.
++Thanks to Tom Thorogood. ++
++
++ ++ ++ ++при обработке запросов со строкой "Expect: 100-continue" в заголовке запроса ++в логах могли появляться сообщения "[crit] SSL_write() failed". ++ ++ ++"[crit] SSL_write() failed" messages might appear in logs ++when handling requests with the "Expect: 100-continue" request header line. ++ ++ ++ ++ ++ ++модуль ngx_http_slice_module не работал в именованных location'ах. ++ ++ ++the ngx_http_slice_module did not work in named locations. ++ ++ ++ ++ ++ ++при использовании AIO после перенаправления запроса с помощью X-Accel-Redirect ++в рабочем процессе мог произойти segmentation fault. ++ ++ ++a segmentation fault might occur in a worker process ++when using AIO after an "X-Accel-Redirect" redirection. ++ ++ ++ ++ ++ ++уменьшено потребление памяти для долгоживущих запросов, использующих сжатие. ++ ++ ++reduced memory consumption for long-lived requests using gzipping. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++при использовании модуля stream nginx мог нагружать процессор; ++ошибка появилась в 1.11.5. ++ ++ ++nginx might hog CPU when using the stream module; ++the bug had appeared in 1.11.5. ++ ++ ++ ++ ++ ++метод аутентификации EXTERNAL в почтовом прокси-сервере ++можно было использовать, даже если он не был разрешён в конфигурации. ++ ++ ++EXTERNAL authentication mechanism in mail proxy ++was accepted even if it was not enabled in the configuration. ++ ++ ++ ++ ++ ++при использовании директивы ssl_verify_client модуля stream ++в рабочем процессе мог произойти segmentation fault. ++ ++ ++a segmentation fault might occur in a worker process ++if the "ssl_verify_client" directive of the stream module was used. ++ ++ ++ ++ ++ ++директива ssl_verify_client модуля stream могла не работать. ++ ++ ++the "ssl_verify_client" directive of the stream module might not work. ++ ++ ++ ++ ++ ++при исчерпании рабочим процессом свободных соединений ++keepalive-соединения могли закрываться излишне агрессивно.
++Спасибо Joel Cunningham. ++
++ ++closing keepalive connections due to no free worker connections ++might be too aggressive.
++Thanks to Joel Cunningham. ++
++
++ ++ ++ ++при использовании директивы sendfile на FreeBSD и macOS ++мог возвращаться некорректный ответ; ++ошибка появилась в 1.7.8. ++ ++ ++an incorrect response might be returned ++when using the "sendfile" directive on FreeBSD and macOS; ++the bug had appeared in 1.7.8. ++ ++ ++ ++ ++ ++при использовании директивы aio_write ++ответ мог сохраняться в кэш не полностью. ++ ++ ++a truncated response might be stored in cache ++when using the "aio_write" directive. ++ ++ ++ ++ ++ ++при использовании директивы aio_write ++могла происходить утечка сокетов. ++ ++ ++a socket leak might occur ++when using the "aio_write" directive. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++директива absolute_redirect. ++ ++ ++the "absolute_redirect" directive. ++ ++ ++ ++ ++ ++параметр escape директивы log_format. ++ ++ ++the "escape" parameter of the "log_format" directive. ++ ++ ++ ++ ++ ++проверка клиентских SSL-сертификатов в модуле stream. ++ ++ ++client SSL certificates verification in the stream module. ++ ++ ++ ++ ++ ++директива ssl_session_ticket_key поддерживает ++шифрование TLS session tickets с помощью AES256 ++при использовании с 80-байтными ключами. ++ ++ ++the "ssl_session_ticket_key" directive supports ++AES256 encryption of TLS session tickets ++when used with 80-byte keys. ++ ++ ++ ++ ++ ++поддержка vim-commentary в скриптах для vim.
++Спасибо Armin Grodon. ++
++ ++vim-commentary support in vim scripts.
++Thanks to Armin Grodon. ++
++
++ ++ ++ ++рекурсия при получении значений переменных не ограничивалась. ++ ++ ++recursion when evaluating variables was not limited. ++ ++ ++ ++ ++ ++в модуле ngx_stream_ssl_preread_module. ++ ++ ++in the ngx_stream_ssl_preread_module. ++ ++ ++ ++ ++ ++если сервер, описанный в блоке upstream в модуле stream, ++был признан неработающим, то после истечения fail_timeout он ++признавался работающим только после завершения тестового соединения; ++теперь достаточно, чтобы соединение было успешно установлено. ++ ++ ++if a server in an upstream in the stream module failed, ++it was considered alive only when a test connection sent ++to it after fail_timeout was closed; ++now a successfully established connection is enough. ++ ++ ++ ++ ++ ++nginx/Windows не собирался с 64-битным Visual Studio. ++ ++ ++nginx/Windows could not be built with 64-bit Visual Studio. ++ ++ ++ ++ ++ ++nginx/Windows не собирался с OpenSSL 1.1.0. ++ ++ ++nginx/Windows could not be built with OpenSSL 1.1.0. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++переменная $ssl_client_verify теперь ++в случае ошибки проверки клиентского сертификата ++содержит строку с описанием ошибки, ++например, "FAILED:certificate has expired". ++ ++ ++now in case of a client certificate verification error ++the $ssl_client_verify variable contains a string with the failure reason, ++for example, "FAILED:certificate has expired". ++ ++ ++ ++ ++ ++переменные $ssl_ciphers, $ssl_curves, ++$ssl_client_v_start, $ssl_client_v_end и $ssl_client_v_remain. ++ ++ ++the $ssl_ciphers, $ssl_curves, ++$ssl_client_v_start, $ssl_client_v_end, and $ssl_client_v_remain variables. ++ ++ ++ ++ ++ ++параметр volatile директивы map. ++ ++ ++the "volatile" parameter of the "map" directive. ++ ++ ++ ++ ++ ++при сборке динамических модулей ++не учитывались заданные для модуля зависимости. ++ ++ ++dependencies specified for a module ++were ignored while building dynamic modules. ++ ++ ++ ++ ++ ++при использовании HTTP/2 и директив limit_req или auth_request ++тело запроса могло быть повреждено; ++ошибка появилась в 1.11.0. ++ ++ ++when using HTTP/2 and the "limit_req" or "auth_request" directives ++client request body might be corrupted; ++the bug had appeared in 1.11.0. ++ ++ ++ ++ ++ ++при использовании HTTP/2 в рабочем процессе мог произойти segmentation fault; ++ошибка появилась в 1.11.3. ++ ++ ++a segmentation fault might occur in a worker process when using HTTP/2; ++the bug had appeared in 1.11.3. ++ ++ ++ ++ ++ ++в модуле ngx_http_mp4_module.
++Спасибо Congcong Hu. ++
++ ++in the ngx_http_mp4_module.
++Thanks to Congcong Hu. ++
++
++ ++ ++ ++в модуле ngx_http_perl_module. ++ ++ ++in the ngx_http_perl_module. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++формат переменных $ssl_client_s_dn и $ssl_client_i_dn ++изменён на соответствующий RFC 2253 (RFC 4514); ++значения в старом формате доступны через переменные ++$ssl_client_s_dn_legacy и $ssl_client_i_dn_legacy. ++ ++ ++format of the $ssl_client_s_dn and $ssl_client_i_dn variables ++has been changed to follow RFC 2253 (RFC 4514); ++values in the old format are available in ++the $ssl_client_s_dn_legacy and $ssl_client_i_dn_legacy variables. ++ ++ ++ ++ ++ ++при сохранении временных файлов в каталоге кэша ++они теперь располагаются не в отдельном подкаталоге для временных файлов, ++а в том же подкаталоге, что и соответствующие файлы в кэше. ++ ++ ++when storing temporary files in a cache directory ++they will be stored in the same subdirectories as corresponding cache files ++instead of a separate subdirectory for temporary files. ++ ++ ++ ++ ++ ++поддержка метода аутентификации EXTERNAL ++в почтовом прокси-сервере.
++Спасибо Robert Norris. ++
++ ++EXTERNAL authentication mechanism support ++in mail proxy.
++Thanks to Robert Norris. ++
++
++ ++ ++ ++поддержка WebP в модуле ngx_http_image_filter_module. ++ ++ ++WebP support in the ngx_http_image_filter_module. ++ ++ ++ ++ ++ ++директива proxy_method поддерживает переменные.
++Спасибо Дмитрию Лазуркину. ++
++ ++variables support in the "proxy_method" directive.
++Thanks to Dmitry Lazurkin. ++
++
++ ++ ++ ++директива http2_max_requests в модуле ngx_http_v2_module. ++ ++ ++the "http2_max_requests" directive in the ngx_http_v2_module. ++ ++ ++ ++ ++ ++директивы proxy_cache_max_range_offset, fastcgi_cache_max_range_offset, ++scgi_cache_max_range_offset и uwsgi_cache_max_range_offset. ++ ++ ++the "proxy_cache_max_range_offset", "fastcgi_cache_max_range_offset", ++"scgi_cache_max_range_offset", and "uwsgi_cache_max_range_offset" directives. ++ ++ ++ ++ ++ ++плавное завершение старых рабочих процессов могло занимать бесконечное время ++при использовании HTTP/2. ++ ++ ++graceful shutdown of old worker processes might require infinite time ++when using HTTP/2. ++ ++ ++ ++ ++ ++в модуле ngx_http_mp4_module. ++ ++ ++in the ngx_http_mp4_module. ++ ++ ++ ++ ++ ++при проксировании WebSocket-соединений и включённом кэшировании ++в логах могли появляться сообщения "ignore long locked inactive cache entry". ++ ++ ++"ignore long locked inactive cache entry" alerts might appear in logs ++when proxying WebSocket connections with caching enabled. ++ ++ ++ ++ ++ ++если во время SSL handshake с бэкендом происходил таймаут, ++nginx ничего не писал в лог ++и возвращал ответ с кодом 502 вместо 504. ++ ++ ++nginx did not write anything to log ++and returned a response with code 502 instead of 504 ++when a timeout occurred during an SSL handshake to a backend. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++параметр configure --with-ipv6 упразднён, ++поддержка IPv6 теперь собирается автоматически. ++ ++ ++the --with-ipv6 configure option was removed, ++now IPv6 support is configured automatically. ++ ++ ++ ++ ++ ++теперь, если в блоке upstream не оказалось доступных серверов, ++nginx не сбрасывает статистику ошибок всех серверов, как делал ранее, ++а ожидает истечения fail_timeout. ++ ++ ++now if there are no available servers in an upstream, ++nginx will not reset number of failures of all servers as it previously did, ++but will wait for fail_timeout to expire. ++ ++ ++ ++ ++ ++модуль ngx_stream_ssl_preread_module. ++ ++ ++the ngx_stream_ssl_preread_module. ++ ++ ++ ++ ++ ++директива server в блоке upstream поддерживает параметр max_conns. ++ ++ ++the "server" directive in the "upstream" context supports ++the "max_conns" parameter. ++ ++ ++ ++ ++ ++параметр configure --with-compat. ++ ++ ++the --with-compat configure option. ++ ++ ++ ++ ++ ++параметры manager_files, manager_threshold и manager_sleep ++директив proxy_cache_path, fastcgi_cache_path, scgi_cache_path и ++uwsgi_cache_path. ++ ++ ++"manager_files", "manager_threshold", and "manager_sleep" parameters ++of the "proxy_cache_path", "fastcgi_cache_path", "scgi_cache_path", and ++"uwsgi_cache_path" directives. ++ ++ ++ ++ ++ ++при сборке perl-модуля не использовались флаги, ++заданные с помощью параметра configure --with-ld-opt. ++ ++ ++flags passed by the --with-ld-opt configure option ++were not used while building perl module. ++ ++ ++ ++ ++ ++в директиве add_after_body при использовании совместно с директивой sub_filter. ++ ++ ++in the "add_after_body" directive when used with the "sub_filter" directive. ++ ++ ++ ++ ++ ++в переменной $realip_remote_addr. ++ ++ ++in the $realip_remote_addr variable. ++ ++ ++ ++ ++ ++директивы dav_access, proxy_store_access, fastcgi_store_access, ++scgi_store_access и uwsgi_store_access ++игнорировали права, заданные для пользователя. ++ ++ ++the "dav_access", "proxy_store_access", "fastcgi_store_access", ++"scgi_store_access", and "uwsgi_store_access" directives ++ignored permissions specified for user. ++ ++ ++ ++ ++ ++unix domain listen-сокеты могли не наследоваться ++при обновлении исполняемого файла на Linux. ++ ++ ++unix domain listen sockets might not be inherited ++during binary upgrade on Linux. ++ ++ ++ ++ ++ ++nginx возвращал ошибку 400 на запросы ++с символом "-" в HTTP-методе. ++ ++ ++nginx returned the 400 response on requests ++with the "-" character in the HTTP method. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++переменная $upstream_bytes_received. ++ ++ ++the $upstream_bytes_received variable. ++ ++ ++ ++ ++ ++переменные $bytes_received, $session_time, $protocol, $status, ++$upstream_addr, $upstream_bytes_sent, $upstream_bytes_received, ++$upstream_connect_time, $upstream_first_byte_time ++и $upstream_session_time в модуле stream. ++ ++ ++the $bytes_received, $session_time, $protocol, $status, ++$upstream_addr, $upstream_bytes_sent, $upstream_bytes_received, ++$upstream_connect_time, $upstream_first_byte_time, ++and $upstream_session_time variables in the stream module. ++ ++ ++ ++ ++ ++модуль ngx_stream_log_module. ++ ++ ++the ngx_stream_log_module. ++ ++ ++ ++ ++ ++параметр proxy_protocol в директиве listen, ++переменные $proxy_protocol_addr и $proxy_protocol_port ++в модуле stream. ++ ++ ++the "proxy_protocol" parameter of the "listen" directive, ++the $proxy_protocol_addr and $proxy_protocol_port variables ++in the stream module. ++ ++ ++ ++ ++ ++модуль ngx_stream_realip_module. ++ ++ ++the ngx_stream_realip_module. ++ ++ ++ ++ ++ ++nginx не собирался с модулем stream и модулем ngx_http_ssl_module, ++но без модуля ngx_stream_ssl_module; ++ошибка появилась в 1.11.3. ++ ++ ++nginx could not be built with the stream module and the ngx_http_ssl_module, ++but without ngx_stream_ssl_module; ++the bug had appeared in 1.11.3. ++ ++ ++ ++ ++ ++опция сокета IP_BIND_ADDRESS_NO_PORT не использовалась; ++ошибка появилась в 1.11.2. ++ ++ ++the IP_BIND_ADDRESS_NO_PORT socket option was not used; ++the bug had appeared in 1.11.2. ++ ++ ++ ++ ++ ++в параметре ranges директивы geo. ++ ++ ++in the "ranges" parameter of the "geo" directive. ++ ++ ++ ++ ++ ++при использовании директив "aio threads" и sendfile ++мог возвращаться некорректный ответ; ошибка появилась в 1.9.13. ++ ++ ++an incorrect response might be returned ++when using the "aio threads" and "sendfile" directives; ++the bug had appeared in 1.9.13. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++теперь accept_mutex по умолчанию выключен. ++ ++ ++now the "accept_mutex" directive is turned off by default. ++ ++ ++ ++ ++ ++теперь nginx использует EPOLLEXCLUSIVE на Linux. ++ ++ ++now nginx uses EPOLLEXCLUSIVE on Linux. ++ ++ ++ ++ ++ ++модуль ngx_stream_geo_module. ++ ++ ++the ngx_stream_geo_module. ++ ++ ++ ++ ++ ++модуль ngx_stream_geoip_module. ++ ++ ++the ngx_stream_geoip_module. ++ ++ ++ ++ ++ ++модуль ngx_stream_split_clients_module. ++ ++ ++the ngx_stream_split_clients_module. ++ ++ ++ ++ ++ ++директивы proxy_pass и proxy_ssl_name в модуле stream ++поддерживают переменные. ++ ++ ++variables support ++in the "proxy_pass" and "proxy_ssl_name" directives in the stream module. ++ ++ ++ ++ ++ ++утечки сокетов при использовании HTTP/2. ++ ++ ++socket leak when using HTTP/2. ++ ++ ++ ++ ++ ++в configure.
++Спасибо Piotr Sikora. ++
++ ++in configure tests.
++Thanks to Piotr Sikora. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++теперь nginx всегда использует внутренние реализации MD5 и SHA1; ++параметры configure --with-md5 и --with-sha1 упразднены. ++ ++ ++now nginx always uses internal MD5 and SHA1 implementations; ++the --with-md5 and --with-sha1 configure options were canceled. ++ ++ ++ ++ ++ ++поддержка переменных в модуле stream. ++ ++ ++variables support in the stream module. ++ ++ ++ ++ ++ ++модуль ngx_stream_map_module. ++ ++ ++the ngx_stream_map_module. ++ ++ ++ ++ ++ ++модуль ngx_stream_return_module. ++ ++ ++the ngx_stream_return_module. ++ ++ ++ ++ ++ ++в директивах proxy_bind, fastcgi_bind, memcached_bind, scgi_bind и uwsgi_bind ++теперь можно указывать порт. ++ ++ ++a port can be specified in the "proxy_bind", "fastcgi_bind", ++"memcached_bind", "scgi_bind", and "uwsgi_bind" directives. ++ ++ ++ ++ ++ ++теперь nginx использует опцию сокета IP_BIND_ADDRESS_NO_PORT, если она доступна. ++ ++ ++now nginx uses the IP_BIND_ADDRESS_NO_PORT socket option when available. ++ ++ ++ ++ ++ ++при использовании HTTP/2 и директивы proxy_request_buffering ++в рабочем процессе мог произойти segmentation fault. ++ ++ ++a segmentation fault might occur in a worker process ++when using HTTP/2 and the "proxy_request_buffering" directive. ++ ++ ++ ++ ++ ++при использовании HTTP/2 ++к запросам, передаваемым на бэкенд, ++всегда добавлялась строка заголовка "Content-Length", ++даже если у запроса не было тела. ++ ++ ++the "Content-Length" request header line ++was always added to requests passed to backends, ++including requests without body, ++when using HTTP/2. ++ ++ ++ ++ ++ ++при использовании HTTP/2 ++в логах могли появляться сообщения "http request count is zero". ++ ++ ++"http request count is zero" alerts might appear in logs ++when using HTTP/2. ++ ++ ++ ++ ++ ++при использовании директивы sub_filter ++могло буферизироваться больше данных, чем это необходимо; ++проблема появилась в 1.9.4. ++ ++ ++unnecessary buffering might occur ++when using the "sub_filter" directive; ++the issue had appeared in 1.9.4. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++при записи тела специально созданного запроса во временный файл ++в рабочем процессе мог происходить segmentation fault ++(CVE-2016-4450); ++ошибка появилась в 1.3.9. ++ ++ ++a segmentation fault might occur in a worker process ++while writing a specially crafted request body to a temporary file ++(CVE-2016-4450); ++the bug had appeared in 1.3.9. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++параметр transparent директив proxy_bind, fastcgi_bind, ++memcached_bind, scgi_bind и uwsgi_bind. ++ ++ ++the "transparent" parameter of the "proxy_bind", "fastcgi_bind", ++"memcached_bind", "scgi_bind", and "uwsgi_bind" directives. ++ ++ ++ ++ ++ ++переменная $request_id. ++ ++ ++the $request_id variable. ++ ++ ++ ++ ++ ++директива map поддерживает комбинации нескольких переменных ++в качестве результирующих значений. ++ ++ ++the "map" directive supports combinations of multiple variables ++as resulting values. ++ ++ ++ ++ ++ ++теперь при использовании метода epoll ++nginx проверяет, поддерживает ли ядро события EPOLLRDHUP, ++и соответственно оптимизирует обработку соединений. ++ ++ ++now nginx checks if EPOLLRDHUP events are supported by kernel, ++and optimizes connection handling accordingly ++if the "epoll" method is used. ++ ++ ++ ++ ++ ++директивы ssl_certificate и ssl_certificate_key ++теперь можно указывать несколько раз ++для загрузки сертификатов разных типов (например, RSA и ECDSA). ++ ++ ++the "ssl_certificate" and "ssl_certificate_key" directives ++can be specified multiple times ++to load certificates of different types (for example, RSA and ECDSA). ++ ++ ++ ++ ++ ++при использовании OpenSSL 1.0.2 и новее ++с помощью директивы ssl_ecdh_curve теперь можно задать список кривых; ++по умолчанию используется встроенный в OpenSSL список кривых. ++ ++ ++the "ssl_ecdh_curve" directive now allows specifying a list of curves ++when using OpenSSL 1.0.2 or newer; ++by default a list built into OpenSSL is used. ++ ++ ++ ++ ++ ++для использования DHE-шифров теперь надо явно задавать файл параметров ++с помощью директивы ssl_dhparam. ++ ++ ++to use DHE ciphers it is now required to specify parameters ++using the "ssl_dhparam" directive. ++ ++ ++ ++ ++ ++переменная $proxy_protocol_port. ++ ++ ++the $proxy_protocol_port variable. ++ ++ ++ ++ ++ ++переменная $realip_remote_port в модуле ngx_http_realip_module. ++ ++ ++the $realip_remote_port variable in the ngx_http_realip_module. ++ ++ ++ ++ ++ ++модуль ngx_http_realip_module теперь позволяет устанавливать ++не только адрес, но и порт клиента. ++ ++ ++the ngx_http_realip_module is now able to set the client port ++in addition to the address. ++ ++ ++ ++ ++ ++при попытке запросить виртуальный сервер, ++отличающийся от согласованного в процессе SSL handshake, ++теперь возвращается ответ "421 Misdirected Request"; ++это улучшает совместимость с некоторыми HTTP/2-клиентами ++в случае использования клиентских сертификатов. ++ ++ ++the "421 Misdirected Request" response now used ++when rejecting requests to a virtual server ++different from one negotiated during an SSL handshake; ++this improves interoperability with some HTTP/2 clients ++when using client certificates. ++ ++ ++ ++ ++ ++HTTP/2-клиенты теперь могут сразу присылать тело запроса; ++директива http2_body_preread_size позволяет указать размер буфера, который ++будет использоваться до того, как nginx начнёт читать тело. ++ ++ ++HTTP/2 clients can now start sending request body immediately; ++the "http2_body_preread_size" directive controls size of the buffer used ++before nginx will start reading client request body. ++ ++ ++ ++ ++ ++при использовании директивы proxy_cache_bypass ++не обновлялись закэшированные ошибочные ответы. ++ ++ ++cached error responses were not updated ++when using the "proxy_cache_bypass" directive. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++при использовании HHVM в качестве FastCGI-сервера ++могли возникать ошибки "recv() failed". ++ ++ ++"recv() failed" errors might occur ++when using HHVM as a FastCGI server. ++ ++ ++ ++ ++ ++при использовании HTTP/2 и директив limit_req или auth_request ++при чтении тела запроса мог произойти таймаут ++или ошибка "client violated flow control"; ++ошибка появилась в 1.9.14. ++ ++ ++when using HTTP/2 and the "limit_req" or "auth_request" directives ++a timeout or a "client violated flow control" error ++might occur while reading client request body; ++the bug had appeared in 1.9.14. ++ ++ ++ ++ ++ ++при использовании HTTP/2 ответ мог не показываться некоторыми браузерами, ++если тело запроса было прочитано не целиком; ++ошибка появилась в 1.9.14. ++ ++ ++a response might not be shown by some browsers ++if HTTP/2 was used and client request body was not fully read; ++the bug had appeared in 1.9.14. ++ ++ ++ ++ ++ ++при использовании директивы "aio threads" соединения могли зависать.
++Спасибо Mindaugas Rasiukevicius. ++
++ ++connections might hang when using the "aio threads" directive.
++Thanks to Mindaugas Rasiukevicius. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++совместимость с OpenSSL 1.1.0. ++ ++ ++OpenSSL 1.1.0 compatibility. ++ ++ ++ ++ ++ ++директивы proxy_request_buffering, fastcgi_request_buffering, ++scgi_request_buffering и uwsgi_request_buffering ++теперь работают при использовании HTTP/2. ++ ++ ++the "proxy_request_buffering", "fastcgi_request_buffering", ++"scgi_request_buffering", and "uwsgi_request_buffering" directives ++now work with HTTP/2. ++ ++ ++ ++ ++ ++при использовании HTTP/2 ++в логах могли появляться сообщения "zero size buf in output". ++ ++ ++"zero size buf in output" alerts might appear in logs ++when using HTTP/2. ++ ++ ++ ++ ++ ++при использовании HTTP/2 ++директива client_max_body_size могла работать неверно. ++ ++ ++the "client_max_body_size" directive might work incorrectly ++when using HTTP/2. ++ ++ ++ ++ ++ ++незначительных ошибок логгирования. ++ ++ ++of minor bugs in logging. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++неидемпотентные запросы (POST, LOCK, PATCH) ++теперь по умолчанию не передаются на другой сервер, ++если запрос уже был отправлен на бэкенд; ++параметр non_idempotent директивы proxy_next_upstream ++явно разрешает повторять такие запросы. ++ ++ ++non-idempotent requests (POST, LOCK, PATCH) ++are no longer passed to the next server by default ++if a request has been sent to a backend; ++the "non_idempotent" parameter of the "proxy_next_upstream" directive ++explicitly allows retrying such requests. ++ ++ ++ ++ ++ ++модуль ngx_http_perl_module теперь можно собрать динамически. ++ ++ ++the ngx_http_perl_module can be built dynamically. ++ ++ ++ ++ ++ ++поддержка UDP в модуле stream. ++ ++ ++UDP support in the stream module. ++ ++ ++ ++ ++ ++директива aio_write. ++ ++ ++the "aio_write" directive. ++ ++ ++ ++ ++ ++теперь cache manager следит за количеством элементов в кэше ++и старается не допускать переполнений зоны разделяемой памяти. ++ ++ ++now cache manager monitors number of elements in caches ++and tries to avoid cache keys zone overflows. ++ ++ ++ ++ ++ ++при использовании директив sendfile и aio с подзапросами ++в логах могли появляться сообщения "task already active" и "second aio post". ++ ++ ++"task already active" and "second aio post" alerts might appear in logs ++when using the "sendfile" and "aio" directives with subrequests. ++ ++ ++ ++ ++ ++при использовании кэширования ++в логах могли появляться сообщения "zero size buf in output", ++если клиент закрывал соединение преждевременно. ++ ++ ++"zero size buf in output" alerts might appear in logs ++if caching was used ++and a client closed a connection prematurely. ++ ++ ++ ++ ++ ++при использовании кэширования ++соединения с клиентами могли закрываться без необходимости.
++Спасибо Justin Li. ++
++ ++connections with clients might be closed needlessly ++if caching was used.
++Thanks to Justin Li. ++
++
++ ++ ++ ++nginx мог нагружать процессор ++при использовании директивы sendfile на Linux и Solaris, ++если отправляемый файл был изменён в процессе отправки. ++ ++ ++nginx might hog CPU ++if the "sendfile" directive was used on Linux or Solaris ++and a file being sent was changed during sending. ++ ++ ++ ++ ++ ++при использовании директив sendfile и "aio threads" ++соединения могли зависать. ++ ++ ++connections might hang ++when using the "sendfile" and "aio threads" directives. ++ ++ ++ ++ ++ ++в директивах proxy_pass, fastcgi_pass, scgi_pass и uwsgi_pass ++при использовании переменных.
++Спасибо Piotr Sikora. ++
++ ++in the "proxy_pass", "fastcgi_pass", "scgi_pass", and "uwsgi_pass" directives ++when using variables.
++Thanks to Piotr Sikora. ++
++
++ ++ ++ ++в модуле ngx_http_sub_filter_module. ++ ++ ++in the ngx_http_sub_filter_module. ++ ++ ++ ++ ++ ++если в закэшированном соединении к бэкенду происходила ошибка, ++запрос передавался на другой сервер ++без учёта директивы proxy_next_upstream. ++ ++ ++if an error occurred in a cached backend connection, ++the request was passed to the next server ++regardless of the proxy_next_upstream directive. ++ ++ ++ ++ ++ ++ошибки "CreateFile() failed" при создании временных файлов на Windows. ++ ++ ++"CreateFile() failed" errors when creating temporary files on Windows. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++кодирование Хаффмана заголовков ответов в HTTP/2.
++Спасибо Владу Краснову. ++
++ ++Huffman encoding of response headers in HTTP/2.
++Thanks to Vlad Krasnov. ++
++
++ ++ ++ ++директива worker_cpu_affinity теперь поддерживает более 64 процессоров. ++ ++ ++the "worker_cpu_affinity" directive now supports more than 64 CPUs. ++ ++ ++ ++ ++ ++совместимость со сторонними модулями на C++; ++ошибка появилась в 1.9.11.
++Спасибо Piotr Sikora. ++
++ ++compatibility with 3rd party C++ modules; ++the bug had appeared in 1.9.11.
++Thanks to Piotr Sikora. ++
++
++ ++ ++ ++nginx не собирался статически с OpenSSL на Linux; ++ошибка появилась в 1.9.11. ++ ++ ++nginx could not be built statically with OpenSSL on Linux; ++the bug had appeared in 1.9.11. ++ ++ ++ ++ ++ ++директива "add_header ... always" с пустым значением ++не удаляла из заголовков ошибочных ответов ++строки Last-Modified и ETag. ++ ++ ++the "add_header ... always" directive with an empty value ++did not delete "Last-Modified" and "ETag" header lines ++from error responses. ++ ++ ++ ++ ++ ++при использовании OpenSSL 1.0.2f в логах могли появляться ++сообщения "called a function you should not call" и ++"shutdown while in init". ++ ++ ++"called a function you should not call" ++and "shutdown while in init" messages might appear in logs ++when using OpenSSL 1.0.2f. ++ ++ ++ ++ ++ ++ошибочные заголовки могли логгироваться некорректно. ++ ++ ++invalid headers might be logged incorrectly. ++ ++ ++ ++ ++ ++утечки сокетов при использовании HTTP/2. ++ ++ ++socket leak when using HTTP/2. ++ ++ ++ ++ ++ ++в модуле ngx_http_v2_module. ++ ++ ++in the ngx_http_v2_module. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++теперь resolver поддерживает TCP. ++ ++ ++TCP support in resolver. ++ ++ ++ ++ ++ ++динамические модули. ++ ++ ++dynamic modules. ++ ++ ++ ++ ++ ++при использовании HTTP/2 ++переменная $request_length не учитывала размер заголовков запроса. ++ ++ ++the $request_length variable did not include size of request headers ++when using HTTP/2. ++ ++ ++ ++ ++ ++в модуле ngx_http_v2_module. ++ ++ ++in the ngx_http_v2_module. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++при использовании директивы resolver ++во время обработки ответов DNS-сервера ++могло происходить разыменование некорректного адреса, ++что позволяло атакующему, ++имеющему возможность подделывать UDP-пакеты от DNS-сервера, ++вызвать segmentation fault в рабочем процессе (CVE-2016-0742). ++ ++ ++invalid pointer dereference might occur ++during DNS server response processing ++if the "resolver" directive was used, ++allowing an attacker who is able to forge UDP packets from the DNS server ++to cause segmentation fault in a worker process (CVE-2016-0742). ++ ++ ++ ++ ++ ++при использовании директивы resolver ++во время обработки CNAME-записей ++могло произойти обращение к ранее освобождённой памяти, ++что позволяло атакующему, ++имеющему возможность инициировать преобразование произвольных имён в адреса, ++вызвать segmentation fault в рабочем процессе, ++а также потенциально могло иметь другие последствия (CVE-2016-0746). ++ ++ ++use-after-free condition might occur ++during CNAME response processing ++if the "resolver" directive was used, ++allowing an attacker who is able to trigger name resolution ++to cause segmentation fault in a worker process, ++or might have potential other impact (CVE-2016-0746). ++ ++ ++ ++ ++ ++при использовании директивы resolver ++во время обработки CNAME-записей ++не во всех случаях проверялось ограничение ++на максимальное количество записей в цепочке, ++что позволяло атакующему, ++имеющему возможность инициировать преобразование произвольных имён в адреса, ++вызвать чрезмерное потребление ресурсов рабочими процессами (CVE-2016-0747). ++ ++ ++CNAME resolution was insufficiently limited ++if the "resolver" directive was used, ++allowing an attacker who is able to trigger arbitrary name resolution ++to cause excessive resource consumption in worker processes (CVE-2016-0747). ++ ++ ++ ++ ++ ++параметр auto директивы worker_cpu_affinity. ++ ++ ++the "auto" parameter of the "worker_cpu_affinity" directive. ++ ++ ++ ++ ++ ++параметр proxy_protocol директивы listen не работал ++с IPv6 listen-сокетами. ++ ++ ++the "proxy_protocol" parameter of the "listen" directive did not work ++with IPv6 listen sockets. ++ ++ ++ ++ ++ ++при использовании директивы keepalive ++соединения к бэкендам могли кэшироваться некорректно. ++ ++ ++connections to upstream servers might be cached incorrectly ++when using the "keepalive" directive. ++ ++ ++ ++ ++ ++после перенаправления запроса с помощью X-Accel-Redirect ++при проксировании использовался HTTP-метод оригинального запроса. ++ ++ ++proxying used the HTTP method of the original request ++after an "X-Accel-Redirect" redirection. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++проксирование в unix domain сокеты не работало при использовании переменных; ++ошибка появилась в 1.9.8. ++ ++ ++proxying to unix domain sockets did not work when using variables; ++the bug had appeared in 1.9.8. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++поддержка pwritev(). ++ ++ ++pwritev() support. ++ ++ ++ ++ ++ ++директива include в блоке upstream. ++ ++ ++the "include" directive inside the "upstream" block. ++ ++ ++ ++ ++ ++модуль ngx_http_slice_module. ++ ++ ++the ngx_http_slice_module. ++ ++ ++ ++ ++ ++при использовании LibreSSL ++в рабочем процессе мог произойти segmentation fault; ++ошибка появилась в 1.9.6. ++ ++ ++a segmentation fault might occur in a worker process ++when using LibreSSL; ++the bug had appeared in 1.9.6. ++ ++ ++ ++ ++ ++nginx мог не собираться на OS X. ++ ++ ++nginx could not be built on OS X in some cases. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++параметр nohostname логгирования в syslog. ++ ++ ++the "nohostname" parameter of logging to syslog. ++ ++ ++ ++ ++ ++директива proxy_cache_convert_head. ++ ++ ++the "proxy_cache_convert_head" directive. ++ ++ ++ ++ ++ ++переменная $realip_remote_addr в модуле ngx_http_realip_module. ++ ++ ++the $realip_remote_addr variable in the ngx_http_realip_module. ++ ++ ++ ++ ++ ++директива expires могла не срабатывать при использовании переменных. ++ ++ ++the "expires" directive might not work when using variables. ++ ++ ++ ++ ++ ++при использовании HTTP/2 ++в рабочем процессе мог произойти segmentation fault; ++ошибка появилась в 1.9.6. ++ ++ ++a segmentation fault might occur in a worker process ++when using HTTP/2; ++the bug had appeared in 1.9.6. ++ ++ ++ ++ ++ ++если nginx был собран с модулем ngx_http_v2_module, ++протокол HTTP/2 мог быть использован клиентом, ++даже если не был указан параметр http2 директивы listen. ++ ++ ++if nginx was built with the ngx_http_v2_module ++it was possible to use the HTTP/2 protocol ++even if the "http2" parameter of the "listen" directive was not specified. ++ ++ ++ ++ ++ ++в модуле ngx_http_v2_module. ++ ++ ++in the ngx_http_v2_module. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++при использовании HTTP/2 ++в рабочем процессе мог произойти segmentation fault.
++Спасибо Piotr Sikora и Denis Andzakovic. ++
++ ++a segmentation fault might occur in a worker process ++when using HTTP/2.
++Thanks to Piotr Sikora and Denis Andzakovic. ++
++
++ ++ ++ ++при использовании HTTP/2 переменная $server_protocol была пустой. ++ ++ ++the $server_protocol variable was empty when using HTTP/2. ++ ++ ++ ++ ++ ++SSL-соединения к бэкендам в модуле stream ++могли неожиданно завершаться по таймауту. ++ ++ ++backend SSL connections in the stream module ++might be timed out unexpectedly. ++ ++ ++ ++ ++ ++при использовании различных настроек ssl_session_cache ++в разных виртуальных серверах ++в рабочем процессе мог произойти segmentation fault. ++ ++ ++a segmentation fault might occur in a worker process ++if different ssl_session_cache settings were used ++in different virtual servers. ++ ++ ++ ++ ++ ++nginx/Windows не собирался с MinGW gcc; ++ошибка появилась в 1.9.4.
++Спасибо Kouhei Sutou. ++
++ ++nginx/Windows could not be built with MinGW gcc; ++the bug had appeared in 1.9.4.
++Thanks to Kouhei Sutou. ++
++
++ ++ ++ ++при использовании директивы timer_resolution на Windows время не обновлялось. ++ ++ ++time was not updated when the timer_resolution directive was used on Windows. ++ ++ ++ ++ ++ ++Незначительные исправления и улучшения.
++Спасибо Markus Linnala, Kurtis Nusbaum и Piotr Sikora. ++
++ ++Miscellaneous minor fixes and improvements.
++Thanks to Markus Linnala, Kurtis Nusbaum and Piotr Sikora. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++модуль ngx_http_v2_module (заменяет модуль ngx_http_spdy_module).
++Спасибо Dropbox и Automattic за спонсирование разработки. ++
++ ++the ngx_http_v2_module (replaces ngx_http_spdy_module).
++Thanks to Dropbox and Automattic for sponsoring this work. ++
++
++ ++ ++ ++теперь по умолчанию директива output_buffers использует два буфера. ++ ++ ++now the "output_buffers" directive uses two buffers by default. ++ ++ ++ ++ ++ ++теперь nginx ограничивает максимальную вложенность подзапросов, ++а не количество одновременных подзапросов. ++ ++ ++now nginx limits subrequests recursion, ++not simultaneous subrequests. ++ ++ ++ ++ ++ ++теперь при возврате ответов из кэша nginx проверяет ключ полностью.
++Спасибо Геннадию Махомеду и Сергею Брестеру. ++
++ ++now nginx checks the whole cache key when returning a response from cache.
++Thanks to Gena Makhomed and Sergey Brester. ++
++
++ ++ ++ ++при использовании кэша ++в логах могли появляться сообщения "header already sent"; ++ошибка появилась в 1.7.5. ++ ++ ++"header already sent" alerts might appear in logs ++when using cache; ++the bug had appeared in 1.7.5. ++ ++ ++ ++ ++ ++при использовании CephFS и директивы timer_resolution на Linux ++в логах могли появляться сообщения ++"writev() failed (4: Interrupted system call)". ++ ++ ++"writev() failed (4: Interrupted system call)" ++errors might appear in logs ++when using CephFS and the "timer_resolution" directive on Linux. ++ ++ ++ ++ ++ ++в обработке ошибок конфигурации.
++Спасибо Markus Linnala. ++
++ ++in invalid configurations handling.
++Thanks to Markus Linnala. ++
++
++ ++ ++ ++при использовании директивы sub_filter на уровне http ++в рабочем процессе происходил segmentation fault; ++ошибка появилась в 1.9.4. ++ ++ ++a segmentation fault occurred in a worker process ++if the "sub_filter" directive was used at http level; ++the bug had appeared in 1.9.4. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++директивы proxy_downstream_buffer и proxy_upstream_buffer в модуле stream ++заменены директивой proxy_buffer_size. ++ ++ ++the "proxy_downstream_buffer" and "proxy_upstream_buffer" directives ++of the stream module are replaced with the "proxy_buffer_size" directive. ++ ++ ++ ++ ++ ++директива tcp_nodelay в модуле stream. ++ ++ ++the "tcp_nodelay" directive in the stream module. ++ ++ ++ ++ ++ ++теперь можно указать несколько директив sub_filter одновременно. ++ ++ ++multiple "sub_filter" directives can be used simultaneously. ++ ++ ++ ++ ++ ++директива sub_filter поддерживает переменные в строке поиска. ++ ++ ++variables support in the search string of the "sub_filter" directive. ++ ++ ++ ++ ++ ++тестирование конфигурации могло не работать под Linux OpenVZ.
++Спасибо Геннадию Махомеду. ++
++ ++configuration testing might fail under Linux OpenVZ.
++Thanks to Gena Makhomed. ++
++
++ ++ ++ ++после переконфигурации старые рабочие процессы могли сильно нагружать процессор ++при больших значениях worker_connections. ++ ++ ++old worker processes might hog CPU after reconfiguration ++with a large number of worker_connections. ++ ++ ++ ++ ++ ++при совместном использовании директив try_files и alias ++внутри location'а, заданного регулярным выражением, ++в рабочем процессе мог произойти segmentation fault; ++ошибка появилась в 1.7.1. ++ ++ ++a segmentation fault might occur in a worker process ++if the "try_files" and "alias" directives were used ++inside a location given by a regular expression; ++the bug had appeared in 1.7.1. ++ ++ ++ ++ ++ ++директива try_files внутри вложенного location'а, заданного регулярным ++выражением, работала неправильно, если во внешнем location'е использовалась ++директива alias. ++ ++ ++the "try_files" directive inside a nested location ++given by a regular expression worked incorrectly ++if the "alias" directive was used in the outer location. ++ ++ ++ ++ ++ ++в обработке ошибок при построении хэш-таблиц. ++ ++ ++in hash table initialization error handling. ++ ++ ++ ++ ++ ++nginx не собирался с Visual Studio 2015. ++ ++ ++nginx could not be built with Visual Studio 2015. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++дублирующиеся блоки http, mail и stream теперь запрещены. ++ ++ ++duplicate "http", "mail", and "stream" blocks are now disallowed. ++ ++ ++ ++ ++ ++ограничение количества соединений в модуле stream. ++ ++ ++connection limiting in the stream module. ++ ++ ++ ++ ++ ++ограничение скорости в модуле stream. ++ ++ ++data rate limiting in the stream module. ++ ++ ++ ++ ++ ++директива zone в блоке upstream не работала на Windows. ++ ++ ++the "zone" directive inside the "upstream" block did not work on Windows. ++ ++ ++ ++ ++ ++совместимость с LibreSSL в модуле stream.
++Спасибо Piotr Sikora. ++
++ ++compatibility with LibreSSL in the stream module.
++Thanks to Piotr Sikora. ++
++
++ ++ ++ ++в параметре --builddir в configure.
++Спасибо Piotr Sikora. ++
++ ++in the "--builddir" configure parameter.
++Thanks to Piotr Sikora. ++
++
++ ++ ++ ++директива ssl_stapling_file не работала; ++ошибка появилась в 1.9.2.
++Спасибо Faidon Liambotis и Brandon Black. ++
++ ++the "ssl_stapling_file" directive did not work; ++the bug had appeared in 1.9.2.
++Thanks to Faidon Liambotis and Brandon Black. ++
++
++ ++ ++ ++при использовании директивы ssl_stapling ++в рабочем процессе мог произойти segmentation fault; ++ошибка появилась в 1.9.2.
++Спасибо Matthew Baldwin. ++
++ ++a segmentation fault might occur in a worker process ++if the "ssl_stapling" directive was used; ++the bug had appeared in 1.9.2.
++Thanks to Matthew Baldwin. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++параметр backlog директивы listen ++в почтовом прокси-сервере и модуле stream. ++ ++ ++the "backlog" parameter of the "listen" directives ++of the mail proxy and stream modules. ++ ++ ++ ++ ++ ++директивы allow и deny в модуле stream. ++ ++ ++the "allow" and "deny" directives in the stream module. ++ ++ ++ ++ ++ ++директива proxy_bind в модуле stream. ++ ++ ++the "proxy_bind" directive in the stream module. ++ ++ ++ ++ ++ ++директива proxy_protocol в модуле stream. ++ ++ ++the "proxy_protocol" directive in the stream module. ++ ++ ++ ++ ++ ++ключ -T. ++ ++ ++the -T switch. ++ ++ ++ ++ ++ ++параметр REQUEST_SCHEME добавлен в стандартные конфигурационные файлы ++fastcgi.conf, fastcgi_params, scgi_params и uwsgi_params. ++ ++ ++the REQUEST_SCHEME parameter added to the fastcgi.conf, fastcgi_params, ++scgi_params, and uwsgi_params standard configuration files. ++ ++ ++ ++ ++ ++параметр reuseport директивы listen в модуле stream ++не работал. ++ ++ ++the "reuseport" parameter of the "listen" directive of the stream module ++did not work. ++ ++ ++ ++ ++ ++OCSP stapling в некоторых случаях мог вернуть устаревший OCSP-ответ. ++ ++ ++OCSP stapling might return an expired OCSP response in some cases. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++теперь протокол SSLv3 по умолчанию запрещён. ++ ++ ++now SSLv3 protocol is disabled by default. ++ ++ ++ ++ ++ ++некоторые давно устаревшие директивы больше не поддерживаются. ++ ++ ++some long deprecated directives are not supported anymore. ++ ++ ++ ++ ++ ++параметр reuseport директивы listen.
++Спасибо Yingqi Lu из Intel и Sepherosa Ziehau. ++
++ ++the "reuseport" parameter of the "listen" directive.
++Thanks to Yingqi Lu at Intel and Sepherosa Ziehau. ++
++
++ ++ ++ ++переменная $upstream_connect_time. ++ ++ ++the $upstream_connect_time variable. ++ ++ ++ ++ ++ ++в директиве hash на big-endian платформах. ++ ++ ++in the "hash" directive on big-endian platforms. ++ ++ ++ ++ ++ ++nginx мог не запускаться на некоторых старых версиях Linux; ++ошибка появилась в 1.7.11. ++ ++ ++nginx might fail to start on some old Linux variants; ++the bug had appeared in 1.7.11. ++ ++ ++ ++ ++ ++в парсинге IP-адресов.
++Спасибо Сергею Половко. ++
++ ++in IP address parsing.
++Thanks to Sergey Polovko. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++устаревшие методы обработки соединений aio и rtsig больше не поддерживаются. ++ ++ ++obsolete aio and rtsig event methods have been removed. ++ ++ ++ ++ ++ ++директива zone в блоке upstream. ++ ++ ++the "zone" directive inside the "upstream" block. ++ ++ ++ ++ ++ ++модуль stream. ++ ++ ++the stream module. ++ ++ ++ ++ ++ ++поддержка byte ranges для ответов модуля ngx_http_memcached_module.
++Спасибо Martin Mlynář. ++
++ ++byte ranges support in the ngx_http_memcached_module.
++Thanks to Martin Mlynář. ++
++
++ ++ ++ ++разделяемую память теперь можно использовать на версиях Windows ++с рандомизацией адресного пространства.
++Спасибо Сергею Брестеру. ++
++ ++shared memory can now be used on Windows versions ++with address space layout randomization.
++Thanks to Sergey Brester. ++
++
++ ++ ++ ++директиву error_log теперь можно использовать ++на уровнях mail и server в почтовом прокси-сервере. ++ ++ ++the "error_log" directive can now be used ++on mail and server levels in mail proxy. ++ ++ ++ ++ ++ ++параметр proxy_protocol директивы listen не работал, ++если не был указан в первой директиве listen для данного listen-сокета. ++ ++ ++the "proxy_protocol" parameter of the "listen" directive did not work ++if not specified in the first "listen" directive for a listen socket. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++теперь директива tcp_nodelay работает для SSL-соединений с бэкендами. ++ ++ ++now the "tcp_nodelay" directive works with backend SSL connections. ++ ++ ++ ++ ++ ++теперь потоки могут использоваться для чтения заголовков файлов в кэше. ++ ++ ++now thread pools can be used to read cache file headers. ++ ++ ++ ++ ++ ++в директиве proxy_request_buffering. ++ ++ ++in the "proxy_request_buffering" directive. ++ ++ ++ ++ ++ ++при использовании потоков на Linux ++в рабочем процессе мог произойти segmentation fault. ++ ++ ++a segmentation fault might occur in a worker process ++when using thread pools on Linux. ++ ++ ++ ++ ++ ++в обработке ошибок при использовании директивы ssl_stapling.
++Спасибо Filipe da Silva. ++
++ ++in error handling when using the "ssl_stapling" directive.
++Thanks to Filipe da Silva. ++
++
++ ++ ++ ++в модуле ngx_http_spdy_module. ++ ++ ++in the ngx_http_spdy_module. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++параметр sendfile директивы aio более не нужен; ++теперь nginx автоматически использует AIO для подгрузки данных для sendfile, ++если одновременно используются директивы aio и sendfile. ++ ++ ++the "sendfile" parameter of the "aio" directive is deprecated; ++now nginx automatically uses AIO to pre-load data for sendfile ++if both "aio" and "sendfile" directives are used. ++ ++ ++ ++ ++ ++экспериментальная поддержка потоков. ++ ++ ++experimental thread pools support. ++ ++ ++ ++ ++ ++директивы proxy_request_buffering, fastcgi_request_buffering, ++scgi_request_buffering и uwsgi_request_buffering. ++ ++ ++the "proxy_request_buffering", "fastcgi_request_buffering", ++"scgi_request_buffering", and "uwsgi_request_buffering" directives. ++ ++ ++ ++ ++ ++экспериментальное API для обработки тела запроса. ++ ++ ++request body filters experimental API. ++ ++ ++ ++ ++ ++проверка клиентских SSL-сертификатов в почтовом прокси-сервере.
++Спасибо Sven Peter, Franck Levionnois и Filipe Da Silva. ++
++ ++client SSL certificates support in mail proxy.
++Thanks to Sven Peter, Franck Levionnois, and Filipe Da Silva. ++
++
++ ++ ++ ++уменьшение времени запуска ++при использовании директивы "hash ... consistent" в блоке upstream.
++Спасибо Wai Keen Woon. ++
++ ++startup speedup ++when using the "hash ... consistent" directive in the upstream block.
++Thanks to Wai Keen Woon. ++
++
++ ++ ++ ++отладочное логгирование в кольцевой буфер в памяти. ++ ++ ++debug logging into a cyclic memory buffer. ++ ++ ++ ++ ++ ++в обработке хэш-таблиц.
++Спасибо Chris West. ++
++ ++in hash table handling.
++Thanks to Chris West. ++
++
++ ++ ++ ++в директиве proxy_cache_revalidate. ++ ++ ++in the "proxy_cache_revalidate" directive. ++ ++ ++ ++ ++ ++SSL-соединения могли зависать, если использовался отложенный accept ++или параметр proxy_protocol директивы listen.
++Спасибо James Hamlin. ++
++ ++SSL connections might hang if deferred accept ++or the "proxy_protocol" parameter of the "listen" directive were used.
++Thanks to James Hamlin. ++
++
++ ++ ++ ++переменная $upstream_response_time могла содержать неверное значение ++при использовании директивы image_filter. ++ ++ ++the $upstream_response_time variable might contain a wrong value ++if the "image_filter" directive was used. ++ ++ ++ ++ ++ ++в обработке целочисленных переполнений.
++Спасибо Régis Leroy. ++
++ ++in integer overflow handling.
++Thanks to Régis Leroy. ++
++
++ ++ ++ ++при использовании LibreSSL было невозможно включить поддержку SSLv3. ++ ++ ++it was not possible to enable SSLv3 with LibreSSL. ++ ++ ++ ++ ++ ++при использовании LibreSSL в логах появлялись сообщения ++"ignoring stale global SSL error ... called a function you should not call". ++ ++ ++the "ignoring stale global SSL error ... called a function you should not call" ++alerts appeared in logs when using LibreSSL. ++ ++ ++ ++ ++ ++сертификаты, указанные в директивах ssl_client_certificate и ++ssl_trusted_certificate, использовались ++для автоматического построения цепочек сертификатов. ++ ++ ++certificates specified by the "ssl_client_certificate" and ++"ssl_trusted_certificate" directives were inadvertently used ++to automatically construct certificate chains. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++параметр use_temp_path директив proxy_cache_path, fastcgi_cache_path, ++scgi_cache_path и uwsgi_cache_path. ++ ++ ++the "use_temp_path" parameter of the "proxy_cache_path", "fastcgi_cache_path", ++"scgi_cache_path", and "uwsgi_cache_path" directives. ++ ++ ++ ++ ++ ++переменная $upstream_header_time. ++ ++ ++the $upstream_header_time variable. ++ ++ ++ ++ ++ ++теперь при переполнении диска nginx пытается писать error_log'и только ++раз в секунду. ++ ++ ++now on disk overflow nginx tries to write error logs once a second only. ++ ++ ++ ++ ++ ++директива try_files при тестировании каталогов ++не игнорировала обычные файлы.
++Спасибо Damien Tournoud. ++
++ ++the "try_files" directive did not ignore normal files ++while testing directories.
++Thanks to Damien Tournoud. ++
++
++ ++ ++ ++при использовании директивы sendfile на OS X ++возникали ошибки "sendfile() failed"; ++ошибка появилась в nginx 1.7.8. ++ ++ ++alerts "sendfile() failed" ++if the "sendfile" directive was used on OS X; ++the bug had appeared in 1.7.8. ++ ++ ++ ++ ++ ++в лог могли писаться сообщения "sem_post() failed". ++ ++ ++alerts "sem_post() failed" might appear in logs. ++ ++ ++ ++ ++ ++nginx не собирался с musl libc.
++Спасибо James Taylor. ++
++ ++nginx could not be built with musl libc.
++Thanks to James Taylor. ++
++
++ ++ ++ ++nginx не собирался на Tru64 UNIX.
++Спасибо Goetz T. Fischer. ++
++ ++nginx could not be built on Tru64 UNIX.
++Thanks to Goetz T. Fischer. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++директивы proxy_cache, fastcgi_cache, scgi_cache и uwsgi_cache ++поддерживают переменные. ++ ++ ++variables support in the "proxy_cache", "fastcgi_cache", "scgi_cache", ++and "uwsgi_cache" directives. ++ ++ ++ ++ ++ ++директива expires поддерживает переменные. ++ ++ ++variables support in the "expires" directive. ++ ++ ++ ++ ++ ++возможность загрузки секретных ключей с аппаратных устройств ++с помощью OpenSSL engines.
++Спасибо Дмитрию Пичулину. ++
++ ++loading of secret keys from hardware tokens ++with OpenSSL engines.
++Thanks to Dmitrii Pichulin. ++
++
++ ++ ++ ++директива autoindex_format. ++ ++ ++the "autoindex_format" directive. ++ ++ ++ ++ ++ ++ревалидация элементов кэша теперь используется только для ответов ++с кодами 200 и 206.
++Спасибо Piotr Sikora. ++
++ ++cache revalidation is now only used for responses ++with 200 and 206 status codes.
++Thanks to Piotr Sikora. ++
++
++ ++ ++ ++строка "TE" заголовка запроса клиента передавалась на бэкенд при проксировании. ++ ++ ++the "TE" client request header line was passed to backends while proxying. ++ ++ ++ ++ ++ ++директивы proxy_pass, fastcgi_pass, scgi_pass и uwsgi_pass ++могли неправильно работать внутри блоков if и limit_except. ++ ++ ++the "proxy_pass", "fastcgi_pass", "scgi_pass", and "uwsgi_pass" directives ++might not work correctly inside the "if" and "limit_except" blocks. ++ ++ ++ ++ ++ ++директива proxy_store с параметром "on" игнорировалась, ++если на предыдущем уровне использовалась директива proxy_store ++с явно заданным путём к файлам. ++ ++ ++the "proxy_store" directive with the "on" parameter was ignored ++if the "proxy_store" directive with an explicitly specified file path ++was used on a previous level. ++ ++ ++ ++ ++ ++nginx не собирался с BoringSSL.
++Спасибо Lukas Tribus. ++
++ ++nginx could not be built with BoringSSL.
++Thanks to Lukas Tribus. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++теперь строки "If-Modified-Since", "If-Range" и им подобные ++в заголовке запроса клиента передаются бэкенду при включённом кэшировании, ++если nginx заранее знает, что не будет кэшировать ответ ++(например, при использовании proxy_cache_min_uses). ++ ++ ++now the "If-Modified-Since", "If-Range", etc. ++client request header lines are passed to a backend while caching ++if nginx knows in advance that the response will not be cached ++(e.g., when using proxy_cache_min_uses). ++ ++ ++ ++ ++ ++теперь после истечения proxy_cache_lock_timeout ++nginx отправляет запрос на бэкенд без кэширования; ++новые директивы proxy_cache_lock_age, fastcgi_cache_lock_age, ++scgi_cache_lock_age и uwsgi_cache_lock_age позволяют указать, ++через какое время блокировка будет принудительно снята ++и будет сделана ещё одна попытка закэшировать ответ. ++ ++ ++now after proxy_cache_lock_timeout ++nginx sends a request to a backend with caching disabled; ++the new directives "proxy_cache_lock_age", "fastcgi_cache_lock_age", ++"scgi_cache_lock_age", and "uwsgi_cache_lock_age" specify a time ++after which the lock will be released ++and another attempt to cache a response will be made. ++ ++ ++ ++ ++ ++директива log_format теперь может использоваться только на уровне http. ++ ++ ++the "log_format" directive can now be used only at http level. ++ ++ ++ ++ ++ ++директивы proxy_ssl_certificate, proxy_ssl_certificate_key, ++proxy_ssl_password_file, uwsgi_ssl_certificate, ++uwsgi_ssl_certificate_key и uwsgi_ssl_password_file.
++Спасибо Piotr Sikora. ++
++ ++the "proxy_ssl_certificate", "proxy_ssl_certificate_key", ++"proxy_ssl_password_file", "uwsgi_ssl_certificate", ++"uwsgi_ssl_certificate_key", and "uwsgi_ssl_password_file" directives.
++Thanks to Piotr Sikora. ++
++
++ ++ ++ ++теперь с помощью X-Accel-Redirect ++можно перейти в именованный location.
++Спасибо Toshikuni Fukaya. ++
++ ++it is now possible to switch to a named location ++using "X-Accel-Redirect".
++Thanks to Toshikuni Fukaya. ++
++
++ ++ ++ ++теперь директива tcp_nodelay работает для SPDY-соединений. ++ ++ ++now the "tcp_nodelay" directive works with SPDY connections. ++ ++ ++ ++ ++ ++новые директивы в скриптах подсветки синтаксиса для vim.
++Спасибо Peter Wu. ++
++ ++new directives in vim syntax highliting scripts.
++Thanks to Peter Wu. ++
++
++ ++ ++ ++nginx игнорировал значение "s-maxage" ++в строке "Cache-Control" в заголовке ответа бэкенда.
++Спасибо Piotr Sikora. ++
++ ++nginx ignored the "s-maxage" value ++in the "Cache-Control" backend response header line.
++Thanks to Piotr Sikora. ++
++
++ ++ ++ ++в модуле ngx_http_spdy_module.
++Спасибо Piotr Sikora. ++
++ ++in the ngx_http_spdy_module.
++Thanks to Piotr Sikora. ++
++
++ ++ ++ ++в директиве ssl_password_file ++при использовании OpenSSL 0.9.8zc, 1.0.0o, 1.0.1j. ++ ++ ++in the "ssl_password_file" directive ++when using OpenSSL 0.9.8zc, 1.0.0o, 1.0.1j. ++ ++ ++ ++ ++ ++при использовании директивы post_action ++в лог писались сообщения "header already sent"; ++ошибка появилась в nginx 1.5.4. ++ ++ ++alerts "header already sent" appeared in logs ++if the "post_action" directive was used; ++the bug had appeared in 1.5.4. ++ ++ ++ ++ ++ ++при использовании директивы "postpone_output 0" с SSI-подзапросами ++в лог могли писаться сообщения "the http output chain is empty". ++ ++ ++alerts "the http output chain is empty" might appear in logs ++if the "postpone_output 0" directive was used with SSI includes. ++ ++ ++ ++ ++ ++в директиве proxy_cache_lock при использовании SSI-подзапросов.
++Спасибо Yichun Zhang. ++
++ ++in the "proxy_cache_lock" directive with SSI subrequests.
++Thanks to Yichun Zhang. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++теперь nginx учитывает при кэшировании строку "Vary" ++в заголовке ответа бэкенда. ++ ++ ++now nginx takes into account the "Vary" ++header line in a backend response while caching. ++ ++ ++ ++ ++ ++директивы proxy_force_ranges, fastcgi_force_ranges, ++scgi_force_ranges и uwsgi_force_ranges. ++ ++ ++the "proxy_force_ranges", "fastcgi_force_ranges", ++"scgi_force_ranges", and "uwsgi_force_ranges" directives. ++ ++ ++ ++ ++ ++директивы proxy_limit_rate, fastcgi_limit_rate, ++scgi_limit_rate и uwsgi_limit_rate. ++ ++ ++the "proxy_limit_rate", "fastcgi_limit_rate", ++"scgi_limit_rate", and "uwsgi_limit_rate" directives. ++ ++ ++ ++ ++ ++параметр Vary директив proxy_ignore_headers, fastcgi_ignore_headers, ++scgi_ignore_headers и uwsgi_ignore_headers. ++ ++ ++the "Vary" parameter of the "proxy_ignore_headers", "fastcgi_ignore_headers", ++"scgi_ignore_headers", and "uwsgi_ignore_headers" directives. ++ ++ ++ ++ ++ ++последняя часть ответа, полученного от бэкенда ++при небуферизированном проксировании, ++могла не отправляться клиенту, ++если использовались директивы gzip или gunzip. ++ ++ ++the last part of a response received from a backend ++with unbufferred proxy ++might not be sent to a client ++if "gzip" or "gunzip" directives were used. ++ ++ ++ ++ ++ ++в директиве proxy_cache_revalidate.
++Спасибо Piotr Sikora. ++
++ ++in the "proxy_cache_revalidate" directive.
++Thanks to Piotr Sikora. ++
++
++ ++ ++ ++в обработке ошибок.
++Спасибо Yichun Zhang и Даниилу Бондареву. ++
++ ++in error handling.
++Thanks to Yichun Zhang and Daniil Bondarev. ++
++
++ ++ ++ ++в директивах ++proxy_next_upstream_tries и proxy_next_upstream_timeout.
++Спасибо Feng Gu. ++
++ ++in the "proxy_next_upstream_tries" and "proxy_next_upstream_timeout" ++directives.
++Thanks to Feng Gu. ++
++
++ ++ ++ ++nginx/Windows не собирался с MinGW-w64 gcc.
++Спасибо Kouhei Sutou. ++
++ ++nginx/Windows could not be built with MinGW-w64 gcc.
++Thanks to Kouhei Sutou. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++устаревшая директива limit_zone больше не поддерживается. ++ ++ ++the deprecated "limit_zone" directive is not supported anymore. ++ ++ ++ ++ ++ ++в директивах limit_conn_zone и limit_req_zone теперь можно использовать ++комбинации нескольких переменных. ++ ++ ++the "limit_conn_zone" and "limit_req_zone" directives now can be used ++with combinations of multiple variables. ++ ++ ++ ++ ++ ++при повторной отправке FastCGI-запроса на бэкенд ++тело запроса могло передаваться неправильно. ++ ++ ++request body might be transmitted incorrectly ++when retrying a FastCGI request to the next upstream server. ++ ++ ++ ++ ++ ++в логгировании в syslog. ++ ++ ++in logging to syslog. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++при использовании общего для нескольких блоков server ++разделяемого кэша SSL-сессий или общего ключа для шифрования ++TLS session tickets было возможно повторно использовать ++SSL-сессию в контексте другого блока server (CVE-2014-3616).
++Спасибо Antoine Delignat-Lavaud. ++
++ ++it was possible to reuse SSL sessions in unrelated contexts ++if a shared SSL session cache or the same TLS session ticket key ++was used for multiple "server" blocks (CVE-2014-3616).
++Thanks to Antoine Delignat-Lavaud. ++
++
++ ++ ++ ++директиву stub_status теперь можно указывать без параметров. ++ ++ ++now the "stub_status" directive does not require a parameter. ++ ++ ++ ++ ++ ++параметр always директивы add_header. ++ ++ ++the "always" parameter of the "add_header" directive. ++ ++ ++ ++ ++ ++директивы ++proxy_next_upstream_tries, proxy_next_upstream_timeout, ++fastcgi_next_upstream_tries, fastcgi_next_upstream_timeout, ++memcached_next_upstream_tries, memcached_next_upstream_timeout, ++scgi_next_upstream_tries, scgi_next_upstream_timeout, ++uwsgi_next_upstream_tries и uwsgi_next_upstream_timeout. ++ ++ ++the ++"proxy_next_upstream_tries", "proxy_next_upstream_timeout", ++"fastcgi_next_upstream_tries", "fastcgi_next_upstream_timeout", ++"memcached_next_upstream_tries", "memcached_next_upstream_timeout", ++"scgi_next_upstream_tries", "scgi_next_upstream_timeout", ++"uwsgi_next_upstream_tries", and "uwsgi_next_upstream_timeout" ++directives. ++ ++ ++ ++ ++ ++в параметре if директивы access_log. ++ ++ ++in the "if" parameter of the "access_log" directive. ++ ++ ++ ++ ++ ++в модуле ngx_http_perl_module.
++Спасибо Piotr Sikora. ++
++ ++in the ngx_http_perl_module.
++Thanks to Piotr Sikora. ++
++
++ ++ ++ ++директива listen почтового прокси-сервера ++не позволяла указать более двух параметров. ++ ++ ++the "listen" directive of the mail proxy module ++did not allow to specify more than two parameters. ++ ++ ++ ++ ++ ++директива sub_filter не работала ++с заменяемой строкой из одного символа. ++ ++ ++the "sub_filter" directive did not work ++with a string to replace consisting of a single character. ++ ++ ++ ++ ++ ++запросы могли зависать, если использовался resolver ++и в процессе обращения к DNS-серверу происходил таймаут. ++ ++ ++requests might hang if resolver was used ++and a timeout occurred during a DNS request. ++ ++ ++ ++ ++ ++в модуле ngx_http_spdy_module при использовании совместно с AIO. ++ ++ ++in the ngx_http_spdy_module when using with AIO. ++ ++ ++ ++ ++ ++в рабочем процессе мог произойти segmentation fault, ++если с помощью директивы set изменялись переменные ++"$http_...", "$sent_http_..." или "$upstream_http_...". ++ ++ ++a segmentation fault might occur in a worker process ++if the "set" directive was used to change the "$http_...", ++"$sent_http_...", or "$upstream_http_..." variables. ++ ++ ++ ++ ++ ++в обработке ошибок выделения памяти.
++Спасибо Markus Linnala и Feng Gu. ++
++ ++in memory allocation error handling.
++Thanks to Markus Linnala and Feng Gu. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++pipelined-команды не отбрасывались ++после команды STARTTLS в SMTP прокси-сервере (CVE-2014-3556); ++ошибка появилась в 1.5.6.
++Спасибо Chris Boulton. ++
++ ++pipelined commands were not discarded ++after STARTTLS command in SMTP proxy (CVE-2014-3556); ++the bug had appeared in 1.5.6.
++Thanks to Chris Boulton. ++
++
++ ++ ++ ++экранирование символов в URI теперь использует ++шестнадцатеричные цифры в верхнем регистре.
++Спасибо Piotr Sikora. ++
++ ++URI escaping now uses ++uppercase hexadecimal digits.
++Thanks to Piotr Sikora. ++
++
++ ++ ++ ++теперь nginx можно собрать с BoringSSL и LibreSSL.
++Спасибо Piotr Sikora. ++
++ ++now nginx can be build with BoringSSL and LibreSSL.
++Thanks to Piotr Sikora. ++
++
++ ++ ++ ++запросы могли зависать, если использовался resolver ++и DNS-сервер возвращал некорректный ответ; ++ошибка появилась в 1.5.8. ++ ++ ++requests might hang if resolver was used ++and a DNS server returned a malformed response; ++the bug had appeared in 1.5.8. ++ ++ ++ ++ ++ ++в модуле ngx_http_spdy_module.
++Спасибо Piotr Sikora. ++
++ ++in the ngx_http_spdy_module.
++Thanks to Piotr Sikora. ++
++
++ ++ ++ ++переменная $uri могла содержать мусор ++при возврате ошибок с кодом 400.
++Спасибо Сергею Боброву. ++
++ ++the $uri variable might contain garbage ++when returning errors with code 400.
++Thanks to Sergey Bobrov. ++
++
++ ++ ++ ++в обработке ошибок в директиве proxy_store ++и в модуле ngx_http_dav_module.
++Спасибо Feng Gu. ++
++ ++in error handling in the "proxy_store" directive ++and the ngx_http_dav_module.
++Thanks to Feng Gu. ++
++
++ ++ ++ ++при логгировании ошибок в syslog мог происходить segmentation fault; ++ошибка появилась в 1.7.1. ++ ++ ++a segmentation fault might occur if logging of errors to syslog was used; ++the bug had appeared in 1.7.1. ++ ++ ++ ++ ++ ++переменные $geoip_latitude, $geoip_longitude, $geoip_dma_code ++и $geoip_area_code могли не работать.
++Спасибо Yichun Zhang. ++
++ ++the $geoip_latitude, $geoip_longitude, $geoip_dma_code, ++and $geoip_area_code variables might not work.
++Thanks to Yichun Zhang. ++
++
++ ++ ++ ++в обработке ошибок выделения памяти.
++Спасибо Tatsuhiko Kubo и Piotr Sikora. ++
++ ++in memory allocation error handling.
++Thanks to Tatsuhiko Kubo and Piotr Sikora. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++weak entity tags теперь не удаляются при изменениях ответа, ++а strong entity tags преобразуются в weak. ++ ++ ++weak entity tags are now preserved on response modifications, ++and strong ones are changed to weak. ++ ++ ++ ++ ++ ++ревалидация элементов кэша теперь, если это возможно, ++использует заголовок If-None-Match. ++ ++ ++cache revalidation now uses If-None-Match header ++if possible. ++ ++ ++ ++ ++ ++директива ssl_password_file. ++ ++ ++the "ssl_password_file" directive. ++ ++ ++ ++ ++ ++при возврате ответа из кэша ++заголовок запроса If-None-Match игнорировался, ++если в ответе не было заголовка Last-Modified. ++ ++ ++the If-None-Match request header line was ignored ++if there was no Last-Modified header ++in a response returned from cache. ++ ++ ++ ++ ++ ++сообщения "peer closed connection in SSL handshake" ++при соединении с бэкендами логгировались на уровне info вместо error. ++ ++ ++"peer closed connection in SSL handshake" messages ++were logged at "info" level instead of "error" while connecting to backends. ++ ++ ++ ++ ++ ++в модуле ngx_http_dav_module в nginx/Windows. ++ ++ ++in the ngx_http_dav_module module in nginx/Windows. ++ ++ ++ ++ ++ ++SPDY-соединения могли неожиданно закрываться, ++если использовалось кэширование. ++ ++ ++SPDY connections might be closed prematurely ++if caching was used. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директива hash в блоке upstream. ++ ++ ++the "hash" directive inside the "upstream" block. ++ ++ ++ ++ ++ ++дефрагментация свободных блоков разделяемой памяти.
++Спасибо Wandenberg Peixoto и Yichun Zhang. ++
++ ++defragmentation of free shared memory blocks.
++Thanks to Wandenberg Peixoto and Yichun Zhang. ++
++
++ ++ ++ ++в рабочем процессе мог произойти segmentation fault, ++если использовалось значение access_log по умолчанию; ++ошибка появилась в 1.7.0.
++Спасибо Piotr Sikora. ++
++ ++a segmentation fault might occur in a worker process ++if the default value of the "access_log" directive was used; ++the bug had appeared in 1.7.0.
++Thanks to Piotr Sikora. ++
++
++ ++ ++ ++завершающий слэш ошибочно удалялся ++из последнего параметра директивы try_files. ++ ++ ++trailing slash was mistakenly removed ++from the last parameter of the "try_files" directive. ++ ++ ++ ++ ++ ++nginx мог не собираться на OS X. ++ ++ ++nginx could not be built on OS X in some cases. ++ ++ ++ ++ ++ ++в модуле ngx_http_spdy_module. ++ ++ ++in the ngx_http_spdy_module. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++переменные "$upstream_cookie_...". ++ ++ ++the "$upstream_cookie_..." variables. ++ ++ ++ ++ ++ ++переменная $ssl_client_fingerprint. ++ ++ ++the $ssl_client_fingerprint variable. ++ ++ ++ ++ ++ ++директивы error_log и access_log теперь поддерживают логгирование в syslog. ++ ++ ++the "error_log" and "access_log" directives now support logging to syslog. ++ ++ ++ ++ ++ ++почтовый прокси-сервер теперь логгирует порт клиента при соединении. ++ ++ ++the mail proxy now logs client port on connect. ++ ++ ++ ++ ++ ++утечки памяти при использовании директивы "ssl_stapling".
++Спасибо Filipe da Silva. ++
++ ++memory leak if the "ssl_stapling" directive was used.
++Thanks to Filipe da Silva. ++
++
++ ++ ++ ++директива alias внутри location'а, заданного регулярным выражением, ++работала неправильно, если использовались директивы if или limit_except. ++ ++ ++the "alias" directive used inside a location given by a regular expression ++worked incorrectly if the "if" or "limit_except" directives were used. ++ ++ ++ ++ ++ ++директива charset не ставила кодировку для сжатых ответов бэкендов. ++ ++ ++the "charset" directive did not set a charset to encoded backend responses. ++ ++ ++ ++ ++ ++директива proxy_pass без URI могла использовать оригинальный запрос ++после установки переменной $args.
++Спасибо Yichun Zhang. ++
++ ++a "proxy_pass" directive without URI part might use original request ++after the $args variable was set.
++Thanks to Yichun Zhang. ++
++
++ ++ ++ ++в работе параметра none директивы smtp_auth; ++ошибка появилась в 1.5.6.
++Спасибо Святославу Никольскому. ++
++ ++in the "none" parameter in the "smtp_auth" directive; ++the bug had appeared in 1.5.6.
++Thanks to Svyatoslav Nikolsky. ++
++
++ ++ ++ ++при совместном использовании sub_filter и SSI ++ответы могли передаваться неверно. ++ ++ ++if sub_filter and SSI were used together, ++then responses might be transferred incorrectly. ++ ++ ++ ++ ++ ++nginx не собирался с параметром --with-file-aio на Linux/aarch64. ++ ++ ++nginx could not be built with the --with-file-aio option on Linux/aarch64. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++проверка SSL-сертификатов бэкендов. ++ ++ ++backend SSL certificate verification. ++ ++ ++ ++ ++ ++поддержка SNI при работе с бэкендами по SSL. ++ ++ ++support for SNI while working with SSL backends. ++ ++ ++ ++ ++ ++переменная $ssl_server_name. ++ ++ ++the $ssl_server_name variable. ++ ++ ++ ++ ++ ++параметр if директивы access_log. ++ ++ ++the "if" parameter of the "access_log" directive. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++улучшена обработка хэш-таблиц; ++в директивах variables_hash_max_size и types_hash_bucket_size ++значения по умолчанию изменены на 1024 и 64 соответственно. ++ ++ ++improved hash table handling; ++the default values of the "variables_hash_max_size" and ++"types_hash_bucket_size" were changed to 1024 and 64 respectively. ++ ++ ++ ++ ++ ++модуль ngx_http_mp4_module теперь понимает аргумент end. ++ ++ ++the ngx_http_mp4_module now supports the "end" argument. ++ ++ ++ ++ ++ ++поддержка byte ranges модулем ngx_http_mp4_module и при сохранении ++ответов в кэш. ++ ++ ++byte ranges support in the ngx_http_mp4_module and while saving responses ++to cache. ++ ++ ++ ++ ++ ++теперь nginx не пишет в лог сообщения "ngx_slab_alloc() failed: no memory" ++при использовании разделяемой памяти в ssl_session_cache ++и в модуле ngx_http_limit_req_module. ++ ++ ++alerts "ngx_slab_alloc() failed: no memory" no longer logged ++when using shared memory in the "ssl_session_cache" directive ++and in the ngx_http_limit_req_module. ++ ++ ++ ++ ++ ++директива underscores_in_headers ++не разрешала подчёркивание в первом символе заголовка.
++Спасибо Piotr Sikora. ++
++ ++the "underscores_in_headers" directive ++did not allow underscore as a first character of a header.
++Thanks to Piotr Sikora. ++
++
++ ++ ++ ++cache manager мог нагружать процессор при выходе в nginx/Windows. ++ ++ ++cache manager might hog CPU on exit in nginx/Windows. ++ ++ ++ ++ ++ ++при использовании ssl_session_cache с параметром shared ++рабочий процесс nginx/Windows завершался аварийно. ++ ++ ++nginx/Windows terminated abnormally ++if the "ssl_session_cache" directive was used with the "shared" parameter. ++ ++ ++ ++ ++ ++в модуле ngx_http_spdy_module. ++ ++ ++in the ngx_http_spdy_module. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++при обработке специально созданного запроса модулем ngx_http_spdy_module ++могло происходить переполнение буфера в рабочем процессе, ++что потенциально могло приводить к выполнению произвольного кода ++(CVE-2014-0133).
++Спасибо Lucas Molas из Programa STIC, Fundación Dr. Manuel ++Sadosky, Buenos Aires, Argentina. ++
++ ++a heap memory buffer overflow might occur in a worker process ++while handling a specially crafted request by ngx_http_spdy_module, ++potentially resulting in arbitrary code execution ++(CVE-2014-0133).
++Thanks to Lucas Molas, researcher at Programa STIC, Fundación Dr. Manuel ++Sadosky, Buenos Aires, Argentina. ++
++
++ ++ ++ ++параметр proxy_protocol в директивах listen и real_ip_header, ++переменная $proxy_protocol_addr. ++ ++ ++the "proxy_protocol" parameters of the "listen" and "real_ip_header" directives, ++the $proxy_protocol_addr variable. ++ ++ ++ ++ ++ ++в директиве fastcgi_next_upstream.
++Спасибо Lucas Molas. ++
++ ++in the "fastcgi_next_upstream" directive.
++Thanks to Lucas Molas. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++при обработке специально созданного запроса модулем ngx_http_spdy_module ++на 32-битных платформах могла повреждаться память рабочего процесса, ++что потенциально могло приводить к выполнению произвольного кода ++(CVE-2014-0088); ++ошибка появилась в 1.5.10.
++Спасибо Lucas Molas из Programa STIC, Fundación Dr. Manuel ++Sadosky, Buenos Aires, Argentina. ++
++ ++memory corruption might occur in a worker process on 32-bit platforms ++while handling a specially crafted request by ngx_http_spdy_module, ++potentially resulting in arbitrary code execution (CVE-2014-0088); ++the bug had appeared in 1.5.10.
++Thanks to Lucas Molas, researcher at Programa STIC, Fundación Dr. Manuel ++Sadosky, Buenos Aires, Argentina. ++
++
++ ++ ++ ++переменная $ssl_session_reused. ++ ++ ++the $ssl_session_reused variable. ++ ++ ++ ++ ++ ++директива client_max_body_size могла не работать ++при чтении тела запроса с использованием chunked transfer encoding; ++ошибка появилась в 1.3.9.
++Спасибо Lucas Molas. ++
++ ++the "client_max_body_size" directive might not work ++when reading a request body using chunked transfer encoding; ++the bug had appeared in 1.3.9.
++Thanks to Lucas Molas. ++
++
++ ++ ++ ++при проксировании WebSocket-соединений ++в рабочем процессе мог произойти segmentation fault. ++ ++ ++a segmentation fault might occur in a worker process ++when proxying WebSocket connections. ++ ++ ++ ++ ++ ++в рабочем процессе мог произойти segmentation fault, ++если использовался модуль ngx_http_spdy_module на 32-битных платформах; ++ошибка появилась в 1.5.10. ++ ++ ++a segmentation fault might occur in a worker process ++if the ngx_http_spdy_module was used on 32-bit platforms; ++the bug had appeared in 1.5.10. ++ ++ ++ ++ ++ ++значение переменной $upstream_status могло быть неверным, ++если использовались директивы proxy_cache_use_stale ++или proxy_cache_revalidate.
++Спасибо Piotr Sikora. ++
++ ++the $upstream_status variable might contain wrong data ++if the "proxy_cache_use_stale" or "proxy_cache_revalidate" directives ++were used.
++Thanks to Piotr Sikora. ++
++
++ ++ ++ ++в рабочем процессе мог произойти segmentation fault, ++если ошибки с кодом 400 с помощью директивы error_page ++перенаправлялись в именованный location. ++ ++ ++a segmentation fault might occur in a worker process ++if errors with code 400 were redirected to a named location ++using the "error_page" directive. ++ ++ ++ ++ ++ ++nginx/Windows не собирался с Visual Studio 2013. ++ ++ ++nginx/Windows could not be built with Visual Studio 2013. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++модуль ngx_http_spdy_module теперь использует протокол SPDY 3.1.
++Спасибо Automattic и MaxCDN за спонсирование разработки. ++
++ ++the ngx_http_spdy_module now uses SPDY 3.1 protocol.
++Thanks to Automattic and MaxCDN for sponsoring this work. ++
++
++ ++ ++ ++модуль ngx_http_mp4_module теперь пропускает дорожки, ++имеющие меньшую длину, чем запрошенная перемотка. ++ ++ ++the ngx_http_mp4_module now skips tracks ++too short for a seek requested. ++ ++ ++ ++ ++ ++в рабочем процессе мог произойти segmentation fault, ++если переменная $ssl_session_id использовалась при логгировании; ++ошибка появилась в 1.5.9. ++ ++ ++a segmentation fault might occur in a worker process ++if the $ssl_session_id variable was used in logs; ++the bug had appeared in 1.5.9. ++ ++ ++ ++ ++ ++переменные $date_local и $date_gmt использовали неверный формат ++вне модуля ngx_http_ssi_filter_module. ++ ++ ++the $date_local and $date_gmt variables used wrong format ++outside of the ngx_http_ssi_filter_module. ++ ++ ++ ++ ++ ++клиентские соединения могли сразу закрываться, ++если использовался отложенный accept; ++ошибка появилась в 1.3.15. ++ ++ ++client connections might be immediately closed ++if deferred accept was used; ++the bug had appeared in 1.3.15. ++ ++ ++ ++ ++ ++сообщения "getsockopt(TCP_FASTOPEN) ... failed" записывались в лог ++в процессе обновления исполняемого файла на Linux; ++ошибка появилась в 1.5.8.
++Спасибо Piotr Sikora. ++
++ ++alerts "getsockopt(TCP_FASTOPEN) ... failed" appeared in logs ++during binary upgrade on Linux; ++the bug had appeared in 1.5.8.
++Thanks to Piotr Sikora. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++теперь в заголовке X-Accel-Redirect nginx ожидает закодированный URI. ++ ++ ++now nginx expects escaped URIs in "X-Accel-Redirect" headers. ++ ++ ++ ++ ++ ++директива ssl_buffer_size. ++ ++ ++the "ssl_buffer_size" directive. ++ ++ ++ ++ ++ ++директиву limit_rate теперь можно использовать для ++ограничения скорости передачи ответов клиенту в SPDY-соединениях. ++ ++ ++the "limit_rate" directive can now be used to ++rate limit responses sent in SPDY connections. ++ ++ ++ ++ ++ ++директива spdy_chunk_size. ++ ++ ++the "spdy_chunk_size" directive. ++ ++ ++ ++ ++ ++директива ssl_session_tickets.
++Спасибо Dirkjan Bussink. ++
++ ++the "ssl_session_tickets" directive.
++Thanks to Dirkjan Bussink. ++
++
++ ++ ++ ++переменная $ssl_session_id содержала всю сессию в сериализованном виде ++вместо её идентификатора.
++Спасибо Ivan Ristić. ++
++ ++the $ssl_session_id variable contained full session serialized ++instead of just a session id.
++Thanks to Ivan Ristić. ++
++
++ ++ ++ ++nginx неправильно обрабатывал закодированный символ "?" в команде SSI include. ++ ++ ++nginx incorrectly handled escaped "?" character in the "include" SSI command. ++ ++ ++ ++ ++ ++модуль ngx_http_dav_module не раскодировал целевой URI при ++обработке методов COPY и MOVE. ++ ++ ++the ngx_http_dav_module did not unescape destination URI ++of the COPY and MOVE methods. ++ ++ ++ ++ ++ ++resolver не понимал доменные имена с точкой в конце. ++Спасибо Yichun Zhang. ++ ++ ++resolver did not understand domain names with a trailing dot. ++Thanks to Yichun Zhang. ++ ++ ++ ++ ++ ++при проксировании в логах могли появляться сообщения "zero size buf in output"; ++ошибка появилась в 1.3.9. ++ ++ ++alerts "zero size buf in output" might appear in logs while proxying; ++the bug had appeared in 1.3.9. ++ ++ ++ ++ ++ ++в рабочем процессе мог произойти segmentation fault, ++если использовался модуль ngx_http_spdy_module. ++ ++ ++a segmentation fault might occur in a worker process ++if the ngx_http_spdy_module was used. ++ ++ ++ ++ ++ ++при использовании методов обработки соединений select, poll и /dev/poll ++проксируемые WebSocket-соединения могли зависать сразу после открытия. ++ ++ ++proxied WebSocket connections might hang right after handshake ++if the select, poll, or /dev/poll methods were used. ++ ++ ++ ++ ++ ++директива xclient почтового прокси-сервера ++некорректно передавала IPv6-адреса. ++ ++ ++the "xclient" directive of the mail proxy module ++incorrectly handled IPv6 client addresses. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++теперь resolver поддерживает IPv6. ++ ++ ++IPv6 support in resolver. ++ ++ ++ ++ ++ ++директива listen поддерживает параметр fastopen.
++Спасибо Mathew Rodley. ++
++ ++the "listen" directive supports the "fastopen" parameter.
++Thanks to Mathew Rodley. ++
++
++ ++ ++ ++поддержка SSL в модуле ngx_http_uwsgi_module.
++Спасибо Roberto De Ioris. ++
++ ++SSL support in the ngx_http_uwsgi_module.
++Thanks to Roberto De Ioris. ++
++
++ ++ ++ ++скрипты подсветки синтаксиса для vim добавлены в contrib.
++Спасибо Evan Miller. ++
++ ++vim syntax highlighting scripts were added to contrib.
++Thanks to Evan Miller. ++
++
++ ++ ++ ++при чтении тела запроса с использованием chunked transfer encoding ++по SSL-соединению мог произойти таймаут. ++ ++ ++a timeout might occur while reading client request body ++in an SSL connection using chunked transfer encoding. ++ ++ ++ ++ ++ ++директива master_process работала неправильно в nginx/Windows. ++ ++ ++the "master_process" directive did not work correctly in nginx/Windows. ++ ++ ++ ++ ++ ++параметр setfib директивы listen мог не работать. ++ ++ ++the "setfib" parameter of the "listen" directive might not work. ++ ++ ++ ++ ++ ++в модуле ngx_http_spdy_module. ++ ++ ++in the ngx_http_spdy_module. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++символ, следующий за незакодированным пробелом в строке запроса, ++обрабатывался неправильно (CVE-2013-4547); ++ошибка появилась в 0.8.41.
++Спасибо Ivan Fratric из Google Security Team. ++
++ ++a character following an unescaped space in a request line ++was handled incorrectly (CVE-2013-4547); ++the bug had appeared in 0.8.41.
++Thanks to Ivan Fratric of the Google Security Team. ++
++
++ ++ ++ ++уровень логгирования ошибок auth_basic об отсутствии пароля ++понижен с уровня error до info. ++ ++ ++a logging level of auth_basic errors about no user/password provided ++has been lowered from "error" to "info". ++ ++ ++ ++ ++ ++директивы proxy_cache_revalidate, fastcgi_cache_revalidate, ++scgi_cache_revalidate и uwsgi_cache_revalidate. ++ ++ ++the "proxy_cache_revalidate", "fastcgi_cache_revalidate", ++"scgi_cache_revalidate", and "uwsgi_cache_revalidate" directives. ++ ++ ++ ++ ++ ++директива ssl_session_ticket_key.
++Спасибо Piotr Sikora. ++
++ ++the "ssl_session_ticket_key" directive.
++Thanks to Piotr Sikora. ++
++
++ ++ ++ ++директива "add_header Cache-Control ''" ++добавляла строку заголовка ответа "Cache-Control" с пустым значением. ++ ++ ++the directive "add_header Cache-Control ''" ++added a "Cache-Control" response header line with an empty value. ++ ++ ++ ++ ++ ++директива "satisfy any" могла вернуть ошибку 403 вместо 401 ++при использовании директив auth_request и auth_basic.
++Спасибо Jan Marc Hoffmann. ++
++ ++the "satisfy any" directive might return 403 error instead of 401 ++if auth_request and auth_basic directives were used.
++Thanks to Jan Marc Hoffmann. ++
++
++ ++ ++ ++параметры accept_filter и deferred директивы listen игнорировались ++для listen-сокетов, создаваемых в процессе обновления исполняемого файла.
++Спасибо Piotr Sikora. ++
++ ++the "accept_filter" and "deferred" parameters of the "listen" directive ++were ignored for listen sockets created during binary upgrade.
++Thanks to Piotr Sikora. ++
++
++ ++ ++ ++часть данных, полученных от бэкенда при небуферизированном проксировании, ++могла не отправляться клиенту сразу, ++если использовались директивы gzip или gunzip.
++Спасибо Yichun Zhang. ++
++ ++some data received from a backend with unbufferred proxy ++might not be sent to a client immediately ++if "gzip" or "gunzip" directives were used.
++Thanks to Yichun Zhang. ++
++
++ ++ ++ ++в обработке ошибок в модуле ngx_http_gunzip_filter_module. ++ ++ ++in error handling in ngx_http_gunzip_filter_module. ++ ++ ++ ++ ++ ++ответы могли зависать, ++если использовался модуль ngx_http_spdy_module ++и директива auth_request. ++ ++ ++responses might hang ++if the ngx_http_spdy_module was used ++with the "auth_request" directive. ++ ++ ++ ++ ++ ++утечки памяти в nginx/Windows. ++ ++ ++memory leak in nginx/Windows. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++директива fastcgi_buffering. ++ ++ ++the "fastcgi_buffering" directive. ++ ++ ++ ++ ++ ++директивы proxy_ssl_protocols и proxy_ssl_ciphers.
++Спасибо Piotr Sikora. ++
++ ++the "proxy_ssl_protocols" and "proxy_ssl_ciphers" directives.
++Thanks to Piotr Sikora. ++
++
++ ++ ++ ++оптимизация SSL handshake при использовании длинных цепочек сертификатов. ++ ++ ++optimization of SSL handshakes when using long certificate chains. ++ ++ ++ ++ ++ ++почтовый прокси-сервер поддерживает SMTP pipelining. ++ ++ ++the mail proxy supports SMTP pipelining. ++ ++ ++ ++ ++ ++в модуле ngx_http_auth_basic_module ++при использовании метода шифрования паролей "$apr1$".
++Спасибо Markus Linnala. ++
++ ++in the ngx_http_auth_basic_module ++when using "$apr1$" password encryption method.
++Thanks to Markus Linnala. ++
++
++ ++ ++ ++на MacOSX, Cygwin и nginx/Windows ++для обработки запроса мог использоваться неверный location, ++если для задания location'ов использовались символы разных регистров. ++ ++ ++in MacOSX, Cygwin, and nginx/Windows ++incorrect location might be used to process a request ++if locations were given using characters in different cases. ++ ++ ++ ++ ++ ++автоматическое перенаправление с добавлением завершающего слэша ++для проксированных location'ов могло не работать. ++ ++ ++automatic redirect with appended trailing slash ++for proxied locations might not work. ++ ++ ++ ++ ++ ++в почтовом прокси-сервере. ++ ++ ++in the mail proxy server. ++ ++ ++ ++ ++ ++в модуле ngx_http_spdy_module. ++ ++ ++in the ngx_http_spdy_module. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++теперь nginx по умолчанию использует HTTP/1.0, ++если точно определить протокол не удалось. ++ ++ ++now nginx assumes HTTP/1.0 by default ++if it is not able to detect protocol reliably. ++ ++ ++ ++ ++ ++директива disable_symlinks теперь использует O_PATH на Linux. ++ ++ ++the "disable_symlinks" directive now uses O_PATH on Linux. ++ ++ ++ ++ ++ ++для определения того, что клиент закрыл соединение, ++при использовании метода epoll ++теперь используются события EPOLLRDHUP. ++ ++ ++now nginx uses EPOLLRDHUP events ++to detect premature connection close by clients ++if the "epoll" method is used. ++ ++ ++ ++ ++ ++в директиве valid_referers при использовании параметра server_names. ++ ++ ++in the "valid_referers" directive if the "server_names" parameter was used. ++ ++ ++ ++ ++ ++переменная $request_time не работала в nginx/Windows. ++ ++ ++the $request_time variable did not work in nginx/Windows. ++ ++ ++ ++ ++ ++в директиве image_filter.
++Спасибо Lanshun Zhou. ++
++ ++in the "image_filter" directive.
++Thanks to Lanshun Zhou. ++
++
++ ++ ++ ++совместимость с OpenSSL 1.0.1f.
++Спасибо Piotr Sikora. ++
++ ++OpenSSL 1.0.1f compatibility.
++Thanks to Piotr Sikora. ++
++
++ ++ ++
++ ++ ++ ++ ++ ++ ++MIME-тип для расширения js изменён на "application/javascript"; ++значение по умолчанию директивы charset_types изменено соответственно. ++ ++ ++the "js" extension MIME type has been changed to "application/javascript"; ++default value of the "charset_types" directive was changed accordingly. ++ ++ ++ ++ ++ ++теперь директива image_filter с параметром size ++возвращает ответ с MIME-типом "application/json". ++ ++ ++now the "image_filter" directive with the "size" parameter ++returns responses with the "application/json" MIME type. ++ ++ ++ ++ ++ ++модуль ngx_http_auth_request_module. ++ ++ ++the ngx_http_auth_request_module. ++ ++ ++ ++ ++ ++на старте или во время переконфигурации мог произойти segmentation fault, ++если использовалась директива try_files с пустым параметром. ++ ++ ++a segmentation fault might occur on start or during reconfiguration ++if the "try_files" directive was used with an empty parameter. ++ ++ ++ ++ ++ ++утечки памяти при использовании в директивах root и auth_basic_user_file ++относительных путей, заданных с помощью переменных. ++ ++ ++memory leak if relative paths were specified using variables ++in the "root" or "auth_basic_user_file" directives. ++ ++ ++ ++ ++ ++директива valid_referers неправильно выполняла регулярные выражения, ++если заголовок Referer начинался с "https://".
++Спасибо Liangbin Li. ++
++ ++the "valid_referers" directive incorrectly executed regular expressions ++if a "Referer" header started with "https://".
++Thanks to Liangbin Li. ++
++
++ ++ ++ ++ответы могли зависать, если использовались подзапросы и при обработке подзапроса ++происходила ошибка во время SSL handshake с бэкендом.
++Спасибо Aviram Cohen. ++
++ ++responses might hang if subrequests were used ++and an SSL handshake error happened during subrequest processing.
++Thanks to Aviram Cohen. ++
++
++ ++ ++ ++в модуле ngx_http_autoindex_module. ++ ++ ++in the ngx_http_autoindex_module. ++ ++ ++ ++ ++ ++в модуле ngx_http_spdy_module. ++ ++ ++in the ngx_http_spdy_module. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++Изменение во внутреннем API: ++теперь при небуферизированной работе с бэкендами ++u->length по умолчанию устанавливается в -1. ++ ++ ++Change in internal API: ++now u->length defaults to -1 ++if working with backends in unbuffered mode. ++ ++ ++ ++ ++ ++теперь при получении неполного ответа от бэкенда ++nginx отправляет полученную часть ответа, ++после чего закрывает соединение с клиентом. ++ ++ ++now after receiving an incomplete response from a backend server ++nginx tries to send an available part of the response to a client, ++and then closes client connection. ++ ++ ++ ++ ++ ++в рабочем процессе мог произойти segmentation fault, ++если использовался модуль ngx_http_spdy_module ++и директива client_body_in_file_only. ++ ++ ++a segmentation fault might occur in a worker process ++if the ngx_http_spdy_module was used ++with the "client_body_in_file_only" directive. ++ ++ ++ ++ ++ ++параметр so_keepalive директивы listen ++мог работать некорректно на DragonFlyBSD.
++Спасибо Sepherosa Ziehau. ++
++ ++the "so_keepalive" parameter of the "listen" directive ++might be handled incorrectly on DragonFlyBSD.
++Thanks to Sepherosa Ziehau. ++
++
++ ++ ++ ++в модуле ngx_http_xslt_filter_module. ++ ++ ++in the ngx_http_xslt_filter_module. ++ ++ ++ ++ ++ ++в модуле ngx_http_sub_filter_module. ++ ++ ++in the ngx_http_sub_filter_module. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++теперь можно использовать несколько директив error_log. ++ ++ ++now several "error_log" directives can be used. ++ ++ ++ ++ ++ ++метод $r->header_in() встроенного перла не возвращал значения строк ++"Cookie" и "X-Forwarded-For" из заголовка запроса; ++ошибка появилась в 1.3.14. ++ ++ ++the $r->header_in() embedded perl method did not return value of the ++"Cookie" and "X-Forwarded-For" request header lines; ++the bug had appeared in 1.3.14. ++ ++ ++ ++ ++ ++в модуле ngx_http_spdy_module.
++Спасибо Jim Radford. ++
++ ++in the ngx_http_spdy_module.
++Thanks to Jim Radford. ++
++
++ ++ ++ ++nginx не собирался на Linux при использовании x32 ABI.
++Спасибо Сергею Иванцову. ++
++ ++nginx could not be built on Linux with x32 ABI.
++Thanks to Serguei Ivantsov. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++директивы ssi_last_modified, sub_filter_last_modified и ++xslt_last_modified.
++Спасибо Алексею Колпакову. ++
++ ++the "ssi_last_modified", "sub_filter_last_modified", and ++"xslt_last_modified" directives.
++Thanks to Alexey Kolpakov. ++
++
++ ++ ++ ++параметр http_403 в директивах proxy_next_upstream, fastcgi_next_upstream, ++scgi_next_upstream и uwsgi_next_upstream. ++ ++ ++the "http_403" parameter of the "proxy_next_upstream", "fastcgi_next_upstream", ++"scgi_next_upstream", and "uwsgi_next_upstream" directives. ++ ++ ++ ++ ++ ++директивы allow и deny теперь поддерживают unix domain сокеты. ++ ++ ++the "allow" and "deny" directives now support unix domain sockets. ++ ++ ++ ++ ++ ++nginx не собирался с модулем ngx_mail_ssl_module, ++но без модуля ngx_http_ssl_module; ++ошибка появилась в 1.3.14. ++ ++ ++nginx could not be built with the ngx_mail_ssl_module, ++but without ngx_http_ssl_module; ++the bug had appeared in 1.3.14. ++ ++ ++ ++ ++ ++в директиве proxy_set_body.
++Спасибо Lanshun Zhou. ++
++ ++in the "proxy_set_body" directive.
++Thanks to Lanshun Zhou. ++
++
++ ++ ++ ++в директиве lingering_time.
++Спасибо Lanshun Zhou. ++
++ ++in the "lingering_time" directive.
++Thanks to Lanshun Zhou. ++
++
++ ++ ++ ++параметр fail_timeout директивы server ++в блоке upstream мог не работать, ++если использовался параметр max_fails; ++ошибка появилась в 1.3.0. ++ ++ ++the "fail_timeout" parameter of the "server" directive ++in the "upstream" context might not work ++if "max_fails" parameter was used; ++the bug had appeared in 1.3.0. ++ ++ ++ ++ ++ ++в рабочем процессе мог произойти segmentation fault, ++если использовалась директива ssl_stapling.
++Спасибо Piotr Sikora. ++
++ ++a segmentation fault might occur in a worker process ++if the "ssl_stapling" directive was used.
++Thanks to Piotr Sikora. ++
++
++ ++ ++ ++в почтовом прокси-сервере.
++Спасибо Filipe Da Silva. ++
++ ++in the mail proxy server.
++Thanks to Filipe Da Silva. ++
++
++ ++ ++ ++nginx/Windows мог перестать принимать соединения, ++если использовалось несколько рабочих процессов. ++ ++ ++nginx/Windows might stop accepting connections ++if several worker processes were used. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++при обработке специально созданного запроса ++мог перезаписываться стек рабочего процесса, ++что могло приводить к выполнению произвольного кода (CVE-2013-2028); ++ошибка появилась в 1.3.9.
++Спасибо Greg MacManus, iSIGHT Partners Labs. ++
++ ++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. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++nginx не собирался с модулем ngx_http_perl_module, ++если использовался параметр --with-openssl; ++ошибка появилась в 1.3.16. ++ ++ ++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. ++ ++ ++ ++ ++ ++в работе с телом запроса из модуля ngx_http_perl_module; ++ошибка появилась в 1.3.9. ++ ++ ++in a request body handling in the ngx_http_perl_module; ++the bug had appeared in 1.3.9. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++в рабочем процессе мог произойти segmentation fault, ++если использовались подзапросы; ++ошибка появилась в 1.3.9. ++ ++ ++a segmentation fault might occur in a worker process ++if subrequests were used; ++the bug had appeared in 1.3.9. ++ ++ ++ ++ ++ ++директива tcp_nodelay вызывала ошибку ++при проксировании WebSocket-соединений в unix domain сокет. ++ ++ ++the "tcp_nodelay" directive caused an error ++if a WebSocket connection was proxied into a unix domain socket. ++ ++ ++ ++ ++ ++переменная $upstream_response_length возвращала значение "0", ++если не использовалась буферизация.
++Спасибо Piotr Sikora. ++
++ ++the $upstream_response_length variable has an incorrect value "0" ++if buffering was not used.
++Thanks to Piotr Sikora. ++
++
++ ++ ++ ++в методах обработки соединений eventport и /dev/poll. ++ ++ ++in the eventport and /dev/poll methods. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++открытие и закрытие соединения без отправки в нём каких-либо данных ++больше не записывается в access_log с кодом ошибки 400. ++ ++ ++opening and closing a connection without sending any data in it ++is no longer logged to access_log with error code 400. ++ ++ ++ ++ ++ ++модуль ngx_http_spdy_module.
++Спасибо Automattic за спонсирование разработки. ++
++ ++the ngx_http_spdy_module.
++Thanks to Automattic for sponsoring this work. ++
++
++ ++ ++ ++директивы limit_req_status и limit_conn_status.
++Спасибо Nick Marden. ++
++ ++the "limit_req_status" and "limit_conn_status" directives.
++Thanks to Nick Marden. ++
++
++ ++ ++ ++директива image_filter_interlace.
++Спасибо Ивану Боброву. ++
++ ++the "image_filter_interlace" directive.
++Thanks to Ian Babrou. ++
++
++ ++ ++ ++переменная $connections_waiting в модуле ngx_http_stub_status_module. ++ ++ ++$connections_waiting variable in the ngx_http_stub_status_module. ++ ++ ++ ++ ++ ++теперь почтовый прокси-сервер поддерживает IPv6-бэкенды. ++ ++ ++the mail proxy module now supports IPv6 backends. ++ ++ ++ ++ ++ ++при повторной отправке запроса на бэкенд ++тело запроса могло передаваться неправильно; ++ошибка появилась в 1.3.9.
++Спасибо Piotr Sikora. ++
++ ++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. ++
++
++ ++ ++ ++в директиве client_body_in_file_only; ++ошибка появилась в 1.3.9. ++ ++ ++in the "client_body_in_file_only" directive; ++the bug had appeared in 1.3.9. ++ ++ ++ ++ ++ ++ответы могли зависать, ++если использовались подзапросы ++и при обработке подзапроса происходила DNS-ошибка.
++Спасибо Lanshun Zhou. ++
++ ++responses might hang ++if subrequests were used ++and a DNS error happened during subrequest processing.
++Thanks to Lanshun Zhou. ++
++
++ ++ ++ ++в процедуре учёта использования бэкендов. ++ ++ ++in backend usage accounting. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++переменные $connections_active, $connections_reading и $connections_writing ++в модуле ngx_http_stub_status_module. ++ ++ ++$connections_active, $connections_reading, and $connections_writing variables ++in the ngx_http_stub_status_module. ++ ++ ++ ++ ++ ++поддержка WebSocket-соединений ++в модулях ngx_http_uwsgi_module и ngx_http_scgi_module. ++ ++ ++support of WebSocket connections ++in the ngx_http_uwsgi_module and ngx_http_scgi_module. ++ ++ ++ ++ ++ ++в обработке виртуальных серверов при использовании SNI. ++ ++ ++in virtual servers handling with SNI. ++ ++ ++ ++ ++ ++при использовании директивы "ssl_session_cache shared" ++новые сессии могли не сохраняться, ++если заканчивалось место в разделяемой памяти.
++Спасибо Piotr Sikora. ++
++ ++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. ++
++
++ ++ ++ ++несколько заголовков X-Forwarded-For обрабатывались неправильно.
++Спасибо Neal Poole за спонсирование разработки. ++
++ ++multiple X-Forwarded-For headers were handled incorrectly.
++Thanks to Neal Poole for sponsoring this work. ++
++
++ ++ ++ ++в модуле ngx_http_mp4_module.
++Спасибо Gernot Vormayr. ++
++ ++in the ngx_http_mp4_module.
++Thanks to Gernot Vormayr. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++теперь для сборки по умолчанию используется компилятор с именем "cc". ++ ++ ++a compiler with name "cc" is now used by default. ++ ++ ++ ++ ++ ++поддержка проксирования WebSocket-соединений.
++Спасибо Apcera и CloudBees за спонсирование разработки. ++
++ ++support for proxying of WebSocket connections.
++Thanks to Apcera and CloudBees for sponsoring this work. ++
++
++ ++ ++ ++директива auth_basic_user_file поддерживает шифрование паролей ++методом "{SHA}".
++Спасибо Louis Opter. ++
++ ++the "auth_basic_user_file" directive supports "{SHA}" ++password encryption method.
++Thanks to Louis Opter. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++директивы proxy_bind, fastcgi_bind, memcached_bind, scgi_bind и uwsgi_bind ++поддерживают переменные. ++ ++ ++variables support in the "proxy_bind", "fastcgi_bind", "memcached_bind", ++"scgi_bind", and "uwsgi_bind" directives. ++ ++ ++ ++ ++ ++переменные $pipe, $request_length, $time_iso8601 и $time_local ++теперь можно использовать не только в директиве log_format.
++Спасибо Kiril Kalchev. ++
++ ++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. ++ ++
++ ++ ++ ++поддержка IPv6 в модуле ngx_http_geoip_module.
++Спасибо Gregor Kališnik. ++
++ ++IPv6 support in the ngx_http_geoip_module.
++Thanks to Gregor Kališnik. ++
++
++ ++ ++ ++директива proxy_method работала неверно, если была указана на уровне http. ++ ++ ++in the "proxy_method" directive. ++ ++ ++ ++ ++ ++в рабочем процессе мог произойти segmentation fault, ++если использовался resolver и метод poll. ++ ++ ++a segmentation fault might occur in a worker process ++if resolver was used with the poll method. ++ ++ ++ ++ ++ ++nginx мог нагружать процессор во время SSL handshake с бэкендом ++при использовании методов обработки соединений select, poll и /dev/poll. ++ ++ ++nginx might hog CPU during SSL handshake with a backend ++if the select, poll, or /dev/poll methods were used. ++ ++ ++ ++ ++ ++ошибка "[crit] SSL_write() failed (SSL:)". ++ ++ ++the "[crit] SSL_write() failed (SSL:)" error. ++ ++ ++ ++ ++ ++в директиве client_body_in_file_only; ++ошибка появилась в 1.3.9. ++ ++ ++in the "client_body_in_file_only" directive; ++the bug had appeared in 1.3.9. ++ ++ ++ ++ ++ ++в директиве fastcgi_keep_conn. ++ ++ ++in the "fastcgi_keep_conn" directive. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++при записи в лог мог происходить segmentation fault; ++ошибка появилась в 1.3.10. ++ ++ ++a segmentation fault might occur if logging was used; ++the bug had appeared in 1.3.10. ++ ++ ++ ++ ++ ++директива proxy_pass не работала с IP-адресами ++без явного указания порта; ++ошибка появилась в 1.3.10. ++ ++ ++the "proxy_pass" directive did not work with IP addresses ++without port specified; ++the bug had appeared in 1.3.10. ++ ++ ++ ++ ++ ++на старте или во время переконфигурации происходил segmentation fault, ++если директива keepalive была указана несколько раз ++в одном блоке upstream. ++ ++ ++a segmentation fault occurred on start or during reconfiguration ++if the "keepalive" directive was specified more than once ++in a single upstream block. ++ ++ ++ ++ ++ ++параметр default директивы geo не определял значение по умолчанию ++для IPv6-адресов. ++ ++ ++parameter "default" of the "geo" directive did not set default value ++for IPv6 addresses. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++для указанных в конфигурационном файле доменных имён теперь ++используются не только IPv4, но и IPv6 адреса. ++ ++ ++domain names specified in configuration file ++are now resolved to IPv6 addresses as well as IPv4 ones. ++ ++ ++ ++ ++ ++теперь при использовании директивы include с маской на Unix-системах ++включаемые файлы сортируются в алфавитном порядке. ++ ++ ++now if the "include" directive with mask is used on Unix systems, ++included files are sorted in alphabetical order. ++ ++ ++ ++ ++ ++директива add_header добавляет строки в ответы с кодом 201. ++ ++ ++the "add_header" directive adds headers to 201 responses. ++ ++ ++ ++ ++ ++директива geo теперь поддерживает IPv6 адреса в формате CIDR. ++ ++ ++the "geo" directive now supports IPv6 addresses in CIDR notation. ++ ++ ++ ++ ++ ++параметры flush и gzip в директиве access_log. ++ ++ ++the "flush" and "gzip" parameters of the "access_log" directive. ++ ++ ++ ++ ++ ++директива auth_basic поддерживает переменные. ++ ++ ++variables support in the "auth_basic" directive. ++ ++ ++ ++ ++ ++nginx в некоторых случаях не собирался с модулем ngx_http_perl_module. ++ ++ ++nginx could not be built with the ngx_http_perl_module in some cases. ++ ++ ++ ++ ++ ++в рабочем процессе мог произойти segmentation fault, ++если использовался модуль ngx_http_xslt_module. ++ ++ ++a segmentation fault might occur in a worker process ++if the ngx_http_xslt_module was used. ++ ++ ++ ++ ++ ++nginx мог не собираться на MacOSX.
++Спасибо Piotr Sikora. ++
++ ++nginx could not be built on MacOSX in some cases.
++Thanks to Piotr Sikora. ++
++
++ ++ ++ ++при использовании директивы limit_rate с большими значениями скорости ++на 32-битных системах ответ мог возвращаться не целиком.
++Спасибо Алексею Антропову. ++
++ ++the "limit_rate" directive with high rates ++might result in truncated responses on 32-bit platforms.
++Thanks to Alexey Antropov. ++
++
++ ++ ++ ++в рабочем процессе мог произойти segmentation fault, ++если использовалась директива if.
++Спасибо Piotr Sikora. ++
++ ++a segmentation fault might occur in a worker process ++if the "if" directive was used.
++Thanks to Piotr Sikora. ++
++
++ ++ ++ ++ответ "100 Continue" выдавался ++вместе с ответом "413 Request Entity Too Large". ++ ++ ++a "100 Continue" response was issued ++with "413 Request Entity Too Large" responses. ++ ++ ++ ++ ++ ++директивы image_filter, image_filter_jpeg_quality и image_filter_sharpen ++могли наследоваться некорректно.
++Спасибо Ивану Боброву. ++
++ ++the "image_filter", "image_filter_jpeg_quality" ++and "image_filter_sharpen" directives ++might be inherited incorrectly.
++Thanks to Ian Babrou. ++
++
++ ++ ++ ++при использовании директивы auth_basic под Linux ++могли возникать ошибки "crypt_r() failed". ++ ++ ++"crypt_r() failed" errors might appear ++if the "auth_basic" directive was used on Linux. ++ ++ ++ ++ ++ ++в обработке backup-серверов.
++Спасибо Thomas Chen. ++
++ ++in backup servers handling.
++Thanks to Thomas Chen. ++
++
++ ++ ++ ++при проксировании HEAD-запросов мог возвращаться некорректный ответ, ++если использовалась директива gzip. ++ ++ ++proxied HEAD requests might return incorrect response ++if the "gzip" directive was used. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++поддержка chunked transfer encoding при получении тела запроса. ++ ++ ++support for chunked transfer encoding while reading client request body. ++ ++ ++ ++ ++ ++переменные $request_time и $msec ++теперь можно использовать не только в директиве log_format. ++ ++ ++the $request_time and $msec variables ++can now be used not only in the "log_format" directive. ++ ++ ++ ++ ++ ++cache manager и cache loader могли не запускаться, ++если использовалось более 512 listen-сокетов. ++ ++ ++cache manager and cache loader processes might not be able to start ++if more than 512 listen sockets were used. ++ ++ ++ ++ ++ ++в модуле ngx_http_dav_module. ++ ++ ++in the ngx_http_dav_module. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++параметр optional_no_ca директивы ssl_verify_client.
++Спасибо Михаилу Казанцеву и Eric O'Connor. ++
++ ++the "optional_no_ca" parameter of the "ssl_verify_client" directive.
++Thanks to Mike Kazantsev and Eric O'Connor. ++
++
++ ++ ++ ++переменные $bytes_sent, $connection и $connection_requests ++теперь можно использовать не только в директиве log_format.
++Спасибо Benjamin Grössing. ++
++ ++the $bytes_sent, $connection, and $connection_requests variables ++can now be used not only in the "log_format" directive.
++Thanks to Benjamin Grössing. ++
++
++ ++ ++ ++параметр auto директивы worker_processes. ++ ++ ++the "auto" parameter of the "worker_processes" directive. ++ ++ ++ ++ ++ ++сообщения "cache file ... has md5 collision". ++ ++ ++"cache file ... has md5 collision" alert. ++ ++ ++ ++ ++ ++в модуле ngx_http_gunzip_filter_module. ++ ++ ++in the ngx_http_gunzip_filter_module. ++ ++ ++ ++ ++ ++в директиве ssl_stapling. ++ ++ ++in the "ssl_stapling" directive. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++поддержка OCSP stapling.
++Спасибо Comodo, DigiCert и GlobalSign за спонсирование разработки. ++
++ ++OCSP stapling support.
++Thanks to Comodo, DigiCert and GlobalSign for sponsoring this work. ++
++
++ ++ ++ ++директива ssl_trusted_certificate. ++ ++ ++the "ssl_trusted_certificate" directive. ++ ++ ++ ++ ++ ++теперь resolver случайным образом меняет порядок ++возвращаемых закэшированных адресов.
++Спасибо Антону Жулину. ++
++ ++resolver now randomly rotates addresses ++returned from cache.
++Thanks to Anton Jouline. ++
++
++ ++ ++ ++совместимость с OpenSSL 0.9.7. ++ ++ ++OpenSSL 0.9.7 compatibility. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++модуль ngx_http_gunzip_filter_module. ++ ++ ++the ngx_http_gunzip_filter_module. ++ ++ ++ ++ ++ ++директива memcached_gzip_flag. ++ ++ ++the "memcached_gzip_flag" directive. ++ ++ ++ ++ ++ ++параметр always директивы gzip_static. ++ ++ ++the "always" parameter of the "gzip_static" directive. ++ ++ ++ ++ ++ ++в директиве "limit_req"; ++ошибка появилась в 1.1.14.
++Спасибо Charles Chen. ++
++ ++in the "limit_req" directive; ++the bug had appeared in 1.1.14.
++Thanks to Charles Chen. ++
++
++ ++ ++ ++nginx не собирался gcc 4.7 с оптимизацией -O2 ++если использовался параметр --with-ipv6. ++ ++ ++nginx could not be built by gcc 4.7 with -O2 optimization ++if the --with-ipv6 option was used. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++модуль ngx_http_mp4_module больше не отфильтровывает дорожки ++в форматах, отличных от H.264 и AAC. ++ ++ ++the ngx_http_mp4_module module no longer skips ++tracks in formats other than H.264 and AAC. ++ ++ ++ ++ ++ ++в рабочем процессе мог произойти segmentation fault, ++если в директиве map в качестве значений использовались переменные. ++ ++ ++a segmentation fault might occur in a worker process ++if the "map" directive was used with variables as values. ++ ++ ++ ++ ++ ++в рабочем процессе мог произойти segmentation fault ++при использовании директивы geo с параметром ranges, ++но без параметра default; ошибка появилась в 0.8.43.
++Спасибо Zhen Chen и Weibin Yao. ++
++ ++a segmentation fault might occur in a worker process ++if the "geo" directive was used with the "ranges" parameter ++but without the "default" parameter; the bug had appeared in 0.8.43.
++Thanks to Zhen Chen and Weibin Yao. ++
++
++ ++ ++ ++в обработке параметра командной строки -p. ++ ++ ++in the -p command-line parameter handling. ++ ++ ++ ++ ++ ++в почтовом прокси-сервере. ++ ++ ++in the mail proxy server. ++ ++ ++ ++ ++ ++незначительных потенциальных ошибок.
++Спасибо Coverity. ++
++ ++of minor potential bugs.
++Thanks to Coverity. ++
++
++ ++ ++ ++nginx/Windows не собирался с Visual Studio 2005 Express.
++Спасибо HAYASHI Kentaro. ++
++ ++nginx/Windows could not be built with Visual Studio 2005 Express.
++Thanks to HAYASHI Kentaro. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++теперь на слушающих IPv6-сокетах параметр ipv6only ++включён по умолчанию. ++ ++ ++the "ipv6only" parameter is now turned on by default for ++listening IPv6 sockets. ++ ++ ++ ++ ++ ++поддержка компилятора Clang. ++ ++ ++the Clang compiler support. ++ ++ ++ ++ ++ ++могли создаваться лишние слушающие сокеты.
++Спасибо Роману Одайскому. ++
++ ++extra listening sockets might be created.
++Thanks to Roman Odaisky. ++
++
++ ++ ++ ++nginx/Windows мог нагружать процессор, если при запуске рабочего процесса ++происходила ошибка.
++Спасибо Ricardo Villalobos Guevara. ++
++ ++nginx/Windows might hog CPU if a worker process failed to start.
++Thanks to Ricardo Villalobos Guevara. ++
++
++ ++ ++ ++директивы proxy_pass_header, fastcgi_pass_header, scgi_pass_header, ++uwsgi_pass_header, proxy_hide_header, fastcgi_hide_header, ++scgi_hide_header и uwsgi_hide_header ++могли наследоваться некорректно. ++ ++ ++the "proxy_pass_header", "fastcgi_pass_header", "scgi_pass_header", ++"uwsgi_pass_header", "proxy_hide_header", "fastcgi_hide_header", ++"scgi_hide_header", and "uwsgi_hide_header" directives ++might be inherited incorrectly. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++поддержка entity tags и директива etag. ++ ++ ++entity tags support and the "etag" directive. ++ ++ ++ ++ ++ ++при использовании директивы map с параметром hostnames ++не игнорировалась конечная точка в исходном значении. ++ ++ ++trailing dot in a source value was not ignored ++if the "map" directive was used with the "hostnames" parameter. ++ ++ ++ ++ ++ ++для обработки запроса мог использоваться неверный location, ++если переход в именованный location происходил ++после изменения URI с помощью директивы rewrite. ++ ++ ++incorrect location might be used to process a request ++if a URI was changed via a "rewrite" directive ++before an internal redirect to a named location. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++параметр single директивы keepalive теперь игнорируется. ++ ++ ++the "single" parameter of the "keepalive" directive is now ignored. ++ ++ ++ ++ ++ ++сжатие SSL теперь отключено ++в том числе при использовании OpenSSL старее 1.0.0. ++ ++ ++SSL compression is now disabled when using all versions of OpenSSL, ++including ones prior to 1.0.0. ++ ++ ++ ++ ++ ++директиву "ip_hash" теперь можно использовать для балансировки IPv6 клиентов. ++ ++ ++it is now possible to use the "ip_hash" directive to balance IPv6 clients. ++ ++ ++ ++ ++ ++переменную $status теперь можно использовать не только в директиве log_format. ++ ++ ++the $status variable can now be used not only in the "log_format" directive. ++ ++ ++ ++ ++ ++при завершении рабочего процесса мог произойти segmentation fault, ++если использовалась директива resolver. ++ ++ ++a segmentation fault might occur in a worker process on shutdown ++if the "resolver" directive was used. ++ ++ ++ ++ ++ ++в рабочем процессе мог произойти segmentation fault, ++если использовался модуль ngx_http_mp4_module. ++ ++ ++a segmentation fault might occur in a worker process ++if the ngx_http_mp4_module was used. ++ ++ ++ ++ ++ ++в модуле ngx_http_mp4_module. ++ ++ ++in the ngx_http_mp4_module. ++ ++ ++ ++ ++ ++в рабочем процессе мог произойти segmentation fault, ++если использовались конфликтующие имена серверов с масками. ++ ++ ++a segmentation fault might occur in a worker process ++if conflicting wildcard server names were used. ++ ++ ++ ++ ++ ++на платформе ARM nginx мог аварийно завершаться по сигналу SIGBUS. ++ ++ ++nginx might be terminated abnormally on a SIGBUS signal on ARM platform. ++ ++ ++ ++ ++ ++во время переконфигурации на HP-UX в лог ++записывался alert "sendmsg() failed (9: Bad file number)". ++ ++ ++an alert "sendmsg() failed (9: Bad file number)" on HP-UX ++while reconfiguration. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++теперь nginx/Windows игнорирует точку в конце компонента URI ++и не разрешает URI, содержащие последовательность ":$".
++Спасибо Владимиру Кочеткову, Positive Research Center. ++
++ ++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. ++
++
++ ++ ++ ++директивы proxy_pass, fastcgi_pass, scgi_pass, uwsgi_pass и ++директива server в блоке upstream ++теперь поддерживают IPv6-адреса. ++ ++ ++the "proxy_pass", "fastcgi_pass", "scgi_pass", "uwsgi_pass" directives, and ++the "server" directive inside the "upstream" block, ++now support IPv6 addresses. ++ ++ ++ ++ ++ ++в директиве resolver теперь можно указывать порт и ++задавать IPv6-адреса DNS-серверов. ++ ++ ++the "resolver" directive now supports IPv6 addresses and ++an optional port specification. ++ ++ ++ ++ ++ ++директива least_conn в блоке upstream. ++ ++ ++the "least_conn" directive inside the "upstream" block. ++ ++ ++ ++ ++ ++при использовании директивы ip_hash ++теперь можно задавать веса серверов. ++ ++ ++it is now possible to specify a weight for servers ++while using the "ip_hash" directive. ++ ++ ++ ++ ++ ++в рабочем процессе мог произойти segmentation fault, ++если использовалась директива image_filter; ++ошибка появилась в 1.3.0. ++ ++ ++a segmentation fault might occur in a worker process ++if the "image_filter" directive was used; ++the bug had appeared in 1.3.0. ++ ++ ++ ++ ++ ++nginx не собирался с модулем ngx_cpp_test_module; ++ошибка появилась в 1.1.12. ++ ++ ++nginx could not be built with ngx_cpp_test_module; ++the bug had appeared in 1.1.12. ++ ++ ++ ++ ++ ++доступ к переменным из SSI и встроенного перла мог не работать после ++переконфигурации.
++Спасибо Yichun Zhang. ++
++ ++access to variables from SSI and embedded perl module might not work after ++reconfiguration.
++Thanks to Yichun Zhang. ++
++
++ ++ ++ ++в модуле ngx_http_xslt_filter_module.
++Спасибо Kuramoto Eiji. ++
++ ++in the ngx_http_xslt_filter_module.
++Thanks to Kuramoto Eiji. ++
++
++ ++ ++ ++утечки памяти при использовании переменной $geoip_org.
++Спасибо Денису Латыпову. ++
++ ++memory leak if $geoip_org variable was used.
++Thanks to Denis F. Latypoff. ++
++
++ ++ ++ ++в директивах proxy_cookie_domain и proxy_cookie_path. ++ ++ ++in the "proxy_cookie_domain" and "proxy_cookie_path" directives. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++директива debug_connection теперь поддерживает IPv6-адреса ++и параметр "unix:". ++ ++ ++the "debug_connection" directive now supports IPv6 addresses ++and the "unix:" parameter. ++ ++ ++ ++ ++ ++директива set_real_ip_from и параметр proxy ++директивы geo теперь поддерживают IPv6-адреса. ++ ++ ++the "set_real_ip_from" directive and the "proxy" parameter ++of the "geo" directive now support IPv6 addresses. ++ ++ ++ ++ ++ ++директивы real_ip_recursive, geoip_proxy и geoip_proxy_recursive. ++ ++ ++the "real_ip_recursive", "geoip_proxy", and "geoip_proxy_recursive" directives. ++ ++ ++ ++ ++ ++параметр proxy_recursive директивы geo. ++ ++ ++the "proxy_recursive" parameter of the "geo" directive. ++ ++ ++ ++ ++ ++в рабочем процессе мог произойти segmentation fault, ++если использовалась директива resolver. ++ ++ ++a segmentation fault might occur in a worker process ++if the "resolver" directive was used. ++ ++ ++ ++ ++ ++в рабочем процессе мог произойти segmentation fault, ++если использовались директивы fastcgi_pass, scgi_pass или uwsgi_pass ++и бэкенд возвращал некорректный ответ. ++ ++ ++a segmentation fault might occur in a worker process ++if the "fastcgi_pass", "scgi_pass", or "uwsgi_pass" directives were used ++and backend returned incorrect response. ++ ++ ++ ++ ++ ++в рабочем процессе мог произойти segmentation fault, ++если использовалась директива rewrite и в новых аргументах запроса в строке ++замены использовались переменные. ++ ++ ++a segmentation fault might occur in a worker process ++if the "rewrite" directive was used and new request arguments ++in a replacement used variables. ++ ++ ++ ++ ++ ++nginx мог нагружать процессор, ++если было достигнуто ограничение на количество открытых файлов. ++ ++ ++nginx might hog CPU ++if the open file resource limit was reached. ++ ++ ++ ++ ++ ++при использовании директивы proxy_next_upstream с параметром http_404 ++nginx мог бесконечно перебирать бэкенды, если в блоке upstream был ++хотя бы один сервер с флагом backup. ++ ++ ++nginx might loop infinitely over backends ++if the "proxy_next_upstream" directive with the "http_404" parameter was used ++and there were backup servers specified in an upstream block. ++ ++ ++ ++ ++ ++при использовании директивы ip_hash ++установка параметра down директивы server ++могла приводить к ненужному перераспределению клиентов между бэкендами. ++ ++ ++adding the "down" parameter of the "server" directive ++might cause unneeded client redistribution among backend servers ++if the "ip_hash" directive was used. ++ ++ ++ ++ ++ ++утечки сокетов.
++Спасибо Yichun Zhang. ++
++ ++socket leak.
++Thanks to Yichun Zhang. ++
++
++ ++ ++ ++в модуле ngx_http_fastcgi_module. ++ ++ ++in the ngx_http_fastcgi_module. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++в рабочем процессе мог произойти segmentation fault, ++если использовалась директива try_files; ++ошибка появилась в 1.1.19. ++ ++ ++a segmentation fault might occur in a worker process ++if the "try_files" directive was used; ++the bug had appeared in 1.1.19. ++ ++ ++ ++ ++ ++ответ мог быть передан не полностью, ++если использовалось больше IOV_MAX буферов. ++ ++ ++response might be truncated ++if there were more than IOV_MAX buffers used. ++ ++ ++ ++ ++ ++в работе параметра crop директивы image_filter.
++Спасибо Maxim Bublis. ++
++ ++in the "crop" parameter of the "image_filter" directive.
++Thanks to Maxim Bublis. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++при обработке специально созданного mp4 файла модулем ngx_http_mp4_module ++могли перезаписываться области памяти рабочего процесса, что могло ++приводить к выполнению произвольного кода (CVE-2012-2089).
++Спасибо Matthew Daley. ++
++ ++specially crafted mp4 file might allow to overwrite ++memory locations in a worker process ++if the ngx_http_mp4_module was used, ++potentially resulting in arbitrary code execution (CVE-2012-2089).
++Thanks to Matthew Daley. ++
++
++ ++ ++ ++nginx/Windows мог завершаться аварийно.
++Спасибо Vincent Lee. ++
++ ++nginx/Windows might be terminated abnormally.
++Thanks to Vincent Lee. ++
++
++ ++ ++ ++nginx нагружал процессор, если все серверы в upstream'е были помечены ++флагом backup. ++ ++ ++nginx hogged CPU if all servers in an upstream were marked as "backup". ++ ++ ++ ++ ++ ++директивы allow и deny могли наследоваться некорректно, ++если в них использовались IPv6 адреса. ++ ++ ++the "allow" and "deny" directives might be inherited incorrectly ++if they were used with IPv6 addresses. ++ ++ ++ ++ ++ ++директивы modern_browser и ancient_browser ++могли наследоваться некорректно. ++ ++ ++the "modern_browser" and "ancient_browser" directives ++might be inherited incorrectly. ++ ++ ++ ++ ++ ++таймауты могли работать некорректно на Solaris/SPARC. ++ ++ ++timeouts might be handled incorrectly on Solaris/SPARC. ++ ++ ++ ++ ++ ++в модуле ngx_http_mp4_module. ++ ++ ++in the ngx_http_mp4_module. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++теперь keepalive соединения не запрещены для Safari по умолчанию. ++ ++ ++keepalive connections are no longer disabled for Safari by default. ++ ++ ++ ++ ++ ++переменная $connection_requests. ++ ++ ++the $connection_requests variable. ++ ++ ++ ++ ++ ++переменные $tcpinfo_rtt, $tcpinfo_rttvar, $tcpinfo_snd_cwnd и ++$tcpinfo_rcv_space. ++ ++ ++$tcpinfo_rtt, $tcpinfo_rttvar, $tcpinfo_snd_cwnd and ++$tcpinfo_rcv_space variables. ++ ++ ++ ++ ++ ++директива worker_cpu_affinity теперь работает на FreeBSD. ++ ++ ++the "worker_cpu_affinity" directive now works on FreeBSD. ++ ++ ++ ++ ++ ++директивы xslt_param и xslt_string_param.
++Спасибо Samuel Behan. ++
++ ++the "xslt_param" and "xslt_string_param" directives.
++Thanks to Samuel Behan. ++
++
++ ++ ++ ++в configure.
++Спасибо Piotr Sikora. ++
++ ++in configure tests.
++Thanks to Piotr Sikora. ++
++
++ ++ ++ ++в модуле ngx_http_xslt_filter_module. ++ ++ ++in the ngx_http_xslt_filter_module. ++ ++ ++ ++ ++ ++nginx не собирался на Debian GNU/Hurd. ++ ++ ++nginx could not be built on Debian GNU/Hurd. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++содержимое ранее освобождённой памяти могло быть отправлено клиенту, ++если бэкенд возвращал специально созданный ответ.
++Спасибо Matthew Daley. ++
++ ++content of previously freed memory might be sent to a client ++if backend returned specially crafted response.
++Thanks to Matthew Daley. ++
++
++ ++ ++ ++при использовании встроенного перла из SSI.
++Спасибо Matthew Daley. ++
++ ++in the embedded perl module if used from SSI.
++Thanks to Matthew Daley. ++
++
++ ++ ++ ++в модуле ngx_http_uwsgi_module. ++ ++ ++in the ngx_http_uwsgi_module. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++ограничение на количество одновременных подзапросов поднято до 200. ++ ++ ++the simultaneous subrequest limit has been raised to 200. ++ ++ ++ ++ ++ ++параметр from в директиве disable_symlinks. ++ ++ ++the "from" parameter of the "disable_symlinks" directive. ++ ++ ++ ++ ++ ++директивы return и error_page теперь могут использоваться для возврата ++перенаправлений с кодом 307. ++ ++ ++the "return" and "error_page" directives can now be used to return 307 ++redirections. ++ ++ ++ ++ ++ ++в рабочем процессе мог произойти segmentation fault, ++если использовалась директива resolver ++и на глобальном уровне не была задана директива error_log.
++Спасибо Роману Арутюняну. ++
++ ++a segmentation fault might occur in a worker process ++if the "resolver" directive was used ++and there was no "error_log" directive specified at global level.
++Thanks to Roman Arutyunyan. ++
++
++ ++ ++ ++в рабочем процессе мог произойти segmentation fault, ++если использовались директивы "proxy_http_version 1.1" или ++"fastcgi_keep_conn on". ++ ++ ++a segmentation fault might occur in a worker process ++if the "proxy_http_version 1.1" or "fastcgi_keep_conn on" directives ++were used. ++ ++ ++ ++ ++ ++утечек памяти.
++Спасибо Lanshun Zhou. ++
++ ++memory leaks.
++Thanks to Lanshun Zhou. ++
++
++ ++ ++ ++в директиве disable_symlinks. ++ ++ ++in the "disable_symlinks" directive. ++ ++ ++ ++ ++ ++при использовании ZFS размер кэша на диске мог считаться некорректно; ++ошибка появилась в 1.0.1. ++ ++ ++on ZFS filesystem disk cache size might be calculated incorrectly; ++the bug had appeared in 1.0.1. ++ ++ ++ ++ ++ ++nginx не собирался компилятором icc 12.1. ++ ++ ++nginx could not be built by the icc 12.1 compiler. ++ ++ ++ ++ ++ ++nginx не собирался gcc на Solaris; ++ошибка появилась в 1.1.15. ++ ++ ++nginx could not be built by gcc on Solaris; ++the bug had appeared in 1.1.15. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++директива disable_symlinks. ++ ++ ++the "disable_symlinks" directive. ++ ++ ++ ++ ++ ++директивы proxy_cookie_domain и proxy_cookie_path. ++ ++ ++the "proxy_cookie_domain" and "proxy_cookie_path" directives. ++ ++ ++ ++ ++ ++nginx мог некорректно сообщать об ошибке "upstream prematurely closed ++connection" вместо "upstream sent too big header".
++Спасибо Feibo Li. ++
++ ++nginx might log incorrect error "upstream prematurely closed connection" ++instead of correct "upstream sent too big header" one.
++Thanks to Feibo Li. ++
++
++ ++ ++ ++nginx не собирался с модулем ngx_http_perl_module, ++если использовался параметр --with-openssl. ++ ++ ++nginx could not be built with the ngx_http_perl_module ++if the --with-openssl option was used. ++ ++ ++ ++ ++ ++количество внутренних перенаправлений в именованные location'ы ++не ограничивалось. ++ ++ ++the number of internal redirects to named locations was not limited. ++ ++ ++ ++ ++ ++вызов $r->flush() несколько раз подряд мог приводить к ошибкам ++в модуле ngx_http_gzip_filter_module. ++ ++ ++calling $r->flush() multiple times might cause errors ++in the ngx_http_gzip_filter_module. ++ ++ ++ ++ ++ ++при использовании директивы proxy_store с SSI-подзапросами ++временные файлы могли не удаляться. ++ ++ ++temporary files might be not removed ++if the "proxy_store" directive was used with SSI includes. ++ ++ ++ ++ ++ ++в некоторых случаях некэшируемые переменные (такие, как $args) ++возвращали старое пустое закэшированное значение. ++ ++ ++in some cases non-cacheable variables (such as the $args variable) ++returned old empty cached value. ++ ++ ++ ++ ++ ++в рабочем процессе мог произойти segmentation fault, ++если одновременно создавалось слишком много SSI-подзапросов; ++ошибка появилась в 0.7.25. ++ ++ ++a segmentation fault might occur in a worker process ++if too many SSI subrequests were issued simultaneously; ++the bug had appeared in 0.7.25. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++теперь можно указать несколько ограничений limit_req одновременно. ++ ++ ++multiple "limit_req" limits may be used simultaneously. ++ ++ ++ ++ ++ ++в обработке ошибок при соединении с бэкендом.
++Спасибо Piotr Sikora. ++
++ ++in error handling while connecting to a backend.
++Thanks to Piotr Sikora. ++
++
++ ++ ++ ++в обработке ошибок при использовании AIO на FreeBSD. ++ ++ ++in AIO error handling on FreeBSD. ++ ++ ++ ++ ++ ++в инициализации библиотеки OpenSSL. ++ ++ ++in the OpenSSL library initialization. ++ ++ ++ ++ ++ ++директивы proxy_redirect могли наследоваться некорректно. ++ ++ ++the "proxy_redirect" directives might be inherited incorrectly. ++ ++ ++ ++ ++ ++утечки памяти при переконфигурации, если использовалась директива pcre_jit. ++ ++ ++memory leak during reconfiguration if the "pcre_jit" directive was used. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++параметры TLSv1.1 и TLSv1.2 в директиве ssl_protocols. ++ ++ ++the "TLSv1.1" and "TLSv1.2" parameters of the "ssl_protocols" directive. ++ ++ ++ ++ ++ ++параметры директивы limit_req наследовались некорректно; ++ошибка появилась в 1.1.12. ++ ++ ++the "limit_req" directive parameters were not inherited correctly; ++the bug had appeared in 1.1.12. ++ ++ ++ ++ ++ ++директива proxy_redirect некорректно обрабатывала заголовок Refresh ++при использовании регулярных выражений. ++ ++ ++the "proxy_redirect" directive incorrectly processed "Refresh" header ++if regular expression were used. ++ ++ ++ ++ ++ ++директива proxy_cache_use_stale с параметром error не возвращала ответ из ++кэша, если все бэкенды были признаны неработающими. ++ ++ ++the "proxy_cache_use_stale" directive with "error" parameter did not return ++answer from cache if there were no live upstreams. ++ ++ ++ ++ ++ ++директива worker_cpu_affinity могла не работать. ++ ++ ++the "worker_cpu_affinity" directive might not work. ++ ++ ++ ++ ++ ++nginx не собирался на Solaris; ++ошибка появилась в 1.1.12. ++ ++ ++nginx could not be built on Solaris; ++the bug had appeared in 1.1.12. ++ ++ ++ ++ ++ ++в модуле ngx_http_mp4_module. ++ ++ ++in the ngx_http_mp4_module. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++после перенаправления запроса с помощью директивы error_page ++директива proxy_pass без URI теперь использует изменённый URI.
++Спасибо Lanshun Zhou. ++
++ ++a "proxy_pass" directive without URI part now uses changed URI ++after redirection with the "error_page" directive.
++Thanks to Lanshun Zhou. ++
++
++ ++ ++ ++директивы proxy/fastcgi/scgi/uwsgi_cache_lock, ++proxy/fastcgi/scgi/uwsgi_cache_lock_timeout. ++ ++ ++the "proxy/fastcgi/scgi/uwsgi_cache_lock", ++"proxy/fastcgi/scgi/uwsgi_cache_lock_timeout" directives. ++ ++ ++ ++ ++ ++директива pcre_jit. ++ ++ ++the "pcre_jit" directive. ++ ++ ++ ++ ++ ++SSI команда if поддерживает выделения в регулярных выражениях. ++ ++ ++the "if" SSI command supports captures in regular expressions. ++ ++ ++ ++ ++ ++SSI команда if не работала внутри команды block. ++ ++ ++the "if" SSI command did not work inside the "block" command. ++ ++ ++ ++ ++ ++директивы limit_conn_log_level и limit_req_log_level могли не работать. ++ ++ ++the "limit_conn_log_level" and "limit_req_log_level" directives might not work. ++ ++ ++ ++ ++ ++директива limit_rate не позволяла передавать на полной скорости, ++даже если был указан очень большой лимит. ++ ++ ++the "limit_rate" directive did not allow to use full throughput, ++even if limit value was very high. ++ ++ ++ ++ ++ ++директива sendfile_max_chunk не работала, ++если использовалась директива limit_rate. ++ ++ ++the "sendfile_max_chunk" directive did not work, ++if the "limit_rate" directive was used. ++ ++ ++ ++ ++ ++если в директиве proxy_pass использовались переменные и не был указан URI, ++всегда использовался URI исходного запроса. ++ ++ ++a "proxy_pass" directive without URI part always used original request URI ++if variables were used. ++ ++ ++ ++ ++ ++после перенаправления запроса с помощью директивы try_files ++директива proxy_pass без URI могла использовать URI исходного запроса.
++Спасибо Lanshun Zhou. ++
++ ++a "proxy_pass" directive without URI part might use original request ++after redirection with the "try_files" directive.
++Thanks to Lanshun Zhou. ++
++
++ ++ ++ ++в модуле ngx_http_scgi_module. ++ ++ ++in the ngx_http_scgi_module. ++ ++ ++ ++ ++ ++в модуле ngx_http_mp4_module. ++ ++ ++in the ngx_http_mp4_module. ++ ++ ++ ++ ++ ++nginx не собирался на Solaris; ++ошибка появилась в 1.1.9. ++ ++ ++nginx could not be built on Solaris; ++the bug had appeared in 1.1.9. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++параметр so_keepalive в директиве listen.
++Спасибо Всеволоду Стахову. ++
++ ++the "so_keepalive" parameter of the "listen" directive.
++Thanks to Vsevolod Stakhov. ++
++
++ ++ ++ ++параметр if_not_empty в директивах fastcgi/scgi/uwsgi_param. ++ ++ ++the "if_not_empty" parameter of the "fastcgi/scgi/uwsgi_param" directives. ++ ++ ++ ++ ++ ++переменная $https. ++ ++ ++the $https variable. ++ ++ ++ ++ ++ ++директива proxy_redirect поддерживает переменные в первом параметре. ++ ++ ++the "proxy_redirect" directive supports variables in the first parameter. ++ ++ ++ ++ ++ ++директива proxy_redirect поддерживает регулярные выражения. ++ ++ ++the "proxy_redirect" directive supports regular expressions. ++ ++ ++ ++ ++ ++переменная $sent_http_cache_control могла содержать неверное значение при ++использовании директивы expires.
++Спасибо Yichun Zhang. ++
++ ++the $sent_http_cache_control variable might contain a wrong value if the ++"expires" directive was used.
++Thanks to Yichun Zhang. ++
++
++ ++ ++ ++директива read_ahead могла не работать при использовании совместно с ++try_files и open_file_cache. ++ ++ ++the "read_ahead" directive might not work combined with "try_files" ++and "open_file_cache". ++ ++ ++ ++ ++ ++если в параметре inactive директивы proxy_cache_path ++было указано малое время, ++в рабочем процессе мог произойти segmentation fault. ++ ++ ++a segmentation fault might occur in a worker process ++if small time was used in the "inactive" parameter of ++the "proxy_cache_path" directive. ++ ++ ++ ++ ++ ++ответы из кэша могли зависать. ++ ++ ++responses from cache might hang. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++при использовании AIO на Linux в рабочем процессе происходил segmentation fault; ++ошибка появилась в 1.1.9. ++ ++ ++a segmentation fault occurred in a worker process if AIO was used on Linux; ++the bug had appeared in 1.1.9. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++теперь двойные кавычки экранируется при выводе SSI-командой echo.
++Спасибо Зауру Абасмирзоеву. ++
++ ++now double quotes are encoded in an "echo" SSI-command output.
++Thanks to Zaur Abasmirzoev. ++
++
++ ++ ++ ++параметр valid в директиве resolver. По умолчанию теперь ++используется TTL, возвращённый DNS-сервером.
++Спасибо Кириллу Коринскому. ++
++ ++the "valid" parameter of the "resolver" directive. By default TTL ++returned by a DNS server is used.
++Thanks to Kirill A. Korinskiy. ++
++
++ ++ ++ ++nginx мог перестать отвечать, если рабочий процесс завершался аварийно. ++ ++ ++nginx might hang after a worker process abnormal termination. ++ ++ ++ ++ ++ ++в рабочем процессе мог произойти segmentation fault, ++если использовалось SNI; ++ошибка появилась в 1.1.2. ++ ++ ++a segmentation fault might occur in a worker process ++if SNI was used; ++the bug had appeared in 1.1.2. ++ ++ ++ ++ ++ ++в директиве keepalive_disable; ++ошибка появилась в 1.1.8.
++Спасибо Александру Усову. ++
++ ++in the "keepalive_disable" directive; ++the bug had appeared in 1.1.8.
++Thanks to Alexander Usov. ++
++
++ ++ ++ ++сигнал SIGWINCH переставал работать после первого обновления исполняемого ++файла; ++ошибка появилась в 1.1.1. ++ ++ ++SIGWINCH signal did not work after first binary upgrade; ++the bug had appeared in 1.1.1. ++ ++ ++ ++ ++ ++теперь ответы бэкендов, длина которых не соответствует заголовку ++Content-Length, не кэширутся. ++ ++ ++backend responses with length not matching "Content-Length" header line ++are no longer cached. ++ ++ ++ ++ ++ ++в директиве scgi_param при использовании составных параметров. ++ ++ ++in the "scgi_param" directive, if complex parameters were used. ++ ++ ++ ++ ++ ++в методе epoll.
++Спасибо Yichun Zhang. ++
++ ++in the "epoll" event method.
++Thanks to Yichun Zhang. ++
++
++ ++ ++ ++в модуле ngx_http_flv_module.
++Спасибо Piotr Sikora. ++
++ ++in the ngx_http_flv_module.
++Thanks to Piotr Sikora. ++
++
++ ++ ++ ++в модуле ngx_http_mp4_module. ++ ++ ++in the ngx_http_mp4_module. ++ ++ ++ ++ ++ ++теперь nginx понимает IPv6-адреса в строке запроса и в заголовке Host. ++ ++ ++IPv6 addresses are now handled properly in a request line and in a "Host" ++request header line. ++ ++ ++ ++ ++ ++директивы add_header и expires не работали для ответов с кодом 206, ++если запрос проксировался. ++ ++ ++"add_header" and "expires" directives did not work if a request was proxied ++and response status code was 206. ++ ++ ++ ++ ++ ++nginx не собирался на FreeBSD 10. ++ ++ ++nginx could not be built on FreeBSD 10. ++ ++ ++ ++ ++ ++nginx не собирался на AIX. ++ ++ ++nginx could not be built on AIX. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++модуль ngx_http_limit_zone_module переименован в ngx_http_limit_conn_module. ++ ++ ++the ngx_http_limit_zone_module was renamed to the ngx_http_limit_conn_module. ++ ++ ++ ++ ++ ++директива limit_zone заменена директивой limit_conn_zone с новым синтаксисом. ++ ++ ++the "limit_zone" directive was superseded by the "limit_conn_zone" directive ++with a new syntax. ++ ++ ++ ++ ++ ++поддержка ограничения по нескольким limit_conn на одном уровне. ++ ++ ++support for multiple "limit_conn" limits on the same level. ++ ++ ++ ++ ++ ++директива image_filter_sharpen. ++ ++ ++the "image_filter_sharpen" directive. ++ ++ ++ ++ ++ ++в рабочем процессе мог произойти segmentation fault, ++если resolver получил большой DNS-ответ.
++Спасибо Ben Hawkes. ++
++ ++a segmentation fault might occur in a worker process ++if resolver got a big DNS response.
++Thanks to Ben Hawkes. ++
++
++ ++ ++ ++в вычислении ключа для кэширования, ++если использовалась внутренняя реализация MD5; ++ошибка появилась в 1.0.4. ++ ++ ++in cache key calculation ++if internal MD5 implementation was used; ++the bug had appeared in 1.0.4. ++ ++ ++ ++ ++ ++строки "If-Modified-Since", "If-Range" и им подобные в заголовке запроса ++клиента могли передаваться бэкенду при кэшировании; или не передаваться при ++выключенном кэшировании, если кэширование было включено в другой части ++конфигурации. ++ ++ ++the "If-Modified-Since", "If-Range", etc. client request header lines ++might be passed to backend while caching; or not passed without caching ++if caching was enabled in another part of the configuration. ++ ++ ++ ++ ++ ++модуль ngx_http_mp4_module выдавал неверную строку "Content-Length" ++в заголовке ответа, использовался аргумент start.
++Спасибо Piotr Sikora. ++
++ ++the module ngx_http_mp4_module sent incorrect "Content-Length" response ++header line if the "start" argument was used.
++Thanks to Piotr Sikora. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++поддержка нескольких DNS серверов в директиве "resolver".
++Спасибо Кириллу Коринскому. ++
++ ++support of several DNS servers in the "resolver" directive.
++Thanks to Kirill A. Korinskiy. ++
++
++ ++ ++ ++на старте или во время переконфигурации происходил segmentation fault, ++если директива ssl использовалась на уровне http и не был указан ++ssl_certificate. ++ ++ ++a segmentation fault occurred on start or during reconfiguration ++if the "ssl" directive was used at http level and there was ++no "ssl_certificate" defined. ++ ++ ++ ++ ++ ++уменьшено потребление памяти при проксировании больших файлов, ++если они буферизировались на диск. ++ ++ ++reduced memory consumption while proxying big files ++if they were buffered to disk. ++ ++ ++ ++ ++ ++в рабочем процессе мог произойти segmentation fault, ++если использовалась директива "proxy_http_version 1.1". ++ ++ ++a segmentation fault might occur in a worker process ++if "proxy_http_version 1.1" directive was used. ++ ++ ++ ++ ++ ++в директиве "expires @time". ++ ++ ++in the "expires @time" directive. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++Изменение во внутреннем API: теперь при внутреннем редиректе ++в именованный location контексты модулей очищаются.
++По запросу Yichun Zhang. ++
++ ++Change in internal API: now module context data are cleared ++while internal redirect to named location.
++Requested by Yichun Zhang. ++
++
++ ++ ++ ++теперь если сервер, описанный в блоке upstream, был признан неработающим, ++то после истечения fail_timeout на него будет отправлен только один запрос; ++сервер будет считаться работающим, если успешно ответит на этот запрос. ++ ++ ++if a server in an upstream failed, only one request will be sent to it ++after fail_timeout; the server will be considered alive if it will ++successfully respond to the request. ++ ++ ++ ++ ++ ++теперь символы 0x7F-0xFF в access_log записываются в виде \xXX. ++ ++ ++now the 0x7F-0xFF characters are escaped as \xXX in an access_log. ++ ++ ++ ++ ++ ++директивы "proxy/fastcgi/scgi/uwsgi_ignore_headers" теперь поддерживают ++значения X-Accel-Limit-Rate, X-Accel-Buffering и X-Accel-Charset. ++ ++ ++"proxy/fastcgi/scgi/uwsgi_ignore_headers" directives support the following ++additional values: X-Accel-Limit-Rate, X-Accel-Buffering, X-Accel-Charset. ++ ++ ++ ++ ++ ++уменьшение потребления памяти при использовании SSL. ++ ++ ++decrease of memory consumption if SSL is used. ++ ++ ++ ++ ++ ++некоторые UTF-8 символы обрабатывались неправильно.
++Спасибо Алексею Куцу. ++
++ ++some UTF-8 characters were processed incorrectly.
++Thanks to Alexey Kuts. ++
++
++ ++ ++ ++директивы модуля ngx_http_rewrite_module, заданные на уровне server, ++применялись повторно, если для запроса не находилось ни одного location'а. ++ ++ ++the ngx_http_rewrite_module directives specified at "server" level were ++executed twice if no matching locations were defined. ++ ++ ++ ++ ++ ++при использовании "aio sendfile" могла происходить утечка сокетов. ++ ++ ++a socket leak might occurred if "aio sendfile" was used. ++ ++ ++ ++ ++ ++при использовании файлового AIO соединения с быстрыми клиентами ++могли быть закрыты по истечению send_timeout. ++ ++ ++connections with fast clients might be closed after send_timeout ++if file AIO was used. ++ ++ ++ ++ ++ ++в модуле ngx_http_autoindex_module. ++ ++ ++in the ngx_http_autoindex_module. ++ ++ ++ ++ ++ ++модуль ngx_http_mp4_module не поддерживал перемотку на 32-битных платформах. ++ ++ ++the module ngx_http_mp4_module did not support seeking on 32-bit platforms. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++директивы uwsgi_buffering и scgi_buffering.
++Спасибо Peter Smit. ++
++ ++the "uwsgi_buffering" and "scgi_buffering" directives.
++Thanks to Peter Smit. ++
++
++ ++ ++ ++при использовании proxy_cache_bypass могли быть закэшированы ++некэшируемые ответы.
++Спасибо John Ferlito. ++
++ ++non-cacheable responses might be cached if "proxy_cache_bypass" directive ++was used.
++Thanks to John Ferlito. ++
++
++ ++ ++ ++в модуле ngx_http_proxy_module при работе с бэкендами по HTTP/1.1. ++ ++ ++in HTTP/1.1 support in the ngx_http_proxy_module. ++ ++ ++ ++ ++ ++закэшированные ответы с пустым телом возвращались некорректно; ++ошибка появилась в 0.8.31. ++ ++ ++cached responses with an empty body were returned incorrectly; ++the bug had appeared in 0.8.31. ++ ++ ++ ++ ++ ++ответы с кодом 201 модуля ngx_http_dav_module были некорректны; ++ошибка появилась в 0.8.32. ++ ++ ++201 responses of the ngx_http_dav_module were incorrect; ++the bug had appeared in 0.8.32. ++ ++ ++ ++ ++ ++в директиве return. ++ ++ ++in the "return" directive. ++ ++ ++ ++ ++ ++при использовании директивы "ssl_session_cache builtin" происходил ++segmentation fault; ++ошибка появилась в 1.1.1. ++ ++ ++the "ssl_session_cache builtin" directive caused segmentation fault; ++the bug had appeared in 1.1.1. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++модуль ngx_http_upstream_keepalive. ++ ++ ++the ngx_http_upstream_keepalive module. ++ ++ ++ ++ ++ ++директива proxy_http_version. ++ ++ ++the "proxy_http_version" directive. ++ ++ ++ ++ ++ ++директива fastcgi_keep_conn. ++ ++ ++the "fastcgi_keep_conn" directive. ++ ++ ++ ++ ++ ++директива worker_aio_requests. ++ ++ ++the "worker_aio_requests" directive. ++ ++ ++ ++ ++ ++если nginx был собран с файловым AIO, ++он не мог запускаться на Linux без поддержки AIO. ++ ++ ++if nginx was built --with-file-aio it could not be run on Linux ++kernel which did not support AIO. ++ ++ ++ ++ ++ ++в обработке ошибок при работе с Linux AIO. ++
++Спасибо Hagai Avrahami. ++
++ ++in Linux AIO error processing. ++
++Thanks to Hagai Avrahami. ++
++
++ ++ ++ ++уменьшено потребление памяти для долгоживущих запросов. ++ ++ ++reduced memory consumption for long-lived requests. ++ ++ ++ ++ ++ ++модуль ngx_http_mp4_module не поддерживал 64-битный MP4-атом co64. ++ ++ ++the module ngx_http_mp4_module did not support 64-bit MP4 "co64" atom. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++модуль ngx_http_mp4_module. ++ ++ ++the module ngx_http_mp4_module. ++ ++ ++ ++ ++ ++в Linux AIO, используемым совместно с open_file_cache. ++ ++ ++in Linux AIO combined with open_file_cache. ++ ++ ++ ++ ++ ++open_file_cache не обновлял информацию о файле, ++если файл был изменён не атомарно. ++ ++ ++open_file_cache did not update file info on retest ++if file was not atomically changed. ++ ++ ++ ++ ++ ++nginx не собирался на MacOSX 10.7. ++ ++ ++nginx could not be built on MacOSX 10.7. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++теперь, если суммарный размер всех диапазонов больше размера исходного ответа, ++то nginx возвращает только исходный ответ, не обрабатывая диапазоны. ++ ++ ++now if total size of all ranges is greater than source response size, ++then nginx disables ranges and returns just the source response. ++ ++ ++ ++ ++ ++директива max_ranges. ++ ++ ++the "max_ranges" directive. ++ ++ ++ ++ ++ ++директивы ssl_verify_client, ssl_verify_depth и ssl_prefer_server_cipher ++могли работать некорректно, если использовался SNI. ++ ++ ++the "ssl_verify_client", "ssl_verify_depth", and "ssl_prefer_server_ciphers" ++directives might work incorrectly if SNI was used. ++ ++ ++ ++ ++ ++в директивах proxy/fastcgi/scgi/ uwsgi_ignore_client_abort. ++ ++ ++in the "proxy/fastcgi/scgi/uwsgi_ignore_client_abort" directives. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++теперь загрузчик кэша за каждую итерацию либо обрабатывает число файлов, ++указанное в параметре load_files, либо работает не дольше времени, ++указанного в параметре loader_threshold. ++ ++ ++now cache loader processes either as many files as specified by "loader_files" ++parameter or works no longer than time specified by the "loader_threshold" ++parameter during each iteration. ++ ++ ++ ++ ++ ++SIGWINCH сигнал теперь работает только в режиме демона. ++ ++ ++now SIGWINCH signal works only in daemon mode. ++ ++ ++ ++ ++ ++теперь разделяемые зоны и кэши используют семафоры POSIX на Solaris.
++Спасибо Денису Иванову. ++
++ ++now shared zones and caches use POSIX semaphores on Solaris.
++Thanks to Den Ivanov. ++
++
++ ++ ++ ++теперь на NetBSD поддерживаются accept фильтры. ++ ++ ++accept filters are now supported on NetBSD. ++ ++ ++ ++ ++ ++nginx не собирался на Linux 3.0. ++ ++ ++nginx could not be built on Linux 3.0. ++ ++ ++ ++ ++ ++в некоторых случаях nginx не использовал сжатие; ++ошибка появилась в 1.1.0. ++ ++ ++nginx did not use gzipping in some cases; ++the bug had appeared in 1.1.0. ++ ++ ++ ++ ++ ++обработка тела запроса могла быть неверной, если клиент использовал pipelining. ++ ++ ++request body might be processed incorrectly if client used pipelining. ++ ++ ++ ++ ++ ++в директиве request_body_in_single_buf. ++ ++ ++in the "request_body_in_single_buf" directive. ++ ++ ++ ++ ++ ++в директивах proxy_set_body и proxy_pass_request_body ++при использовании SSL-соединения с бэкендом. ++ ++ ++in "proxy_set_body" and "proxy_pass_request_body" directives ++if SSL connection to backend was used. ++ ++ ++ ++ ++ ++nginx нагружал процессор, если все серверы в upstream'е были помечены ++флагом down. ++ ++ ++nginx hogged CPU if all servers in an upstream were marked as "down". ++ ++ ++ ++ ++ ++при переконфигурации мог произойти segmentation fault, ++если в предыдущей конфигурации был определён, но не использовался ++ssl_session_cache. ++ ++ ++a segmentation fault might occur during reconfiguration ++if ssl_session_cache was defined but not used in previous configuration. ++ ++ ++ ++ ++ ++при использовании большого количества backup-серверов ++в рабочем процессе мог произойти segmentation fault. ++ ++ ++a segmentation fault might occur in a worker process ++if many backup servers were used in an upstream. ++ ++ ++ ++ ++ ++при использовании директив fastcgi/scgi/uwsgi_param ++со значениями, начинающимися со строки "HTTP_", ++в рабочем процессе мог произойти segmentation fault; ++ошибка появилась в 0.8.40. ++ ++ ++a segmentation fault might occur in a worker process ++if "fastcgi/scgi/uwsgi_param" directives were used ++with values starting with "HTTP_"; ++the bug had appeared in 0.8.40. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++уменьшение времени работы загрузчика кэша. ++ ++ ++cache loader run time decrease. ++ ++ ++ ++ ++ ++параметры loader_files, loader_sleep и loader_threshold ++директив proxy/fastcgi/scgi/uwsgi_cache_path. ++ ++ ++"loader_files", "loader_sleep", and "loader_threshold" options ++of the "proxy/fastcgi/scgi/uwsgi_cache_path" directives. ++ ++ ++ ++ ++ ++уменьшение времени загрузки конфигураций с большим количеством HTTPS серверов. ++ ++ ++loading time decrease of configuration with large number of HTTPS sites. ++ ++ ++ ++ ++ ++теперь nginx поддерживает шифры с обменом ECDHE-ключами.
++Спасибо Adrian Kotelba. ++
++ ++now nginx supports ECDHE key exchange ciphers.
++Thanks to Adrian Kotelba. ++
++
++ ++ ++ ++директива lingering_close.
++Спасибо Максиму Дунину. ++
++ ++the "lingering_close" directive.
++Thanks to Maxim Dounin. ++
++
++ ++ ++ ++закрытия соединения для pipelined-запросов.
++Спасибо Максиму Дунину. ++
++ ++in closing connection for pipelined requests.
++Thanks to Maxim Dounin. ++
++
++ ++ ++ ++nginx не запрещал сжатие при получении значения "gzip;q=0" ++в строке "Accept-Encoding" в заголовке запроса клиента. ++ ++ ++nginx did not disable gzipping if client sent "gzip;q=0" in ++"Accept-Encoding" request header line. ++ ++ ++ ++ ++ ++таймаута при небуферизированном проксировании.
++Спасибо Максиму Дунину. ++
++ ++in timeout in unbuffered proxied mode.
++Thanks to Maxim Dounin. ++
++
++ ++ ++ ++утечки памяти при использовании переменных в директиве proxy_pass ++при работе с бэкендом по HTTPS.
++Спасибо Максиму Дунину. ++
++ ++memory leaks when a "proxy_pass" directive contains variables and proxies ++to an HTTPS backend.
++Thanks to Maxim Dounin. ++
++
++ ++ ++ ++в проверке параметра директивы proxy_pass, заданного переменными.
++Спасибо Lanshun Zhou. ++
++ ++in parameter validation of a "proxy_pass" directive with variables.
++Thanks to Lanshun Zhou. ++
++
++ ++ ++ ++SSL не работал на QNX.
++Спасибо Максиму Дунину. ++
++ ++SSL did not work on QNX.
++Thanks to Maxim Dounin. ++
++
++ ++ ++ ++SSL модули не собирались gcc 4.6 без параметра --with-debug. ++ ++ ++SSL modules could not be built by gcc 4.6 without --with-debug option. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++теперь по умолчанию используются следующие шифры SSL: "HIGH:!aNULL:!MD5".
++Спасибо Rob Stradling. ++
++ ++now default SSL ciphers are "HIGH:!aNULL:!MD5".
++Thanks to Rob Stradling. ++
++
++ ++ ++ ++директивы referer_hash_max_size и referer_hash_bucket_size.
++Спасибо Witold Filipczyk. ++
++ ++the "referer_hash_max_size" and "referer_hash_bucket_size" ++directives.
++Thanks to Witold Filipczyk. ++
++
++ ++ ++ ++переменная $uid_reset. ++ ++ ++$uid_reset variable. ++ ++ ++ ++ ++ ++при использовании кэширования ++в рабочем процессе мог произойти segmentation fault.
++Спасибо Lanshun Zhou. ++
++ ++a segmentation fault might occur in a worker process, ++if a caching was used.
++Thanks to Lanshun Zhou. ++
++
++ ++ ++ ++при использовании кэширования рабочие процессы ++могли зациклиться во время переконфигурации; ++ошибка появилась в 0.8.48.
++Спасибо Максиму Дунину. ++
++ ++worker processes may got caught in an endless loop during reconfiguration, ++if a caching was used; ++the bug had appeared in 0.8.48.
++Thanks to Maxim Dounin. ++
++
++ ++ ++ ++сообщения "stalled cache updating".
++Спасибо Максиму Дунину. ++
++ ++"stalled cache updating" alert.
++Thanks to Maxim Dounin. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++теперь в регулярных выражениях в директиве map можно задать ++чувствительность к регистру с помощью префиксов "~" и "~*". ++ ++ ++now regular expressions case sensitivity in the "map" directive ++is given by prefixes "~" or "~*". ++ ++ ++ ++ ++ ++теперь разделяемые зоны и кэши используют семафоры POSIX на Linux.
++Спасибо Денису Латыпову. ++
++ ++now shared zones and caches use POSIX semaphores on Linux.
++Thanks to Denis F. Latypoff. ++
++
++ ++ ++ ++сообщения "stalled cache updating". ++ ++ ++"stalled cache updating" alert. ++ ++ ++ ++ ++ ++nginx не собирался с параметром --without-http_auth_basic_module; ++ошибка появилась в 1.0.3. ++ ++ ++nginx could not be built --without-http_auth_basic_module; ++the bug had appeared in 1.0.3. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++директива auth_basic_user_file поддерживает шифрование пароля ++методами "$apr1", "{PLAIN}" и "{SSHA}".
++Спасибо Максиму Дунину. ++
++ ++the "auth_basic_user_file" directive supports "$apr1", "{PLAIN}", ++and "{SSHA}" password encryption methods.
++Thanks to Maxim Dounin. ++
++
++ ++ ++ ++директива geoip_org и переменная $geoip_org.
++Спасибо Александру Ускову, Arnaud Granal и Денису Латыпову. ++
++ ++the "geoip_org" directive and $geoip_org variable.
++Thanks to Alexander Uskov, Arnaud Granal, and Denis F. Latypoff. ++
++
++ ++ ++ ++модули ngx_http_geo_module и ngx_http_geoip_module поддерживают ++адреса IPv4, отображённые на IPv6 адреса. ++ ++ ++ngx_http_geo_module and ngx_http_geoip_module support IPv4 addresses ++mapped to IPv6 addresses. ++ ++ ++ ++ ++ ++при проверке адреса IPv4, отображённого на адрес IPv6, ++в рабочем процессе происходил segmentation fault, ++если директивы access или deny были определены только для адресов IPv6; ++ошибка появилась в 0.8.22. ++ ++ ++a segmentation fault occurred in a worker process ++during testing IPv4 address mapped to IPv6 address, ++if access or deny rules were defined only for IPv6; ++the bug had appeared in 0.8.22. ++ ++ ++ ++ ++ ++закэшированный ответ мог быть испорчен, если значения директив ++proxy/fastcgi/scgi/uwsgi_cache_bypass и proxy/fastcgi/scgi/ uwsgi_no_cache ++были разными; ++ошибка появилась в 0.8.46. ++ ++ ++a cached response may be broken if "proxy/fastcgi/scgi/ uwsgi_cache_bypass" ++and "proxy/fastcgi/scgi/uwsgi_no_cache" directive values were different; ++the bug had appeared in 0.8.46. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++теперь разделяемые зоны и кэши используют семафоры POSIX. ++ ++ ++now shared zones and caches use POSIX semaphores. ++ ++ ++ ++ ++ ++в работе параметра rotate директивы image_filter.
++Спасибо Adam Bocim. ++
++ ++in the "rotate" parameter of the "image_filter" directive.
++Thanks to Adam Bocim. ++
++
++ ++ ++ ++nginx не собирался на Solaris; ++ошибка появилась в 1.0.1. ++ ++ ++nginx could not be built on Solaris; ++the bug had appeared in 1.0.1. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++теперь директива split_clients использует алгоритм MurmurHash2 из-за ++лучшего распределения.
++Спасибо Олегу Мамонтову. ++
++ ++now the "split_clients" directive uses MurmurHash2 algorithm because ++of better distribution.
++Thanks to Oleg Mamontov. ++
++
++ ++ ++ ++теперь длинные строки, начинающиеся с нуля, не считаются ложными ++значениями.
++Спасибо Максиму Дунину. ++
++ ++now long strings starting with zero are not considered as false values.
++Thanks to Maxim Dounin. ++
++
++ ++ ++ ++теперь по умолчанию nginx использует значение 511 для listen backlog на Linux. ++ ++ ++now nginx uses a default listen backlog value 511 on Linux. ++ ++ ++ ++ ++ ++переменные $upstream_... можно использовать в SSI и перловом модулях. ++ ++ ++the $upstream_... variables may be used in the SSI and perl modules. ++ ++ ++ ++ ++ ++теперь nginx лучше ограничивает размер кэша на диске.
++Спасибо Олегу Мамонтову. ++
++ ++now nginx limits better disk cache size.
++Thanks to Oleg Mamontov. ++
++
++ ++ ++ ++при парсинге неправильного IPv4 адреса мог произойти segmentation fault; ++ошибка появилась в 0.8.22.
++Спасибо Максиму Дунину. ++
++ ++a segmentation fault might occur while parsing incorrect IPv4 address; ++the bug had appeared in 0.9.3.
++Thanks to Maxim Dounin. ++
++
++ ++ ++ ++nginx не собирался gcc 4.6 без параметра --with-debug. ++ ++ ++nginx could not be built by gcc 4.6 without --with-debug option. ++ ++ ++ ++ ++ ++nginx не собирался на Solaris 9 и более ранних; ++ошибка появилась в 0.9.3.
++Спасибо Dagobert Michelsen. ++
++ ++nginx could not be built on Solaris 9 and earlier; ++the bug had appeared in 0.9.3.
++Thanks to Dagobert Michelsen. ++
++
++ ++ ++ ++переменная $request_time имела неверные значения, если использовались ++подзапросы; ++ошибка появилась в 0.8.47.
++Спасибо Игорю А. Валькову. ++
++ ++$request_time variable had invalid values if subrequests were used; ++the bug had appeared in 0.8.47.
++Thanks to Igor A. Valcov. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++cache manager мог нагружать процессор после переконфигурации.
++Спасибо Максиму Дунину. ++
++ ++a cache manager might hog CPU after reload.
++Thanks to Maxim Dounin. ++
++
++ ++ ++ ++директива "image_filter crop" неправильно работала в сочетании с ++"image_filter rotate 180". ++ ++ ++an "image_filter crop" directive worked incorrectly coupled with ++an "image_filter rotate 180" directive. ++ ++ ++ ++ ++ ++директива "satisfy any" запрещала выдачу пользовательской страницы ++для 401 кода. ++ ++ ++a "satisfy any" directive disabled custom 401 error page. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++теперь соединения в состоянии keepalive могут быть закрыты преждевременно, ++если у воркера нет свободных соединений.
++Спасибо Максиму Дунину. ++
++ ++now keepalive connections may be closed premature, ++if there are no free worker connections.
++Thanks to Maxim Dounin. ++
++
++ ++ ++ ++параметр rotate директивы image_filter.
++Спасибо Adam Bocim. ++
++ ++the "rotate" parameter of the "image_filter" directive.
++Thanks to Adam Bocim. ++
++
++ ++ ++ ++ситуации, когда бэкенд в директивах fastcgi_pass, scgi_pass или uwsgi_pass ++задан выражением и ссылается на описанный upstream. ++ ++ ++a case when a backend in "fastcgi_pass", "scgi_pass", or "uwsgi_pass" ++directives is given by expression and refers to a defined upstream. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++директива map поддерживает регулярные выражения в качестве значения ++первого параметра. ++ ++ ++the "map" directive supports regular expressions as value of the first ++parameter. ++ ++ ++ ++ ++ ++переменная $time_iso8601 для access_log.
++Спасибо Michael Lustfield. ++
++ ++$time_iso8601 access_log variable.
++Thanks to Michael Lustfield. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++теперь по умолчанию nginx использует значение -1 для listen backlog ++на Linux.
++Спасибо Андрею Нигматулину. ++
++ ++now nginx uses a default listen backlog value -1 on Linux.
++Thanks to Andrei Nigmatulin. ++
++
++ ++ ++ ++параметр utf8 в директивах geoip_country и geoip_city.
++Спасибо Денису Латыпову. ++
++ ++the "utf8" parameter of "geoip_country" and "geoip_city" directives.
++Thanks to Denis F. Latypoff. ++
++
++ ++ ++ ++исправление в умолчательной директиве proxy_redirect, если в директиве ++proxy_pass не был описан URI.
++Спасибо Максиму Дунину. ++
++ ++in a default "proxy_redirect" directive if "proxy_pass" directive has no ++URI part.
++Thanks to Maxim Dounin. ++
++
++ ++ ++ ++директива error_page не работала с нестандартными кодами ошибок; ++ошибка появилась в 0.8.53.
++Спасибо Максиму Дунину. ++
++ ++an "error_page" directive did not work with nonstandard error codes; ++the bug had appeared in 0.8.53.
++Thanks to Maxim Dounin. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++директива server_name поддерживает переменную $hostname. ++ ++ ++the "server_name" directive supports the $hostname variable. ++ ++ ++ ++ ++ ++494 код для ошибки "Request Header Too Large". ++ ++ ++494 code for "Request Header Too Large" error. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++если для пары IPv6-адрес:порт описан только один сервер, то выделения ++в регулярных выражениях в директиве server_name не работали. ++ ++ ++if there was a single server for given IPv6 address:port pair, ++then captures in regular expressions in a "server_name" directive did not work. ++ ++ ++ ++ ++ ++nginx не собирался под Solaris; ++ошибка появилась в 0.9.0. ++ ++ ++nginx could not be built on Solaris; ++the bug had appeared in 0.9.0. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++поддержка строки "If-Unmodified-Since" в заголовке запроса клиента. ++ ++ ++the "If-Unmodified-Since" client request header line support. ++ ++ ++ ++ ++ ++использование accept(), если accept4() не реализован; ++ошибка появилась в 0.9.0. ++ ++ ++fallback to accept() syscall if accept4() was not implemented; ++the issue had appeared in 0.9.0. ++ ++ ++ ++ ++ ++nginx не собирался под Cygwin; ++ошибка появилась в 0.9.0. ++ ++ ++nginx could not be built on Cygwin; ++the bug had appeared in 0.9.0. ++ ++ ++ ++ ++ ++уязвимости в OpenSSL CVE-2010-4180.
++Спасибо Максиму Дунину. ++
++ ++for OpenSSL vulnerability CVE-2010-4180.
++Thanks to Maxim Dounin. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++директивы вида "return CODE message" не работали; ++ошибка появилась в 0.9.0. ++ ++ ++"return CODE message" directives did not work; ++the bug had appeared in 0.9.0. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директива keepalive_disable. ++ ++ ++the "keepalive_disable" directive. ++ ++ ++ ++ ++ ++директива map поддерживает переменные в качестве значения определяемой ++переменной. ++ ++ ++the "map" directive supports variables as value of a defined variable. ++ ++ ++ ++ ++ ++директива map поддерживает пустые строки в качестве значения первого параметра. ++ ++ ++the "map" directive supports empty strings as value of the first parameter. ++ ++ ++ ++ ++ ++директива map поддерживает выражения в первом параметре. ++ ++ ++the "map" directive supports expressions as the first parameter. ++ ++ ++ ++ ++ ++страница руководства nginx(8).
++Спасибо Сергею Осокину. ++
++ ++nginx(8) manual page.
++Thanks to Sergey Osokin. ++
++
++ ++ ++ ++поддержка accept4() в Linux.
++Спасибо Simon Liu. ++
++ ++Linux accept4() support.
++Thanks to Simon Liu. ++
++
++ ++ ++ ++устранение предупреждения линкера о "sys_errlist" и "sys_nerr" под Linux; ++предупреждение появилось в 0.8.35. ++ ++ ++elimination of Linux linker warning about "sys_errlist" and "sys_nerr"; ++the warning had appeared in 0.8.35. ++ ++ ++ ++ ++ ++при использовании директивы auth_basic ++в рабочем процессе мог произойти segmentation fault.
++Спасибо Михаилу Лалетину. ++
++ ++a segmentation fault might occur in a worker process, ++if the "auth_basic" directive was used.
++Thanks to Michail Laletin. ++
++
++ ++ ++ ++совместимость с модулем ngx_http_eval_module; ++ошибка появилась в 0.8.42. ++ ++ ++compatibility with ngx_http_eval_module; ++the bug had appeared in 0.8.42. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++теперь директива error_page позволяет менять код статуса у редиректа. ++ ++ ++now the "error_page" directive allows to change a status code in a redirect. ++ ++ ++ ++ ++ ++директива gzip_disable поддерживает специальную маску degradation. ++ ++ ++the "gzip_disable" directive supports special "degradation" mask. ++ ++ ++ ++ ++ ++при использовании файлового AIO могла происходить утечка сокетов.
++Спасибо Максиму Дунину. ++
++ ++a socket leak might occurred if file AIO was used.
++Thanks to Maxim Dounin. ++
++
++ ++ ++ ++если в первом сервере не была описана директива listen и нигде явно ++не описан сервер по умолчанию, то сервером по умолчанию становился ++следующий сервер с директивой listen; ++ошибка появилась в 0.8.21. ++ ++ ++if the first server had no "listen" directive and there was no explicit ++default server, then a next server with a "listen" directive became ++the default server; ++the bug had appeared in 0.8.21. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++nginx использовал режим SSL для listen сокета, если для него был ++установлен любой listen-параметр; ++ошибка появилась в 0.8.51. ++ ++ ++nginx used SSL mode for a listen socket if any listen option was set; ++the bug had appeared in 0.8.51. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директива secure_link_expires упразднена. ++ ++ ++the "secure_link_expires" directive has been canceled. ++ ++ ++ ++ ++ ++уровень логгирования ошибок resolver'а понижен с уровня alert на error. ++ ++ ++a logging level of resolver errors has been lowered from "alert" to "error". ++ ++ ++ ++ ++ ++теперь параметр "ssl" listen-сокета можно устанавливать несколько раз. ++ ++ ++now a listen socket "ssl" parameter may be set several times. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директивы secure_link, secure_link_md5 и secure_link_expires ++модуля ngx_http_secure_link_module. ++ ++ ++the "secure_link", "secure_link_md5", and "secure_link_expires" directives of ++the ngx_http_secure_link_module. ++ ++ ++ ++ ++ ++ключ -q.
++Спасибо Геннадию Махомеду. ++
++ ++the -q switch.
++Thanks to Gena Makhomed. ++
++
++ ++ ++ ++при использовании кэширования рабочие процессы и могли зациклиться ++во время переконфигурации; ++ошибка появилась в 0.8.48. ++ ++ ++worker processes may got caught in an endless loop during reconfiguration, ++if a caching was used; ++the bug had appeared in 0.8.48. ++ ++ ++ ++ ++ ++в директиве gzip_disable.
++Спасибо Derrick Petzold. ++
++ ++in the "gzip_disable" directive.
++Thanks to Derrick Petzold. ++
++
++ ++ ++ ++nginx/Windows не мог посылать сигналы stop, quit, reopen, reload процессу, ++запущенному в другой сессии. ++ ++ ++nginx/Windows could not send stop, quit, reopen, and reload signals ++to a process run in other session. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++директива image_filter_jpeg_quality поддерживает переменные. ++ ++ ++the "image_filter_jpeg_quality" directive supports variables. ++ ++ ++ ++ ++ ++при использовании переменной $geoip_region_name ++в рабочем процессе мог произойти segmentation fault; ++ошибка появилась в 0.8.48. ++ ++ ++a segmentation fault might occur in a worker process, ++if the $geoip_region_name variables was used; ++the bug had appeared in 0.8.48. ++ ++ ++ ++ ++ ++ошибки, перехваченные error_page, кэшировались только до следующего запроса; ++ошибка появилась в 0.8.48. ++ ++ ++errors intercepted by error_page were cached only for next request; ++the bug had appeared in 0.8.48. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++теперь по умолчанию директива server_name имеет значение пустое имя "".
++Спасибо Геннадию Махомеду. ++
++ ++now the "server_name" directive default value is an empty name "".
++Thanks to Gena Makhomed. ++
++
++ ++ ++ ++теперь по умолчанию директива server_name_in_redirect имеет значение off. ++ ++ ++now the "server_name_in_redirect" directive default value is "off". ++ ++ ++ ++ ++ ++переменные $geoip_dma_code, $geoip_area_code и $geoip_region_name.
++Спасибо Christine McGonagle. ++
++ ++the $geoip_dma_code, $geoip_area_code, and $geoip_region_name variables.
++Thanks to Christine McGonagle. ++
++
++ ++ ++ ++директивы proxy_pass, fastcgi_pass, uwsgi_pass и scgi_pass не наследовались ++в блоки limit_except. ++ ++ ++the "proxy_pass", "fastcgi_pass", "uwsgi_pass", and "scgi_pass" directives ++were not inherited inside "limit_except" blocks. ++ ++ ++ ++ ++ ++директивы proxy_cache_min_uses, fastcgi_cache_min_uses ++uwsgi_cache_min_uses и scgi_cache_min_uses не работали; ++ошибка появилась в 0.8.46. ++ ++ ++the "proxy_cache_min_uses", "fastcgi_cache_min_uses" ++"uwsgi_cache_min_uses", and "scgi_cache_min_uses" directives did not work; ++the bug had appeared in 0.8.46. ++ ++ ++ ++ ++ ++директива fastcgi_split_path_info неверно использовала выделения, ++если в выделения попадала только часть URI.
++Спасибо Юрию Тарадаю и Frank Enderle. ++
++ ++the "fastcgi_split_path_info" directive used incorrectly captures, ++if only parts of an URI were captured.
++Thanks to Yuriy Taraday and Frank Enderle. ++
++
++ ++ ++ ++директива rewrite не экранировала символ ";" при копировании из URI ++в аргументы.
++Спасибо Daisuke Murase. ++
++ ++the "rewrite" directive did not escape a ";" character during copying ++from URI to query string.
++Thanks to Daisuke Murase. ++
++
++ ++ ++ ++модуль ngx_http_image_filter_module закрывал соединение, ++если изображение было больше размера image_filter_buffer. ++ ++ ++the ngx_http_image_filter_module closed a connection, ++if an image was larger than "image_filter_buffer" size. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++переменная $request_time имела неверные значения для подзапросов. ++ ++ ++$request_time variable had invalid values for subrequests. ++ ++ ++ ++ ++ ++ошибки, перехваченные error_page, не кэшировались. ++ ++ ++errors intercepted by error_page could not be cached. ++ ++ ++ ++ ++ ++если использовался параметр max_size, то cache manager мог зациклиться; ++ошибка появилась в 0.8.46. ++ ++ ++a cache manager process may got caught in an endless loop, ++if max_size parameter was used; ++the bug had appeared in 0.8.46. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директивы proxy_no_cache, fastcgi_no_cache, uwsgi_no_cache ++и scgi_no_cache теперь влияют только на сохранение закэшированного ответа. ++ ++ ++now the "proxy_no_cache", "fastcgi_no_cache", "uwsgi_no_cache", and ++"scgi_no_cache" directives affect on a cached response saving only. ++ ++ ++ ++ ++ ++директивы proxy_cache_bypass, fastcgi_cache_bypass, uwsgi_cache_bypass ++и scgi_cache_bypass. ++ ++ ++the "proxy_cache_bypass", "fastcgi_cache_bypass", "uwsgi_cache_bypass", ++and "scgi_cache_bypass" directives. ++ ++ ++ ++ ++ ++nginx не освобождал память в keys_zone кэшей в случае ошибки работы с ++бэкендом: память освобождалась только по истечении времени неактивности ++или при недостатке памяти. ++ ++ ++nginx did not free memory in cache keys zones if there was an error ++during working with backend: the memory was freed only after inactivity ++time or on memory low condition. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++улучшения в модуле ngx_http_xslt_filter.
++Спасибо Laurence Rowe. ++
++ ++ngx_http_xslt_filter improvements.
++Thanks to Laurence Rowe. ++
++
++ ++ ++ ++ответ SSI модуля мог передаваться не полностью после команды include ++с параметром wait="yes"; ++ошибка появилась в 0.7.25.
++Спасибо Максиму Дунину. ++
++ ++SSI response might be truncated after include with wait="yes"; ++the bug had appeared in 0.7.25.
++Thanks to Maxim Dounin. ++
++
++ ++ ++ ++директива listen не поддерживала параметр setfib=0. ++ ++ ++the "listen" directive did not support the "setfib=0" parameter. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++теперь nginx по умолчанию не кэширует ответы бэкендов, ++в заголовке которых есть строка "Set-Cookie". ++ ++ ++now nginx does not cache by default backend responses, ++if they have a "Set-Cookie" header line. ++ ++ ++ ++ ++ ++директива listen поддерживает параметр setfib.
++Спасибо Андрею Филонову. ++
++ ++the "listen" directive supports the "setfib" parameter.
++Thanks to Andrew Filonov. ++
++
++ ++ ++ ++директива sub_filter могла изменять регистр букв при частичном совпадении. ++ ++ ++the "sub_filter" directive might change character case on partial match. ++ ++ ++ ++ ++ ++совместимость с HP/UX. ++ ++ ++compatibility with HP/UX. ++ ++ ++ ++ ++ ++совместимость с компилятором AIX xlC_r. ++ ++ ++compatibility with AIX xlC_r compiler. ++ ++ ++ ++ ++ ++nginx считал большие пакеты SSLv2 как обычные текстовые запросы.
++Спасибо Miroslaw Jaworski. ++
++ ++nginx treated large SSLv2 packets as plain requests.
++Thanks to Miroslaw Jaworski. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++ускорение загрузки больших баз geo-диапазонов. ++ ++ ++large geo ranges base loading speed-up. ++ ++ ++ ++ ++ ++перенаправление ошибки в "location /zero {return 204;}" без изменения ++кода ответа оставляло тело ошибки; ++ошибка появилась в 0.8.42. ++ ++ ++an error_page redirection to "location /zero {return 204;}" without ++changing status code kept the error body; ++the bug had appeared in 0.8.42. ++ ++ ++ ++ ++ ++nginx мог закрывать IPv6 listen сокет во время переконфигурации.
++Спасибо Максиму Дунину. ++
++ ++nginx might close IPv6 listen socket during reconfiguration.
++Thanks to Maxim Dounin. ++
++
++ ++ ++ ++переменную $uid_set можно использовать на любой стадии обработки запроса. ++ ++ ++the $uid_set variable may be used at any request processing stage. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++теперь nginx проверяет location'ы, заданные регулярными выражениями, ++если запрос полностью совпал с location'ом, заданным строкой префикса. ++Предыдущее поведение появилось в 0.7.1. ++ ++ ++now nginx tests locations given by regular expressions, ++if request was matched exactly by a location given by a prefix string. ++The previous behavior has been introduced in 0.7.1. ++ ++ ++ ++ ++ ++модуль ngx_http_scgi_module.
++Спасибо Manlio Perillo. ++
++ ++the ngx_http_scgi_module.
++Thanks to Manlio Perillo. ++
++
++ ++ ++ ++в директиве return можно добавлять текст ответа. ++ ++ ++a text answer may be added to a "return" directive. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++рабочий процесс nginx/Windows мог завершаться аварийно при запросе файла ++с неверной кодировкой UTF-8. ++ ++ ++nginx/Windows worker might be terminated abnormally if a requested file name ++has invalid UTF-8 encoding. ++ ++ ++ ++ ++ ++теперь nginx разрешает использовать пробелы в строке запроса. ++ ++ ++now nginx allows to use spaces in a request line. ++ ++ ++ ++ ++ ++директива proxy_redirect неправильно изменяла строку "Refresh" в заголовке ++ответа бэкенда.
++Спасибо Андрею Андрееву и Максиму Согину. ++
++ ++the "proxy_redirect" directive changed incorrectly a backend "Refresh" ++response header line.
++Thanks to Andrey Andreew and Max Sogin. ++
++
++ ++ ++ ++nginx не поддерживал путь без имени хоста в ++строке "Destination" в заголовке запроса. ++ ++ ++nginx did not support path without host name ++in "Destination" request header line. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++теперь nginx/Windows игнорирует имя потока файла по умолчанию.
++Спасибо Jose Antonio Vazquez Gonzalez. ++
++ ++now nginx/Windows ignores default file stream name.
++Thanks to Jose Antonio Vazquez Gonzalez. ++
++
++ ++ ++ ++модуль ngx_http_uwsgi_module.
++Спасибо Roberto De Ioris. ++
++ ++the ngx_http_uwsgi_module.
++Thanks to Roberto De Ioris. ++
++
++ ++ ++ ++директива fastcgi_param со значением, начинающимся со строки "HTTP_", ++изменяет строку заголовка в запросе клиента. ++ ++ ++a "fastcgi_param" directive with value starting with "HTTP_" overrides ++a client request header line. ++ ++ ++ ++ ++ ++строки "If-Modified-Since", "If-Range" и им подобные в заголовке запроса ++клиента передавались FastCGI-серверу при кэшировании. ++ ++ ++the "If-Modified-Since", "If-Range", etc. client request header lines ++were passed to FastCGI-server while caching. ++ ++ ++ ++ ++ ++listen unix domain сокет нельзя было изменить во время переконфигурации.
++Спасибо Максиму Дунину. ++
++ ++listen unix domain socket could not be changed during reconfiguration.
++Thanks to Maxim Dounin. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++наследуемая директива alias неправильно работала во вложенном location'е. ++ ++ ++an inherited "alias" directive worked incorrectly in inclusive location. ++ ++ ++ ++ ++ ++в комбинации директив alias с переменными и try_files; ++ ++ ++in "alias" with variables and "try_files" directives combination. ++ ++ ++ ++ ++ ++listen unix domain и IPv6 сокеты не наследовались во время обновления ++без перерыва.
++Спасибо Максиму Дунину. ++
++ ++listen unix domain and IPv6 sockets did not inherit while online upgrade.
++Thanks to Maxim Dounin. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++директивы proxy_no_cache и fastcgi_no_cache. ++ ++ ++the "proxy_no_cache" and "fastcgi_no_cache" directives. ++ ++ ++ ++ ++ ++теперь при использовании переменной $scheme в директиве rewrite ++автоматически делается редирект.
++Спасибо Piotr Sikora. ++
++ ++now the "rewrite" directive does a redirect automatically ++if the $scheme variable is used.
++Thanks to Piotr Sikora. ++
++
++ ++ ++ ++теперь задержки в директиве limit_req соответствует описанному алгоритму.
++Спасибо Максиму Дунину. ++
++ ++now "limit_req" delay directive conforms to the described algorithm.
++Thanks to Maxim Dounin. ++
++
++ ++ ++ ++переменную $uid_got нельзя было использовать в SSI и перловом модулях. ++ ++ ++the $uid_got variable might not be used in the SSI and perl modules. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++модуль ngx_http_split_clients_module. ++ ++ ++the ngx_http_split_clients_module. ++ ++ ++ ++ ++ ++директива map поддерживает ключи больше 255 символов. ++ ++ ++the "map" directive supports keys more than 255 characters. ++ ++ ++ ++ ++ ++nginx игнорировал значения "private" и "no-store" в строке "Cache-Control" ++в заголовке ответа бэкенда. ++ ++ ++nginx ignored the "private" and "no-store" values ++in the "Cache-Control" backend response header line. ++ ++ ++ ++ ++ ++параметр stub в SSI-директиве include не использовался, ++если пустой ответ имел код 200. ++ ++ ++a "stub" parameter of an "include" SSI directive was not used, ++if empty response has 200 status code. ++ ++ ++ ++ ++ ++если проксированный или FastCGI запрос внутренне перенаправлялся ++в другой проксированный или FastCGI location, ++то в рабочем процессе мог произойти segmentation fault; ++ошибка появилась в 0.8.33.
++Спасибо Yichun Zhang. ++
++ ++if a proxied or FastCGI request was internally redirected ++to another proxied or FastCGI location, ++then a segmentation fault might occur in a worker process; ++the bug had appeared in 0.8.33.
++Thanks to Yichun Zhang. ++
++
++ ++ ++ ++соединения IMAP к серверу Zimbra могло зависнуть до таймаута.
++Спасибо Alan Batie. ++
++ ++IMAP connections may hang until they timed out ++while talking to Zimbra server.
++Thanks to Alan Batie. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++модуль ngx_http_dav_module неправильно обрабатывал методы DELETE, COPY и MOVE ++для симлинков. ++ ++ ++the ngx_http_dav_module handled incorrectly the DELETE, COPY, and MOVE methods ++for symlinks. ++ ++ ++ ++ ++ ++модуль SSI в подзапросах использовал закэшированные в основном запросе ++значения переменных $query_string, $arg_... и им подобных. ++ ++ ++values of the $query_string, $arg_..., etc. variables cached in main ++request were used by the SSI module in subrequests. ++ ++ ++ ++ ++ ++значение переменной повторно экранировалось после каждого вывода ++SSI-команды echo; ++ошибка появилась в 0.6.14. ++ ++ ++a variable value was repeatedly encoded after each ++an "echo" SSI-command output; ++the bug had appeared in 0.6.14. ++ ++ ++ ++ ++ ++рабочий процесс зависал при запросе файла FIFO.
++Спасибо Vicente Aguilar и Максиму Дунину. ++
++ ++a worker process hung if a FIFO file was requested.
++Thanks to Vicente Aguilar and Maxim Dounin. ++
++
++ ++ ++ ++совместимость с OpenSSL-1.0.0 на 64-битном Linux.
++Спасибо Максиму Дунину. ++
++ ++OpenSSL-1.0.0 compatibility on 64-bit Linux.
++Thanks to Maxim Dounin. ++
++
++ ++ ++ ++nginx не собирался с параметром --without-http-cache; ++ошибка появилась в 0.8.35. ++ ++ ++nginx could not be built --without-http-cache; ++the bug had appeared in 0.8.35. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++теперь charset-фильтр работает до SSI-фильтра. ++ ++ ++now the charset filter runs before the SSI filter. ++ ++ ++ ++ ++ ++директива chunked_transfer_encoding. ++ ++ ++the "chunked_transfer_encoding" directive. ++ ++ ++ ++ ++ ++символ "&" при копировании в аргументы в правилах rewrite не экранировался. ++ ++ ++an "&" character was not escaped when it was copied in arguments part ++in a rewrite rule. ++ ++ ++ ++ ++ ++nginx мог завершаться аварийно во время обработки сигнала или ++при использовании директивы timer_resolution на платформах, ++не поддерживающих методы kqueue или eventport.
++Спасибо George Xie и Максиму Дунину. ++
++ ++nginx might be terminated abnormally ++while a signal processing or if the directive "timer_resolution" was used ++on platforms which do not support kqueue or eventport notification methods.
++Thanks to George Xie and Maxim Dounin. ++
++
++ ++ ++ ++если временные файлы и постоянное место хранения располагались на разных ++файловых системах, то у постоянных файлов время изменения было неверным.
++Спасибо Максиму Дунину. ++
++ ++if temporary files and permanent storage area resided at different ++file systems, then permanent file modification times were incorrect.
++Thanks to Maxim Dounin. ++
++
++ ++ ++ ++модуль ngx_http_memcached_module мог выдавать ошибку "memcached sent invalid ++trailer".
++Спасибо Максиму Дунину. ++
++ ++ngx_http_memcached_module might issue the error message "memcached sent invalid ++trailer".
++Thanks to Maxim Dounin. ++
++
++ ++ ++ ++nginx не мог собрать библиотеку zlib-1.2.4 из исходных текстов.
++Спасибо Максиму Дунину. ++
++ ++nginx could not built zlib-1.2.4 library using the library sources.
++Thanks to Maxim Dounin. ++
++
++ ++ ++ ++в рабочем процессе происходил segmentation fault, ++если перед ответом FastCGI-сервера было много вывода в stderr; ++ошибка появилась в 0.8.34.
++Спасибо Максиму Дунину. ++
++ ++a segmentation fault occurred in a worker process, ++if there was large stderr output before FastCGI response; ++the bug had appeared in 0.8.34.
++Thanks to Maxim Dounin. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++nginx не поддерживал все шифры, используемые в клиентских сертификатах.
++Спасибо Иннокентию Еникееву. ++
++ ++nginx did not support all ciphers and digests used in client certificates.
++Thanks to Innocenty Enikeew. ++
++
++ ++ ++ ++nginx неправильно кэшировал FastCGI-ответы, если перед ответом было ++много вывода в stderr. ++ ++ ++nginx cached incorrectly FastCGI responses if there was large stderr output ++before response. ++ ++ ++ ++ ++ ++nginx не поддерживал HTTPS-рефереры. ++ ++ ++nginx did not support HTTPS referrers. ++ ++ ++ ++ ++ ++nginx/Windows мог не находить файлы, если путь в конфигурации был задан ++в другом регистре; ++ошибка появилась в 0.8.33. ++ ++ ++nginx/Windows might not find file if path in configuration was given ++in other character case; ++the bug had appeared in 0.8.33. ++ ++ ++ ++ ++ ++переменная $date_local выдавала неверное время, ++если использовался формат "%s".
++Спасибо Максиму Дунину. ++
++ ++the $date_local variable has an incorrect value, ++if the "%s" format was used.
++Thanks to Maxim Dounin. ++
++
++ ++ ++ ++если ssl_session_cache не был установлен или установлен в none, ++то при проверке клиентского сертификаты могла происходить ++ошибка "session id context uninitialized"; ++ошибка появилась в 0.7.1. ++ ++ ++if ssl_session_cache was not set or was set to "none", ++then during client certificate verify ++the error "session id context uninitialized" might occur; ++the bug had appeared in 0.7.1. ++ ++ ++ ++ ++ ++geo-диапазон возвращал значение по умолчанию, если диапазон включал ++в себя одну и более сетей размером /16 и не начинался на границе сети ++размером /16. ++ ++ ++a geo range returned default value if the range included two or more ++/16 networks and did not begin at /16 network boundary. ++ ++ ++ ++ ++ ++блок, используемый в параметре stub в SSI-директиве include, ++выводился с MIME-типом "text/plain". ++ ++ ++a block used in a "stub" parameter of an "include" SSI directive ++was output with "text/plain" MIME type. ++ ++ ++ ++ ++ ++$r->sleep() не работал; ++ошибка появилась в 0.8.11. ++ ++ ++$r->sleep() did not work; ++the bug had appeared in 0.8.11. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++теперь nginx/Windows игнорирует пробелы в конце URI.
++Спасибо Dan Crowley, Core Security Technologies. ++
++ ++now nginx/Windows ignores trailing spaces in URI.
++Thanks to Dan Crowley, Core Security Technologies. ++
++
++ ++ ++ ++теперь nginx/Windows игнорирует короткие имена файлов.
++Спасибо Dan Crowley, Core Security Technologies. ++
++ ++now nginx/Windows ignores short files names.
++Thanks to Dan Crowley, Core Security Technologies. ++
++
++ ++ ++ ++теперь keepalive соединения после запросов POST не запрещаются для ++MSIE 7.0+.
++Спасибо Adam Lounds. ++
++ ++now keepalive connections after POST requests are not disabled for ++MSIE 7.0+.
++Thanks to Adam Lounds. ++
++
++ ++ ++ ++теперь keepalive соединения запрещены для Safari.
++Спасибо Joshua Sierles. ++
++ ++now keepalive connections are disabled for Safari.
++Thanks to Joshua Sierles. ++
++
++ ++ ++ ++если проксированный или FastCGI запрос внутренне перенаправлялся ++в другой проксированный или FastCGI location, то переменная ++$upstream_response_time могла иметь ненормально большое значение; ++ошибка появилась в 0.8.7. ++ ++ ++if a proxied or FastCGI request was internally redirected ++to another proxied or FastCGI location, ++then $upstream_response_time variable may have abnormally large value; ++the bug had appeared in 0.8.7. ++ ++ ++ ++ ++ ++в рабочем процессе мог произойти segmentation fault ++при отбрасывания тела запроса; ++ошибка появилась в 0.8.11. ++ ++ ++a segmentation fault might occur in a worker process, ++while discarding a request body; ++the bug had appeared in 0.8.11. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++ошибки при использовании кодировки UTF-8 в ngx_http_autoindex_module.
++Спасибо Максиму Дунину. ++
++ ++UTF-8 encoding usage in the ngx_http_autoindex_module.
++Thanks to Maxim Dounin. ++
++
++ ++ ++ ++именованные выделения в регулярных выражениях работали только для ++двух переменных.
++Спасибо Максиму Дунину. ++
++ ++regular expression named captures worked for two names only.
++Thanks to Maxim Dounin. ++
++
++ ++ ++ ++теперь в строке заголовка запроса "Host" используется имя "localhost", ++если в директиве auth_http указан unix domain сокет.
++Спасибо Максиму Дунину. ++
++ ++now the "localhost" name is used in the "Host" request header line, ++if an unix domain socket is defined in the "auth_http" directive.
++Thanks to Maxim Dounin. ++
++
++ ++ ++ ++nginx не поддерживал передачу chunk'ами для 201-ых ответов.
++Спасибо Julian Reich. ++
++ ++nginx did not support chunked transfer encoding for 201 responses.
++Thanks to Julian Reich. ++
++
++ ++ ++ ++если директива "expires modified" выставляла дату в прошлом, то в строке ++заголовка ответа "Cache-Control" выдавалось отрицательное число.
++Спасибо Алексею Капранову. ++
++ ++if the "expires modified" set date in the past, then a negative number ++was set in the "Cache-Control" response header line.
++Thanks to Alex Kapranoff. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++теперь директива error_page может перенаправлять ответы со статусом 301 и 302. ++ ++ ++now the "error_page" directive may redirect the 301 and 302 responses. ++ ++ ++ ++ ++ ++переменные $geoip_city_continent_code, $geoip_latitude и $geoip_longitude.
++Спасибо Arvind Sundararajan. ++
++ ++the $geoip_city_continent_code, $geoip_latitude, and $geoip_longitude ++variables.
++Thanks to Arvind Sundararajan. ++
++
++ ++ ++ ++модуль ngx_http_image_filter_module теперь всегда удаляет ++EXIF и другие данные, если они занимают больше 5% в JPEG-файле. ++ ++ ++now the ngx_http_image_filter_module deletes always EXIF and other ++application specific data if the data consume more than 5% of a JPEG file. ++ ++ ++ ++ ++ ++nginx закрывал соединение при запросе закэшированного ++ответа с пустым телом.
++Спасибо Piotr Sikora. ++
++ ++nginx closed a connection if a cached response had an empty body.
++Thanks to Piotr Sikora. ++
++
++ ++ ++ ++nginx мог не собираться gcc 4.x при использовании оптимизации -O2 и выше.
++Спасибо Максиму Дунину и Денису Латыпову. ++
++ ++nginx might not be built by gcc 4.x if the -O2 or higher optimization option ++was used.
++Thanks to Maxim Dounin and Denis F. Latypoff. ++
++
++ ++ ++ ++регулярные выражения в location всегда тестировались с учётом регистра; ++ошибка появилась в 0.8.25. ++ ++ ++regular expressions in location were always tested in case-sensitive mode; ++the bug had appeared in 0.8.25. ++ ++ ++ ++ ++ ++nginx кэшировал 304 ответ, если в заголовке проксируемого запроса ++была строка "If-None-Match".
++Спасибо Tim Dettrick и David Kostal. ++
++ ++nginx cached a 304 response if there was the "If-None-Match" header line ++in a proxied request.
++Thanks to Tim Dettrick and David Kostal. ++
++
++ ++ ++ ++nginx/Windows пытался дважды удалить временный файл ++при перезаписи уже существующего файла. ++ ++ ++nginx/Windows tried to delete a temporary file twice ++if the file should replace an already existent file. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++теперь по умолчанию размер буфера директивы large_client_header_buffers ++равен 8K.
++Спасибо Andrew Cholakian. ++
++ ++now the default buffer size of the "large_client_header_buffers" ++directive is 8K.
++Thanks to Andrew Cholakian. ++
++
++ ++ ++ ++файл conf/fastcgi.conf для простых конфигураций FastCGI. ++ ++ ++the conf/fastcgi.conf for simple FastCGI configurations. ++ ++ ++ ++ ++ ++nginx/Windows пытался дважды переименовать временный файл ++при перезаписи уже существующего файла. ++ ++ ++nginx/Windows tried to rename a temporary file twice if the file ++should replace an already existent file. ++ ++ ++ ++ ++ ++ошибки double free or corruption, возникающей, если имя хоста не было найдено; ++ошибка появилась в 0.8.22.
++Спасибо Константину Свисту. ++
++ ++of "double free or corruption" error issued if host could not be resolved; ++the bug had appeared in 0.8.22.
++Thanks to Konstantin Svist. ++
++
++ ++ ++ ++в использовании libatomic на некоторых платформах.
++Спасибо W-Mark Kubacki. ++
++ ++in libatomic usage on some platforms.
++Thanks to W-Mark Kubacki. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++теперь для проксируемых ответов HTTP/0.9 в лог пишется код ответа "009". ++ ++ ++now the "009" status code is written to an access log for proxied HTTP/0.9 ++responses. ++ ++ ++ ++ ++ ++директивы addition_types, charset_types, gzip_types, ssi_types, ++sub_filter_types и xslt_types поддерживают параметр "*". ++ ++ ++the "addition_types", "charset_types", "gzip_types", "ssi_types", ++"sub_filter_types", and "xslt_types" directives support an "*" parameter. ++ ++ ++ ++ ++ ++использование встроенных атомарных операций GCC 4.1+.
++Спасибо W-Mark Kubacki. ++
++ ++GCC 4.1+ built-in atomic operations usage.
++Thanks to W-Mark Kubacki. ++
++
++ ++ ++ ++параметр --with-libatomic[=DIR] в configure.
++Спасибо W-Mark Kubacki. ++
++ ++the --with-libatomic[=DIR] option in the configure.
++Thanks to W-Mark Kubacki. ++
++
++ ++ ++ ++listen unix domain сокет имели ограниченные права доступа. ++ ++ ++listen unix domain socket had limited access rights. ++ ++ ++ ++ ++ ++закэшированные ответы ответов HTTP/0.9 неправильно обрабатывались. ++ ++ ++cached HTTP/0.9 responses were handled incorrectly. ++ ++ ++ ++ ++ ++именованные выделения в регулярных выражениях, заданные как "?P<...>", ++не работали в директиве server_name.
++Спасибо Максиму Дунину. ++
++ ++regular expression named captures given by "?P<...>" did not work ++in a "server_name" directive.
++Thanks to Maxim Dounin. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++nginx не собирался с параметром --without-pcre; ++ошибка появилась в 0.8.25. ++ ++ ++nginx could not be built with the --without-pcre parameter; ++the bug had appeared in 0.8.25. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++регулярные выражения не работали в nginx/Windows; ++ошибка появилась в 0.8.25. ++ ++ ++regular expressions did not work in nginx/Windows; ++the bug had appeared in 0.8.25. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ошибки при использовании выделений в директиве rewrite; ++ошибка появилась в 0.8.25. ++ ++ ++in captures usage in "rewrite" directive; ++the bug had appeared in 0.8.25. ++ ++ ++ ++ ++ ++nginx не собирался без параметра --with-debug; ++ошибка появилась в 0.8.25. ++ ++ ++nginx could not be built without the --with-debug option; ++the bug had appeared in 0.8.25. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++теперь в лог ошибок не пишется сообщение, если переменная не найдена ++с помощью метода $r->variable(). ++ ++ ++now no message is written in an error log if a variable is not found by ++$r->variable() method. ++ ++ ++ ++ ++ ++модуль ngx_http_degradation_module. ++ ++ ++the ngx_http_degradation_module. ++ ++ ++ ++ ++ ++именованные выделения в регулярных выражениях. ++ ++ ++regular expression named captures. ++ ++ ++ ++ ++ ++теперь при использовании переменных в директиве proxy_pass не требуется ++задавать URI. ++ ++ ++now URI part is not required a "proxy_pass" directive if variables are used. ++ ++ ++ ++ ++ ++теперь директива msie_padding работает и для Chrome. ++ ++ ++now the "msie_padding" directive works for Chrome too. ++ ++ ++ ++ ++ ++в рабочем процессе происходил segmentation fault при недостатке памяти; ++ошибка появилась в 0.8.18. ++ ++ ++a segmentation fault occurred in a worker process on low memory condition; ++the bug had appeared in 0.8.18. ++ ++ ++ ++ ++ ++nginx передавал сжатые ответы клиентам, не поддерживающим сжатие, ++при настройках gzip_static on и gzip_vary off; ++ошибка появилась в 0.8.16. ++ ++ ++nginx sent gzipped responses to clients those do not support gzip, ++if "gzip_static on" and "gzip_vary off"; ++the bug had appeared in 0.8.16. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++nginx всегда добавлял строку "Content-Encoding: gzip" в заголовок ++304-ых ответов модуля ngx_http_gzip_static_module. ++ ++ ++nginx always added "Content-Encoding: gzip" response header line ++in 304 responses sent by ngx_http_gzip_static_module. ++ ++ ++ ++ ++ ++nginx не собирался без параметра --with-debug; ++ошибка появилась в 0.8.23. ++ ++ ++nginx could not be built without the --with-debug option; ++the bug had appeared in 0.8.23. ++ ++ ++ ++ ++ ++параметр "unix:" в директиве set_real_ip_from неправильно наследовался ++с предыдущего уровня. ++ ++ ++the "unix:" parameter of the "set_real_ip_from" directive inherited ++incorrectly from previous level. ++ ++ ++ ++ ++ ++в resolver'е при определении пустого имени. ++ ++ ++in resolving empty name. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++теперь SSL/TLS renegotiation запрещён.
++Спасибо Максиму Дунину. ++
++ ++now SSL/TLS renegotiation is disabled.
++Thanks to Maxim Dounin. ++
++
++ ++ ++ ++listen unix domain сокет не наследовался во время обновления без перерыва. ++ ++ ++listen unix domain socket did not inherit while online upgrade. ++ ++ ++ ++ ++ ++параметр "unix:" в директиве set_real_ip_from не работал без ещё ++одной директивы с любым IP-адресом. ++ ++ ++the "unix:" parameter of the "set_real_ip_from" directive did not without ++yet another directive with any IP address. ++ ++ ++ ++ ++ ++segmentation fault и зацикливания в resolver'е. ++ ++ ++segmentation fault and infinite looping in resolver. ++ ++ ++ ++ ++ ++в resolver'е.
++Спасибо Артёму Бохану. ++
++ ++in resolver.
++Thanks to Artem Bokhan. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++директивы proxy_bind, fastcgi_bind и memcached_bind. ++ ++ ++the "proxy_bind", "fastcgi_bind", and "memcached_bind" directives. ++ ++ ++ ++ ++ ++директивы access и deny поддерживают IPv6. ++ ++ ++the "access" and the "deny" directives support IPv6. ++ ++ ++ ++ ++ ++директива set_real_ip_from поддерживает IPv6 адреса в заголовках запроса. ++ ++ ++the "set_real_ip_from" directive supports IPv6 addresses in request headers. ++ ++ ++ ++ ++ ++параметр "unix:" в директиве set_real_ip_from. ++ ++ ++the "unix:" parameter of the "set_real_ip_from" directive. ++ ++ ++ ++ ++ ++nginx не удалял unix domain сокет после тестирования конфигурации. ++ ++ ++nginx did not delete unix domain socket after configuration testing. ++ ++ ++ ++ ++ ++nginx удалял unix domain сокет во время обновления без перерыва. ++ ++ ++nginx deleted unix domain socket while online upgrade. ++ ++ ++ ++ ++ ++оператор "!-x" не работал.
++Спасибо Максиму Дунину. ++
++ ++the "!-x" operator did not work.
++Thanks to Maxim Dounin. ++
++
++ ++ ++ ++в рабочем процессе мог произойти segmentation fault ++при использовании limit_rate в HTTPS сервере.
++Спасибо Максиму Дунину. ++
++ ++a segmentation fault might occur in a worker process, ++if limit_rate was used in HTTPS server.
++Thanks to Maxim Dounin. ++
++
++ ++ ++ ++при записи в лог переменной $limit_rate ++в рабочем процессе происходил segmentation fault.
++Спасибо Максиму Дунину. ++
++ ++a segmentation fault might occur in a worker process ++while $limit_rate logging.
++Thanks to Maxim Dounin. ++
++
++ ++ ++ ++в рабочем процессе мог произойти segmentation fault, ++если внутри блока server не было директивы listen; ++ошибка появилась в 0.8.21. ++ ++ ++a segmentation fault might occur in a worker process, ++if there was no "listen" directive in "server" block; ++the bug had appeared in 0.8.21. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++теперь ключ -V показывает статус поддержки TLS SNI. ++ ++ ++now the "-V" switch shows TLS SNI support. ++ ++ ++ ++ ++ ++директива listen модуля HTTP поддерживает unix domain сокеты.
++Спасибо Hongli Lai. ++
++ ++the "listen" directive of the HTTP module supports unix domain sockets.
++Thanks to Hongli Lai. ++
++
++ ++ ++ ++параметр "default_server" в директиве listen. ++ ++ ++the "default_server" parameter of the "listen" directive. ++ ++ ++ ++ ++ ++теперь параметр "default" не обязателен для установки параметров listen-сокета. ++ ++ ++now a "default" parameter is not required to set listen socket options. ++ ++ ++ ++ ++ ++nginx не поддерживал даты в 2038 году на 32-битных платформах; ++ ++ ++nginx did not support dates in 2038 year on 32-bit platforms; ++ ++ ++ ++ ++ ++утечки сокетов; ++ошибка появилась в 0.8.11. ++ ++ ++socket leak; ++the bug had appeared in 0.8.11. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++теперь по умолчанию используются следующие шифры SSL: "HIGH:!ADH:!MD5". ++ ++ ++now default SSL ciphers are "HIGH:!ADH:!MD5". ++ ++ ++ ++ ++ ++модуль ngx_http_autoindex_module не показывал последний слэш для линков ++на каталоги; ++ошибка появилась в 0.7.15. ++ ++ ++the ngx_http_autoindex_module did not show the trailing slash in links to ++a directory; ++the bug had appeared in 0.7.15. ++ ++ ++ ++ ++ ++nginx не закрывал лог, заданный параметром конфигурации --error-log-path; ++ошибка появилась в 0.7.53. ++ ++ ++nginx did not close a log file set by the --error-log-path configuration option; ++the bug had appeared in 0.7.53. ++ ++ ++ ++ ++ ++nginx не считал запятую разделителем в строке "Cache-Control" в ++заголовке ответа бэкенда. ++ ++ ++nginx did not treat a comma as separator in the "Cache-Control" backend response ++header line. ++ ++ ++ ++ ++ ++nginx/Windows мог не создать временный файл, файл в кэше или файл ++с помощью директив proxy/fastcgi_store, если рабочий процесс не имел ++достаточно прав для работы с каталогами верхнего уровня. ++ ++ ++nginx/Windows might not create temporary file, a cache file, or ++"proxy/fastcgi_store"d file if a worker had no enough access rights ++for top level directories. ++ ++ ++ ++ ++ ++строки "Set-Cookie" и "P3P" в заголовке ответа FastCGI-сервера не скрывались ++при кэшировании, если не использовались директивы fastcgi_hide_header ++с любыми параметрами. ++ ++ ++the "Set-Cookie" and "P3P" FastCGI response header lines were not hidden ++while caching if no "fastcgi_hide_header" directives were used with ++any parameters. ++ ++ ++ ++ ++ ++nginx неверно считал размер кэша на диске. ++ ++ ++nginx counted incorrectly disk cache size. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++теперь протокол SSLv2 по умолчанию запрещён. ++ ++ ++now SSLv2 protocol is disabled by default. ++ ++ ++ ++ ++ ++теперь по умолчанию используются следующие шифры SSL: ++"ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM". ++ ++ ++now default SSL ciphers are "ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM". ++ ++ ++ ++ ++ ++директива limit_req не работала; ++ошибка появилась в 0.8.18. ++ ++ ++a "limit_req" directive did not work; ++the bug had appeared in 0.8.18. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директива read_ahead. ++ ++ ++the "read_ahead" directive. ++ ++ ++ ++ ++ ++теперь можно использовать несколько директив perl_modules. ++ ++ ++now several "perl_modules" directives may be used. ++ ++ ++ ++ ++ ++директивы limit_req_log_level и limit_conn_log_level. ++ ++ ++the "limit_req_log_level" and "limit_conn_log_level" directives. ++ ++ ++ ++ ++ ++теперь директива limit_req соответствует алгоритму leaky bucket.
++Спасибо Максиму Дунину. ++
++ ++now "limit_req" directive conforms to the leaky bucket algorithm.
++Thanks to Maxim Dounin. ++
++
++ ++ ++ ++nginx не работал на Linux/sparc.
++Спасибо Marcus Ramberg. ++
++ ++nginx did not work on Linux/sparc.
++Thanks to Marcus Ramberg. ++
++
++ ++ ++ ++nginx слал символ '\0' в строке "Location" в заголовке в ответе на запрос ++MKCOL.
++Спасибо Xie Zhenye. ++
++ ++nginx sent '\0' in a "Location" response header line on MKCOL request.
++Thanks to Xie Zhenye. ++
++
++ ++ ++ ++вместо кода ответа 499 в лог записывался код 0; ++ошибка появилась в 0.8.11. ++ ++ ++zero status code was logged instead of 499 status code; ++the bug had appeared in 0.8.11. ++ ++ ++ ++ ++ ++утечки сокетов; ++ошибка появилась в 0.8.11. ++ ++ ++socket leak; ++the bug had appeared in 0.8.11. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++теперь символы "/../" запрещены в строке "Destination" в заголовке запроса. ++ ++ ++now "/../" are disabled in "Destination" request header line. ++ ++ ++ ++ ++ ++теперь значение переменной $host всегда в нижнем регистре. ++ ++ ++now $host variable value is always low case. ++ ++ ++ ++ ++ ++переменная $ssl_session_id. ++ ++ ++the $ssl_session_id variable. ++ ++ ++ ++ ++ ++утечки сокетов; ++ошибка появилась в 0.8.11. ++ ++ ++socket leak; ++the bug had appeared in 0.8.11. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директива image_filter_transparency. ++ ++ ++the "image_filter_transparency" directive. ++ ++ ++ ++ ++ ++директива "addition_types" была неверно названа "addtion_types". ++ ++ ++"addition_types" directive was incorrectly named "addtion_types". ++ ++ ++ ++ ++ ++порчи кэша resolver'а.
++Спасибо Matthew Dempsky. ++
++ ++resolver cache poisoning.
++Thanks to Matthew Dempsky. ++
++
++ ++ ++ ++утечки памяти в resolver'е.
++Спасибо Matthew Dempsky. ++
++ ++memory leak in resolver.
++Thanks to Matthew Dempsky. ++
++
++ ++ ++ ++неверная строка запроса в переменной $request записывалась в access_log ++только при использовании error_log на уровне info или debug. ++ ++ ++invalid request line in $request variable was written in access_log ++only if error_log was set to "info" or "debug" level. ++ ++ ++ ++ ++ ++в поддержке альфа-канала PNG в модуле ngx_http_image_filter_module. ++ ++ ++in PNG alpha-channel support in the ngx_http_image_filter_module. ++ ++ ++ ++ ++ ++nginx всегда добавлял строку "Vary: Accept-Encoding" в заголовок ответа, ++если обе директивы gzip_static и gzip_vary были включены. ++ ++ ++nginx always added "Vary: Accept-Encoding" response header line, ++if both "gzip_static" and "gzip_vary" were on. ++ ++ ++ ++ ++ ++в поддержке кодировки UTF-8 директивой try_files в nginx/Windows. ++ ++ ++in UTF-8 encoding support by "try_files" directive in nginx/Windows. ++ ++ ++ ++ ++ ++ошибки при использовании post_action; ++ошибка появилась в 0.8.11.
++Спасибо Игорю Артемьеву. ++
++ ++in "post_action" directive usage; ++the bug had appeared in 0.8.11.
++Thanks to Igor Artemiev. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++при обработке специально созданного запроса ++в рабочем процессе мог произойти segmentation fault.
++Спасибо Chris Ries. ++
++ ++a segmentation fault might occur in worker process ++while specially crafted request handling.
++Thanks to Chris Ries. ++
++
++ ++ ++ ++если были описаны имена .domain.tld, .sub.domain.tld и .domain-some.tld, ++то имя .sub.domain.tld попадало под маску .domain.tld. ++ ++ ++if names .domain.tld, .sub.domain.tld, and .domain-some.tld were defined, ++then the name .sub.domain.tld was matched by .domain.tld. ++ ++ ++ ++ ++ ++в поддержке прозрачности в модуле ngx_http_image_filter_module. ++ ++ ++in transparency support in the ngx_http_image_filter_module. ++ ++ ++ ++ ++ ++в файловом AIO. ++ ++ ++in file AIO. ++ ++ ++ ++ ++ ++ошибки при использовании X-Accel-Redirect; ++ошибка появилась в 0.8.11. ++ ++ ++in X-Accel-Redirect usage; ++the bug had appeared in 0.8.11. ++ ++ ++ ++ ++ ++ошибки при использовании встроенного перла; ++ошибка появилась в 0.8.11. ++ ++ ++in embedded perl module; ++the bug had appeared in 0.8.11. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++устаревший закэшированный запрос мог залипнуть в состоянии "UPDATING". ++ ++ ++an expired cached response might stick in the "UPDATING" state. ++ ++ ++ ++ ++ ++при использовании error_log на уровне info или debug ++в рабочем процессе мог произойти segmentation fault.
++Спасибо Сергею Боченкову. ++
++ ++a segmentation fault might occur in worker process, ++if error_log was set to info or debug level.
++Thanks to Sergey Bochenkov. ++
++
++ ++ ++ ++ошибки при использовании встроенного перла; ++ошибка появилась в 0.8.11. ++ ++ ++in embedded perl module; ++the bug had appeared in 0.8.11. ++ ++ ++ ++ ++ ++директива error_page не перенаправляла ошибку 413; ++ошибка появилась в 0.6.10. ++ ++ ++an "error_page" directive did not redirect a 413 error; ++the bug had appeared in 0.6.10. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++в директиве "aio sendfile"; ++ошибка появилась в 0.8.12. ++ ++ ++in the "aio sendfile" directive; ++the bug had appeared in 0.8.12. ++ ++ ++ ++ ++ ++nginx не собирался без параметра --with-file-aio на FreeBSD; ++ошибка появилась в 0.8.12. ++ ++ ++nginx could not be built without the --with-file-aio option on FreeBSD; ++the bug had appeared in 0.8.12. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++параметр sendfile в директиве aio во FreeBSD. ++ ++ ++the "sendfile" parameter in the "aio" directive on FreeBSD. ++ ++ ++ ++ ++ ++ошибки при использовании try_files; ++ошибка появилась в 0.8.11. ++ ++ ++in try_files; ++the bug had appeared in 0.8.11. ++ ++ ++ ++ ++ ++ошибки при использовании memcached; ++ошибка появилась в 0.8.11. ++ ++ ++in memcached; ++the bug had appeared in 0.8.11. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++теперь директива "gzip_disable msie6" не запрещает сжатие для ++MSIE 6.0 SV1. ++ ++ ++now directive "gzip_disable msie6" does not disable gzipping for ++MSIE 6.0 SV1. ++ ++ ++ ++ ++ ++поддержка файлового AIO во FreeBSD и Linux. ++ ++ ++file AIO support on FreeBSD and Linux. ++ ++ ++ ++ ++ ++директива directio_alignment. ++ ++ ++the "directio_alignment" directive. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++утечек памяти при использовании базы GeoIP City. ++ ++ ++memory leaks if GeoIP City database was used. ++ ++ ++ ++ ++ ++ошибки при копировании временных файлов в постоянное место хранения; ++ошибка появилась в 0.8.9. ++ ++ ++in copying temporary files to permanent storage area; ++the bug had appeared in 0.8.9. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++теперь стартовый загрузчик кэша работает в отдельном процесс; ++это должно улучшить обработку больших кэшей. ++ ++ ++now the start cache loader runs in a separate process; ++this should improve large caches handling. ++ ++ ++ ++ ++ ++теперь временные файлы и постоянное место хранения могут располагаться ++на разных файловых системах. ++ ++ ++now temporary files and permanent storage area may reside at ++different file systems. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++в обработке заголовков ответа, разделённых в FastCGI-записях. ++ ++ ++in handling FastCGI headers split in records. ++ ++ ++ ++ ++ ++если запрос обрабатывался в двух проксированных или FastCGI location'ах ++и в первом из них использовалось кэширование, ++то в рабочем процессе происходил segmentation fault; ++ошибка появилась в 0.8.7. ++ ++ ++a segmentation fault occurred in worker process, ++if a request was handled in two proxied or FastCGIed locations ++and a caching was enabled in the first location; ++the bug had appeared in 0.8.7. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++минимальная поддерживаемая версия OpenSSL—0.9.7. ++ ++ ++minimum supported OpenSSL version is 0.9.7. ++ ++ ++ ++ ++ ++параметр ask директивы ssl_verify_client изменён на параметр optional ++и теперь он проверяет клиентский сертификат, если он был предложен.
++Спасибо Brice Figureau. ++
++ ++the "ask" parameter of the "ssl_verify_client" directive was changed ++to the "optional" parameter and now it checks a client certificate if it was ++offered.
++Thanks to Brice Figureau. ++
++
++ ++ ++ ++переменная $ssl_client_verify.
++Спасибо Brice Figureau. ++
++ ++the $ssl_client_verify variable.
++Thanks to Brice Figureau. ++
++
++ ++ ++ ++директива ssl_crl.
++Спасибо Brice Figureau. ++
++ ++the "ssl_crl" directive.
++Thanks to Brice Figureau. ++
++
++ ++ ++ ++параметр proxy директивы geo. ++ ++ ++the "proxy" parameter of the "geo" directive. ++ ++ ++ ++ ++ ++директива image_filter поддерживает переменные для задания размеров. ++ ++ ++the "image_filter" directive supports variables for setting size. ++ ++ ++ ++ ++ ++использование переменной $ssl_client_cert портило память; ++ошибка появилась в 0.7.7.
++Спасибо Сергею Журавлёву. ++
++ ++the $ssl_client_cert variable usage corrupted memory; ++the bug had appeared in 0.7.7.
++Thanks to Sergey Zhuravlev. ++
++
++ ++ ++ ++директивы proxy_pass_header и fastcgi_pass_header" не передавали клиенту ++строки "X-Accel-Redirect", "X-Accel-Limit-Rate", "X-Accel-Buffering" и ++"X-Accel-Charset" из заголовка ответа бэкенда.
++Спасибо Максиму Дунину. ++
++ ++"proxy_pass_header" and "fastcgi_pass_header" directives did not pass to ++a client the "X-Accel-Redirect", "X-Accel-Limit-Rate", "X-Accel-Buffering", ++and "X-Accel-Charset" lines from backend response header.
++Thanks to Maxim Dounin. ++
++
++ ++ ++ ++в обработке строк "Last-Modified" и "Accept-Ranges" в заголовке ответа бэкенда; ++ошибка появилась в 0.7.44.
++Спасибо Максиму Дунину. ++
++ ++in handling "Last-Modified" and "Accept-Ranges" backend response header lines; ++the bug had appeared in 0.7.44.
++Thanks to Maxim Dounin. ++
++
++ ++ ++ ++ошибки "[alert] zero size buf" при получении пустых ответы в подзапросах; ++ошибка появилась в 0.8.5. ++ ++ ++the "[alert] zero size buf" error if subrequest returns an empty response; ++the bug had appeared in 0.8.5. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++модуль ngx_http_geoip_module. ++ ++ ++the ngx_http_geoip_module. ++ ++ ++ ++ ++ ++XSLT-фильтр мог выдавать ошибку "not well formed XML document" для ++правильного документа.
++Спасибо Kuramoto Eiji. ++
++ ++XSLT filter may fail with message "not well formed XML document" ++for valid XML document.
++Thanks to Kuramoto Eiji. ++
++
++ ++ ++ ++в MacOSX, Cygwin и nginx/Windows при проверке location'ов, заданных ++регулярным выражением, теперь всегда делается сравнение без учёта ++регистра символов. ++ ++ ++now in MacOSX, Cygwin, and nginx/Windows locations given by a regular ++expression are always tested in case insensitive mode. ++ ++ ++ ++ ++ ++теперь nginx/Windows игнорирует точки в конце URI.
++Спасибо Hugo Leisink. ++
++ ++now nginx/Windows ignores trailing dots in URI.
++Thanks to Hugo Leisink. ++
++
++ ++ ++ ++имя файла указанного в --conf-path игнорировалось при установке; ++ошибка появилась в 0.6.6.
++Спасибо Максиму Дунину. ++
++ ++name of file specified in --conf-path was not honored during installation; ++the bug had appeared in 0.6.6.
++Thanks to Maxim Dounin. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++теперь nginx разрешает подчёркивания в методе запроса. ++ ++ ++now nginx allows underscores in a request method. ++ ++ ++ ++ ++ ++при использовании HTTP Basic-аутентификации на Windows ++для неверных имени/пароля возвращалась 500-ая ошибка. ++ ++ ++a 500 error code was returned for invalid login/password while HTTP ++Basic authentication on Windows. ++ ++ ++ ++ ++ ++ответы модуля ngx_http_perl_module не работали в подзапросах. ++ ++ ++ngx_http_perl_module responses did not work in subrequests. ++ ++ ++ ++ ++ ++в модуле ngx_http_limit_req_module.
++Спасибо Максиму Дунину. ++
++ ++in ngx_http_limit_req_module.
++Thanks to Maxim Dounin. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++nginx не собирался с параметром --without-http-cache; ++ошибка появилась в 0.8.3. ++ ++ ++nginx could not be built --without-http-cache; ++the bug had appeared in 0.8.3. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++переменная $upstream_cache_status. ++ ++ ++the $upstream_cache_status variable. ++ ++ ++ ++ ++ ++nginx не собирался на MacOSX 10.6. ++ ++ ++nginx could not be built on MacOSX 10.6. ++ ++ ++ ++ ++ ++nginx не собирался с параметром --without-http-cache; ++ошибка появилась в 0.8.2. ++ ++ ++nginx could not be built --without-http-cache; ++the bug had appeared in 0.8.2. ++ ++ ++ ++ ++ ++если использовался перехват 401 ошибки от бэкенда и бэкенд ++не возвращал строку "WWW-Authenticate" в заголовке ответа, ++то в рабочем процессе происходил segmentation fault.
++Спасибо Евгению Мычло. ++
++ ++a segmentation fault occurred in worker process, ++if a backend 401 error was intercepted and the backend did not set ++the "WWW-Authenticate" response header line.
++Thanks to Eugene Mychlo. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++во взаимодействии open_file_cache и proxy/fastcgi кэша на старте. ++ ++ ++in open_file_cache and proxy/fastcgi cache interaction on start up. ++ ++ ++ ++ ++ ++open_file_cache мог кэшировать открытые файлы очень долго; ++ошибка появилась в 0.7.4. ++ ++ ++open_file_cache might cache open file descriptors too long; ++the bug had appeared in 0.7.4. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++параметр updating в директивах proxy_cache_use_stale и fastcgi_cache_use_stale. ++ ++ ++the "updating" parameter in "proxy_cache_use_stale" and ++"fastcgi_cache_use_stale" directives. ++ ++ ++ ++ ++ ++строки "If-Modified-Since", "If-Range" и им подобные в заголовке запроса ++клиента передавались бэкенду при кэшировании, если не использовалась ++директива proxy_set_header с любыми параметрами. ++ ++ ++the "If-Modified-Since", "If-Range", etc. client request header lines ++were passed to backend while caching if no "proxy_set_header" directive ++was used with any parameters. ++ ++ ++ ++ ++ ++строки "Set-Cookie" и "P3P" в заголовке ответа бэкенда не скрывались ++при кэшировании, если не использовались директивы ++proxy_hide_header/fastcgi_hide_header с любыми параметрами. ++ ++ ++the "Set-Cookie" and "P3P" response header lines were not hidden while caching ++if no "proxy_hide_header/fastcgi_hide_header" directives were used with ++any parameters. ++ ++ ++ ++ ++ ++модуль ngx_http_image_filter_module не понимал формат GIF87a.
++Спасибо Денису Ильиных. ++
++ ++the ngx_http_image_filter_module did not support GIF87a format.
++Thanks to Denis Ilyinyh. ++
++
++ ++ ++ ++nginx не собирался на Solaris 10 и более ранних; ++ошибка появилась в 0.7.56. ++ ++ ++nginx could not be built modules on Solaris 10 and early; ++the bug had appeared in 0.7.56. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++директива keepalive_requests. ++ ++ ++the "keepalive_requests" directive. ++ ++ ++ ++ ++ ++директива limit_rate_after.
++Спасибо Ivan Debnar. ++
++ ++the "limit_rate_after" directive.
++Thanks to Ivan Debnar. ++
++
++ ++ ++ ++XSLT-фильтр не работал в подзапросах. ++ ++ ++XLST filter did not work in subrequests. ++ ++ ++ ++ ++ ++обработке относительных путей в nginx/Windows. ++ ++ ++in relative paths handling in nginx/Windows. ++ ++ ++ ++ ++ ++в proxy_store, fastcgi_store, proxy_cache и fastcgi_cache в nginx/Windows. ++ ++ ++in proxy_store, fastcgi_store, proxy_cache, and fastcgi_cache in nginx/Windows. ++ ++ ++ ++ ++ ++в обработке ошибок выделения памяти.
++Спасибо Максиму Дунину и Кириллу Коринскому. ++
++ ++in memory allocation error handling.
++Thanks to Maxim Dounin and Kirill A. Korinskiy. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++директивы proxy_cache_methods и fastcgi_cache_methods. ++ ++ ++the "proxy_cache_methods" and "fastcgi_cache_methods" directives. ++ ++ ++ ++ ++ ++утечки сокетов; ++ошибка появилась в 0.7.25.
++Спасибо Максиму Дунину. ++
++ ++socket leak; ++the bug had appeared in 0.7.25.
++Thanks to Maxim Dounin. ++
++
++ ++ ++ ++при использовании переменной $request_body ++в рабочем процессе происходил segmentation fault, ++если в запросе не было тела; ++ошибка появилась в 0.7.58. ++ ++ ++a segmentation fault occurred in worker process, ++if a request had no body and the $request_body ++variable was used;
++the bug had appeared in 0.7.58. ++
++
++ ++ ++ ++SSL-модули могли не собираться на Solaris и Linux; ++ошибка появилась в 0.7.56. ++ ++ ++the SSL modules might not built on Solaris and Linux;
++the bug had appeared in 0.7.56. ++
++
++ ++ ++ ++ответы модуля ngx_http_xslt_filter_module не обрабатывались ++SSI-, charset- и gzip-фильтрами. ++ ++ ++ngx_http_xslt_filter_module responses were not handled by SSI, charset, ++and gzip filters. ++ ++ ++ ++ ++ ++директива charset не ставила кодировку для ответов модуля ++ngx_http_gzip_static_module. ++ ++ ++a "charset" directive did not set a charset to ngx_http_gzip_static_module ++responses. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++директива listen почтового прокси-сервера поддерживает IPv6. ++ ++ ++a "listen" directive of the mail proxy module supports IPv6. ++ ++ ++ ++ ++ ++директива image_filter_jpeg_quality. ++ ++ ++the "image_filter_jpeg_quality" directive. ++ ++ ++ ++ ++ ++директива client_body_in_single_buffer. ++ ++ ++the "client_body_in_single_buffer" directive. ++ ++ ++ ++ ++ ++переменная $request_body. ++ ++ ++the $request_body variable. ++ ++ ++ ++ ++ ++в модуле ngx_http_autoindex_module в ссылках на имена файлов, ++содержащих символ ":". ++ ++ ++in ngx_http_autoindex_module in file name links ++having a ":" symbol in the name. ++ ++ ++ ++ ++ ++процедура "make upgrade" не работала; ++ошибка появилась в 0.7.53.
++Спасибо Денису Латыпову. ++
++ ++"make upgrade" procedure did not work; ++the bug had appeared in 0.7.53.
++Thanks to Denis F. Latypoff. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++при перенаправлении ошибок модуля ngx_http_image_filter_module ++в именованный location в рабочем процессе происходил floating-point fault; ++ошибка появилась в 0.7.56. ++ ++ ++a floating-point fault occurred in worker process, ++if the ngx_http_image_filter_module errors were redirected to named location; ++the bug had appeared in 0.7.56. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++nginx/Windows поддерживает IPv6 в директиве listen модуля HTTP. ++ ++ ++nginx/Windows supports IPv6 in a "listen" directive of the HTTP module. ++ ++ ++ ++ ++ ++в модуле ngx_http_image_filter_module. ++ ++ ++in ngx_http_image_filter_module. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++параметры http_XXX в директивах proxy_cache_use_stale ++и fastcgi_cache_use_stale не работали. ++ ++ ++the http_XXX parameters in "proxy_cache_use_stale" and ++"fastcgi_cache_use_stale" directives did not work. ++ ++ ++ ++ ++ ++fastcgi кэш не кэшировал ответы, состоящие только из заголовка. ++ ++ ++fastcgi cache did not cache header only responses. ++ ++ ++ ++ ++ ++ошибки "select() failed (9: Bad file descriptor)" в nginx/Unix ++и "select() failed (10038: ...)" в nginx/Windows. ++ ++ ++of "select() failed (9: Bad file descriptor)" error in nginx/Unix ++and "select() failed (10038: ...)" error in nginx/Windows. ++ ++ ++ ++ ++ ++при использовании директивы debug_connection ++в рабочем процессе мог произойти segmentation fault; ++ошибка появилась в 0.7.54. ++ ++ ++a segmentation fault might occur in worker process, ++if an "debug_connection" directive was used; ++the bug had appeared in 0.7.54. ++ ++ ++ ++ ++ ++в сборке модуля ngx_http_image_filter_module. ++ ++ ++fix ngx_http_image_filter_module building errors. ++ ++ ++ ++ ++ ++файлы больше 2G не передавались с использованием $r->sendfile.
++Спасибо Максиму Дунину. ++
++ ++the files bigger than 2G could not be transferred using $r->sendfile.
++Thanks to Maxim Dounin. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++модуль ngx_http_image_filter_module. ++ ++ ++the ngx_http_image_filter_module. ++ ++ ++ ++ ++ ++директивы proxy_ignore_headers и fastcgi_ignore_headers. ++ ++ ++the "proxy_ignore_headers" and "fastcgi_ignore_headers" directives. ++ ++ ++ ++ ++ ++при использовании переменных "open_file_cache_errors on" ++в рабочем процессе мог произойти segmentation fault; ++ошибка появилась в 0.7.53. ++ ++ ++a segmentation fault might occur in worker process, ++if an "open_file_cache_errors off" directive was used; ++the bug had appeared in 0.7.53. ++ ++ ++ ++ ++ ++директива "port_in_redirect off" не работала; ++ошибка появилась в 0.7.39. ++ ++ ++the "port_in_redirect off" directive did not work; ++the bug had appeared in 0.7.39. ++ ++ ++ ++ ++ ++улучшение обработки ошибок метода select. ++ ++ ++improve handling of "select" method errors. ++ ++ ++ ++ ++ ++ошибки "select() failed (10022: ...)" в nginx/Windows. ++ ++ ++of "select() failed (10022: ...)" error in nginx/Windows. ++ ++ ++ ++ ++ ++в текстовых сообщениях об ошибках в nginx/Windows; ++ошибка появилась в 0.7.53. ++ ++ ++in error text descriptions in nginx/Windows; ++the bug had appeared in 0.7.53. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++теперь лог, указанный в --error-log-path, создаётся с самого начала работы. ++ ++ ++now a log set by --error-log-path is created from the very start-up. ++ ++ ++ ++ ++ ++теперь ошибки и предупреждения при старте записываются в error_log ++и выводятся на stderr. ++ ++ ++now the start up errors and warnings are outputted to an error_log and stderr. ++ ++ ++ ++ ++ ++при сборке с пустым параметром --prefix= nginx использует как префикс каталог, ++в котором он был запущен. ++ ++ ++the empty --prefix= configure parameter forces nginx to use a directory ++where it was run as prefix. ++ ++ ++ ++ ++ ++ключ -p. ++ ++ ++the -p switch. ++ ++ ++ ++ ++ ++ключ -s на Unix-платформах. ++ ++ ++the -s switch on Unix platforms. ++ ++ ++ ++ ++ ++ключи -? и -h.
++Спасибо Jerome Loyet. ++
++ ++the -? and -h switches.
++Thanks to Jerome Loyet. ++
++
++ ++ ++ ++теперь ключи можно задавать в сжатой форме. ++ ++ ++now switches may be set in condensed form. ++ ++ ++ ++ ++ ++nginx/Windows не работал, если файл конфигурации был задан ключом -c. ++ ++ ++nginx/Windows did not work if configuration file was given by the -c switch. ++ ++ ++ ++ ++ ++при использовании директив proxy_store, fastcgi_store, ++proxy_cache или fastcgi_cache временные файлы могли не удаляться.
++Спасибо Максиму Дунину. ++
++ ++temporary files might be not removed if the "proxy_store", "fastcgi_store", ++"proxy_cache", or "fastcgi_cache" were used.
++Thanks to Maxim Dounin. ++
++
++ ++ ++ ++в заголовке Auth-Method запроса серверу аутентификации почтового ++прокси-сервера передавалось неверное значение; ++ошибка появилась в 0.7.34.
++Спасибо Simon Lecaille. ++
++ ++an incorrect value was passed to mail proxy authentication server ++in "Auth-Method" header line; ++the bug had appeared
++in 0.7.34.
++Thanks to Simon Lecaille. ++
++
++ ++ ++ ++при логгировании на Linux не писались текстовые описания системных ошибок; ++ошибка появилась в 0.7.45. ++ ++ ++system error text descriptions were not logged on Linux;
++the bug had appeared in 0.7.45. ++
++
++ ++ ++ ++директива fastcgi_cache_min_uses не работала.
++Спасибо Андрею Воробьёву. ++
++ ++the "fastcgi_cache_min_uses" directive did not work.
++Thanks to Andrew Vorobyoff. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++первая бинарная версия под Windows. ++ ++ ++the first native Windows binary release. ++ ++ ++ ++ ++ ++корректная обработка метода HEAD при кэшировании. ++ ++ ++in processing HEAD method while caching. ++ ++ ++ ++ ++ ++корректная обработка строк "If-Modified-Since", "If-Range" и им подобных ++в заголовке запроса клиента при кэшировании. ++ ++ ++in processing the "If-Modified-Since", "If-Range", etc. client request ++header lines while caching. ++ ++ ++ ++ ++ ++теперь строки "Set-Cookie" и "P3P" скрываются в заголовке ответа ++для закэшированных ответов. ++ ++ ++now the "Set-Cookie" and "P3P" header lines are hidden in cacheable responses. ++ ++ ++ ++ ++ ++если nginx был собран с модулем ngx_http_perl_module и perl ++поддерживал потоки, то при выходе основного процесса ++могла выдаваться ошибка "panic: MUTEX_LOCK". ++ ++ ++if nginx was built with the ngx_http_perl_module and with a perl which ++supports threads, then during a master process exit ++the message "panic: MUTEX_LOCK" might be issued. ++ ++ ++ ++ ++ ++nginx не собирался с параметром --without-http-cache; ++ошибка появилась в 0.7.48. ++ ++ ++nginx could not be built --without-http-cache; ++the bug had appeared in 0.7.48. ++ ++ ++ ++ ++ ++nginx не собирался на платформах, отличных от i386, amd64, sparc и ppc; ++ошибка появилась в 0.7.42. ++ ++ ++nginx could not be built on platforms different from i386, amd64, sparc, ++and ppc; ++the bug had appeared in 0.7.42. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директива try_files поддерживает код ответа в последнем параметре. ++ ++ ++the "try_files" directive supports a response code in the fallback parameter. ++ ++ ++ ++ ++ ++теперь в директиве return можно использовать любой код ответа. ++ ++ ++now any response code can be used in the "return" directive. ++ ++ ++ ++ ++ ++директива error_page делала внешний редирект без строки запроса; ++ошибка появилась в 0.7.44. ++ ++ ++the "error_page" directive made an external redirect without query string; ++the bug had appeared in 0.7.44. ++ ++ ++ ++ ++ ++если сервера слушали на нескольких явно описанных адресах, ++то виртуальные сервера могли не работать; ++ошибка появилась в 0.7.39. ++ ++ ++if servers listened on several defined explicitly addresses, ++then virtual servers might not work; ++the bug had appeared in 0.7.39. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++переменные $arg_... не работали; ++ошибка появилась в 0.7.49. ++ ++ ++the $arg_... variables did not work; ++the bug had appeared in 0.7.49. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++при использовании переменных $arg_... ++в рабочем процессе мог произойти segmentation fault; ++ошибка появилась в 0.7.48. ++ ++ ++a segmentation fault might occur in worker process, ++if the $arg_... variables were used; ++the bug had appeared in 0.7.48. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директива proxy_cache_key. ++ ++ ++the "proxy_cache_key" directive. ++ ++ ++ ++ ++ ++теперь nginx учитывает при кэшировании строки "X-Accel-Expires", ++"Expires" и "Cache-Control" в заголовке ответа бэкенда. ++ ++ ++now nginx takes into account the "X-Accel-Expires", "Expires", and ++"Cache-Control" header lines in a backend response. ++ ++ ++ ++ ++ ++теперь nginx кэширует только ответы на запросы GET. ++ ++ ++now nginx caches responses for the GET requests only. ++ ++ ++ ++ ++ ++директива fastcgi_cache_key не наследовалась. ++ ++ ++the "fastcgi_cache_key" directive was not inherited. ++ ++ ++ ++ ++ ++переменные $arg_... не работали с SSI-подзапросами.
++Спасибо Максиму Дунину. ++
++ ++the $arg_... variables did not work with SSI subrequests.
++Thanks to Maxim Dounin. ++
++
++ ++ ++ ++nginx не собирался с библиотекой uclibc.
++Спасибо Timothy Redaelli. ++
++ ++nginx could not be built with uclibc library.
++Thanks to Timothy Redaelli. ++
++
++ ++ ++ ++nginx не собирался на OpenBSD; ++ошибка появилась в 0.7.46. ++ ++ ++nginx could not be built on OpenBSD; ++the bug had appeared in 0.7.46. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++nginx не собирался на FreeBSD 6 и более ранних версиях; ++ошибка появилась в 0.7.46. ++ ++ ++nginx could not be built on FreeBSD 6 and early versions; ++the bug had appeared in 0.7.46. ++ ++ ++ ++ ++ ++nginx не собирался на MacOSX; ++ошибка появилась в 0.7.46. ++ ++ ++nginx could not be built on MacOSX; ++the bug had appeared in 0.7.46. ++ ++ ++ ++ ++ ++если использовался параметр max_size, то cache manager мог удалить весь кэш; ++ошибка появилась в 0.7.46. ++ ++ ++if the "max_size" parameter was set, then the cache manager might purge ++a whole cache; ++the bug had appeared in 0.7.46. ++ ++ ++ ++ ++ ++в рабочем процессе мог произойти segmentation fault, ++если директивы proxy_cache/fastcgi_cache ++и proxy_cache_valid/ fastcgi_cache_valid не были заданы на одном уровне; ++ошибка появилась в 0.7.46. ++ ++ ++a segmentation fault might occur in worker process, ++if the "proxy_cache"/"fastcgi_cache" and ++the "proxy_cache_valid"/ "fastcgi_cache_valid" were set on different levels; ++the bug had appeared in 0.7.46. ++ ++ ++ ++ ++ ++в рабочем процессе мог произойти segmentation fault ++при перенаправлении запроса проксированному или FastCGI-серверу ++с помощью error_page или try_files; ++ошибка появилась в 0.7.44. ++ ++ ++a segmentation fault might occur in worker process, ++if a request was redirected to a proxied or FastCGI server via ++error_page or try_files; ++the bug had appeared in 0.7.44. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++архив предыдущего релиза был неверным. ++ ++ ++the previous release tarball was incorrect. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++теперь директивы proxy_cache и proxy_cache_valid можно задавать ++на разных уровнях. ++ ++ ++now the "proxy_cache" and the "proxy_cache_valid" directives can be set on ++different levels. ++ ++ ++ ++ ++ ++параметр clean_time в директиве proxy_cache_path удалён. ++ ++ ++the "clean_time" parameter of the "proxy_cache_path" directive is canceled. ++ ++ ++ ++ ++ ++параметр max_size в директиве proxy_cache_path. ++ ++ ++the "max_size" parameter of the "proxy_cache_path" directive. ++ ++ ++ ++ ++ ++предварительная поддержка кэширования в модуле ngx_http_fastcgi_module. ++ ++ ++the ngx_http_fastcgi_module preliminary cache support. ++ ++ ++ ++ ++ ++теперь при ошибках выделения в разделяемой памяти в логе указываются ++названия директивы и зоны. ++ ++ ++now on shared memory allocation errors directive and zone names are logged. ++ ++ ++ ++ ++ ++директива "add_header last-modified ''" не удаляла в заголовке ответа ++строку "Last-Modified"; ++ошибка появилась в 0.7.44. ++ ++ ++the directive "add_header last-modified ''" did not delete a "Last-Modified" ++response header line; ++the bug had appeared in 0.7.44. ++ ++ ++ ++ ++ ++в директиве auth_basic_user_file не работал относительный путь, ++заданный строкой без переменных; ++ошибка появилась в 0.7.44.
++Спасибо Jerome Loyet. ++
++ ++a relative path in the "auth_basic_user_file" directive given without variables ++did not work; ++the bug had appeared in 0.7.44.
++Thanks to Jerome Loyet. ++
++
++ ++ ++ ++в директиве alias, заданной переменными ++без ссылок на выделения в регулярных выражениях; ++ошибка появилась в 0.7.42. ++ ++ ++in an "alias" directive given using variables ++without references to captures of regular expressions; ++the bug had appeared in 0.7.42. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++предварительная поддержка кэширования в модуле ngx_http_proxy_module. ++ ++ ++the ngx_http_proxy_module preliminary cache support. ++ ++ ++ ++ ++ ++параметр --with-pcre в configure. ++ ++ ++the --with-pcre option in the configure. ++ ++ ++ ++ ++ ++теперь директива try_files может быть использована на уровне server. ++ ++ ++the "try_files" directive is now allowed on the server block level. ++ ++ ++ ++ ++ ++директива try_files неправильно обрабатывала строку запроса в последнем ++параметре. ++ ++ ++the "try_files" directive handled incorrectly a query string ++in a fallback parameter. ++ ++ ++ ++ ++ ++директива try_files могла неверно тестировать каталоги. ++ ++ ++the "try_files" directive might test incorrectly directories. ++ ++ ++ ++ ++ ++если для пары адрес:порт описан только один сервер, то выделения ++в регулярных выражениях в директиве server_name не работали. ++ ++ ++if there was a single server for given address:port pair, ++then captures in regular expressions in a "server_name" directive did not work. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++запрос обрабатывался неверно, если директива root использовала переменные; ++ошибка появилась в 0.7.42. ++ ++ ++a request was handled incorrectly, if a "root" directive used variables; ++the bug had appeared in 0.7.42. ++ ++ ++ ++ ++ ++если сервер слушал на адресах типа "*", то значение переменной $server_addr ++было "0.0.0.0"; ++ошибка появилась в 0.7.36. ++ ++ ++if a server listened on wildcard address, then the $server_addr variable ++value was "0.0.0.0"; ++the bug had appeared in 0.7.36. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ошибка "Invalid argument", возвращаемая setsockopt(TCP_NODELAY) на Solaris, ++теперь игнорируется. ++ ++ ++now the "Invalid argument" error returned by setsockopt(TCP_NODELAY) on Solaris, ++is ignored. ++ ++ ++ ++ ++ ++при отсутствии файла, указанного в директиве auth_basic_user_file, ++теперь возвращается ошибка 403 вместо 500. ++ ++ ++now if a file specified in a "auth_basic_user_file" directive is absent, ++then the 403 error is returned instead of the 500 one. ++ ++ ++ ++ ++ ++директива auth_basic_user_file поддерживает переменные. ++
++Спасибо Кириллу Коринскому. ++
++ ++the "auth_basic_user_file" directive supports variables.
++Thanks to Kirill A. Korinskiy. ++
++
++ ++ ++ ++директива listen поддерживает параметр ipv6only.
++Спасибо Zhang Hua. ++
++ ++the "listen" directive supports the "ipv6only" parameter. ++
++Thanks to Zhang Hua. ++
++
++ ++ ++ ++в директиве alias со ссылками на выделения в регулярных выражениях; ++ошибка появилась в 0.7.40. ++ ++ ++in an "alias" directive with references to captures of regular expressions; ++the bug had appeared in 0.7.40. ++ ++ ++ ++ ++ ++совместимость с Tru64 UNIX.
++Спасибо Dustin Marquess. ++
++ ++compatibility with Tru64 UNIX.
++Thanks to Dustin Marquess. ++
++
++ ++ ++ ++nginx не собирался без библиотеки PCRE; ++ошибка появилась в 0.7.41. ++ ++ ++nginx could not be built without PCRE library; ++the bug had appeared in 0.7.41. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++в рабочем процессе мог произойти segmentation fault, ++если в server_name или location были выделения в регулярных выражениях; ++ошибка появилась в 0.7.40.
++Спасибо Владимиру Сопоту. ++
++ ++a segmentation fault might occur in worker process, ++if a "server_name" or a "location" directives had captures ++in regular expressions; ++the issue had appeared in 0.7.40.
++Thanks to Vladimir Sopot. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++директива location поддерживает выделения в регулярных выражениях. ++ ++ ++the "location" directive supports captures in regular expressions. ++ ++ ++ ++ ++ ++директиву alias с ссылками на выделения в регулярных выражениях ++можно использовать внутри location'а, заданного регулярным выражением ++с выделениями. ++ ++ ++an "alias" directive with capture references may be used inside ++a location given by a regular expression with captures. ++ ++ ++ ++ ++ ++директива server_name поддерживает выделения в регулярных выражениях. ++ ++ ++the "server_name" directive supports captures in regular expressions. ++ ++ ++ ++ ++ ++модуль ngx_http_autoindex_module не показывал последний слэш для каталогов ++на файловой системе XFS; ++ошибка появилась в 0.7.15.
++Спасибо Дмитрию Кузьменко. ++
++ ++the ngx_http_autoindex_module did not show the trailing slash in directories ++on XFS filesystem; ++the issue had appeared in 0.7.15.
++Thanks to Dmitry Kuzmenko. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++при включённом сжатии большие ответы с использованием SSI могли зависать; ++ошибка появилась в 0.7.28.
++Спасибо Артёму Бохану. ++
++ ++large response with SSI might hang, if gzipping was enabled; ++the bug had appeared in 0.7.28.
++Thanks to Artem Bokhan. ++
++
++ ++ ++ ++при использовании коротких статических вариантов в директиве try_files ++в рабочем процессе мог произойти segmentation fault. ++ ++ ++a segmentation fault might occur in worker process, ++if short static variants are used in a "try_files" directive. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++логгирование ошибок аутентификации. ++ ++ ++authentication failures logging. ++ ++ ++ ++ ++ ++имя/пароль, заданные в auth_basic_user_file, игнорировались после нечётного ++числа пустых строк.
++Спасибо Александру Загребину. ++
++ ++name/password in auth_basic_user_file were ignored after odd number ++of empty lines.
++Thanks to Alexander Zagrebin. ++
++
++ ++ ++ ++при использовании длинного пути в unix domain сокете ++в главном процессе происходил segmentation fault; ++ошибка появилась в 0.7.36. ++ ++ ++a segmentation fault occurred in a master process, ++if long path was used in unix domain socket; ++the bug had appeared in 0.7.36. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++директивы, использующие upstream'ы, не работали; ++ошибка появилась в 0.7.36. ++ ++ ++directives using upstreams did not work; ++the bug had appeared in 0.7.36. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++предварительная поддержка IPv6; ++директива listen модуля HTTP поддерживает IPv6. ++ ++ ++a preliminary IPv6 support; ++the "listen" directive of the HTTP module supports IPv6. ++ ++ ++ ++ ++ ++переменная $ancient_browser не работала для браузеров, заданных ++директивами modern_browser. ++ ++ ++the $ancient_browser variable did not work for browsers ++preset by a "modern_browser" directives. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директива ssl_engine не использовала SSL-акселератор ++для асимметричных шифров.
++Спасибо Marcin Gozdalik. ++
++ ++a "ssl_engine" directive did not use a SSL-accelerator ++for asymmetric ciphers.
++Thanks to Marcin Gozdalik. ++
++
++ ++ ++ ++директива try_files выставляла MIME-type, исходя из расширения ++первоначального запроса. ++ ++ ++a "try_files" directive set MIME type depending on an ++original request extension. ++ ++ ++ ++ ++ ++в директивах server_name, valid_referers и map ++неправильно обрабатывались имена вида "*domain.tld", ++если использовались маски вида ".domain.tld" и ".subdomain.domain.tld"; ++ошибка появилась в 0.7.9. ++ ++ ++"*domain.tld" names were handled incorrectly in ++"server_name", "valid_referers", and "map" directives, ++if ".domain.tld" and ".subdomain.domain.tld" wildcards were used; ++the bug had appeared in 0.7.9. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++параметр off в директиве if_modified_since. ++ ++ ++the "off" parameter of the "if_modified_since" directive. ++ ++ ++ ++ ++ ++теперь после команды XCLIENT nginx посылает команду HELO/EHLO.
++Спасибо Максиму Дунину. ++
++ ++now nginx sends an HELO/EHLO command after a XCLIENT command.
++Thanks to Maxim Dounin. ++
++
++ ++ ++ ++поддержка Microsoft-специфичного режима ++"AUTH LOGIN with User Name" ++в почтовом прокси-сервере.
++Спасибо Максиму Дунину. ++
++ ++Microsoft specific "AUTH LOGIN with User Name" mode support ++in mail proxy server.
++Thanks to Maxim Dounin. ++
++
++ ++ ++ ++в директиве rewrite, возвращающей редирект, старые аргументы присоединялись ++к новым через символ "?" вместо "&";
++ошибка появилась в 0.1.18.
++Спасибо Максиму Дунину. ++
++ ++in a redirect rewrite directive original arguments were concatenated with ++new arguments by a "?" rather than an "&";
++the bug had appeared in 0.1.18.
++Thanks to Maxim Dounin. ++
++
++ ++ ++ ++nginx не собирался на AIX. ++ ++ ++nginx could not be built on AIX. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++если на запрос с телом возвращался редирект, то ответ мог быть двойным ++при использовании методов epoll или rtsig.
++Спасибо Eden Li. ++
++ ++a double response might be returned if the epoll or rtsig methods are used ++and a redirect was returned to a request with body.
++Thanks to Eden Li. ++
++
++ ++ ++ ++для некоторых типов редиректов в переменной $sent_http_location ++было пустое значение. ++ ++ ++the $sent_http_location variable was empty for some redirects types. ++ ++ ++ ++ ++ ++при использовании директивы resolver в SMTP прокси-сервере ++в рабочем процессе мог произойти segmentation fault. ++ ++ ++a segmentation fault might occur in worker process ++if "resolver" directive was used in SMTP proxy. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++теперь в директиве try_files можно явно указать проверку каталога. ++ ++ ++now a directory existence testing can be set explicitly ++in the "try_files" directive. ++ ++ ++ ++ ++ ++fastcgi_store не всегда сохранял файлы. ++ ++ ++fastcgi_store stored files not always. ++ ++ ++ ++ ++ ++в гео-диапазонах. ++ ++ ++in geo ranges. ++ ++ ++ ++ ++ ++ошибки выделения больших блоков в разделяемой памяти, ++если nginx был собран без отладки.
++Спасибо Андрею Квасову. ++
++ ++in shared memory allocations if nginx was built without debugging.
++Thanks to Andrey Kvasov. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++теперь директива try_files проверяет только файлы, игнорируя каталоги. ++ ++ ++now the "try_files" directive tests files only and ignores directories. ++ ++ ++ ++ ++ ++директива fastcgi_split_path_info. ++ ++ ++the "fastcgi_split_path_info" directive. ++ ++ ++ ++ ++ ++Исправления в поддержке строки "Expect" в заголовке запроса. ++ ++ ++Bugfixes in an "Expect" request header line support. ++ ++ ++ ++ ++ ++Исправления в гео-диапазонах. ++ ++ ++Bugfixes in geo ranges. ++ ++ ++ ++ ++ ++при отсутствии ответа ngx_http_memcached_module возвращал ++в теле ответа строку "END" вместо 404-ой страницы по умолчанию; ++ошибка появилась в 0.7.18.
++Спасибо Максиму Дунину. ++
++ ++in a miss case ngx_http_memcached_module returned the "END" line ++as response body instead of default 404 page body; ++the bug had appeared in 0.7.18.
++Thanks to Maxim Dounin. ++
++
++ ++ ++ ++при проксировании SMTP nginx выдавал сообщение ++"250 2.0.0 OK" вместо "235 2.0.0 OK"; ++ошибка появилась в 0.7.22.
++Спасибо Максиму Дунину. ++
++ ++while SMTP proxying nginx issued message ++"250 2.0.0 OK" instead of "235 2.0.0 OK"; ++the bug had appeared in 0.7.22.
++Thanks to Maxim Dounin. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++ ++в рабочем процессе происходил segmentation fault, ++если в директивах fastcgi_pass или proxy_pass ++использовались переменные и имя хоста должно было резолвиться; ++ошибка появилась в 0.7.29. ++ ++ ++a segmentation fault occurred in worker process, ++if variables were used in the "fastcgi_pass" or "proxy_pass" directives ++and host name must be resolved; ++the bug had appeared in 0.7.29. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директивы fastcgi_pass и proxy_pass не поддерживали переменные ++при использовании unix domain сокетов. ++ ++ ++the "fastcgi_pass" and "proxy_pass" directives did not support ++variables if unix domain sockets were used. ++ ++ ++ ++ ++ ++Исправления в обработке подзапросов; ++ошибки появились в 0.7.25. ++ ++ ++Bugfixes in subrequest processing; ++the bugs had appeared in 0.7.25. ++ ++ ++ ++ ++ ++ответ "100 Continue" выдавался для запросов версии HTTP/1.0;
++Спасибо Максиму Дунину. ++
++ ++a "100 Continue" response was issued for HTTP/1.0 requests;
++Thanks to Maxim Dounin. ++
++
++ ++ ++ ++в выделении памяти в модуле ngx_http_gzip_filter_module под Cygwin. ++ ++ ++in memory allocation in the ngx_http_gzip_filter_module on Cygwin. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++в выделении памяти в модуле ngx_http_gzip_filter_module. ++ ++ ++in memory allocation in the ngx_http_gzip_filter_module. ++ ++ ++ ++ ++ ++значения по умолчанию для директивы gzip_buffers изменены с 4 4k/8k ++на 32 4k или 16 8k. ++ ++ ++the default "gzip_buffers" directive values have been changed ++to 32 4k or 16 8k from 4 4k/8k. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директива try_files. ++ ++ ++the "try_files" directive. ++ ++ ++ ++ ++ ++директива fastcgi_pass поддерживает переменные. ++ ++ ++variables support in the "fastcgi_pass" directive. ++ ++ ++ ++ ++ ++теперь директива geo может брать адрес из переменной.
++Спасибо Андрею Нигматулину. ++
++ ++now the $geo variable may get an address from a variable.
++Thanks to Andrei Nigmatulin. ++
++
++ ++ ++ ++теперь модификатор location'а можно указывать без пробела перед названием. ++ ++ ++now a location's modifier may be used without space before name. ++ ++ ++ ++ ++ ++переменная $upstream_response_length. ++ ++ ++the $upstream_response_length variable. ++ ++ ++ ++ ++ ++теперь директива add_header не добавляет пустое значение. ++ ++ ++now a "add_header" directive does not add an empty value. ++ ++ ++ ++ ++ ++при запросе файла нулевой длины nginx закрывал соединение, ничего не передав; ++ошибка появилась в 0.7.25. ++ ++ ++if zero length static file was requested, then nginx just closed connection; ++the bug had appeared in 0.7.25. ++ ++ ++ ++ ++ ++метод MOVE не мог перемещать файл в несуществующий каталог. ++ ++ ++a MOVE method could not move file in non-existent directory. ++ ++ ++ ++ ++ ++если в сервере не был описан ни один именованный location, ++но такой location использовался в директиве error_page, ++то в рабочем процессе происходил segmentation fault.
++Спасибо Сергею Боченкову. ++
++ ++a segmentation fault occurred in worker process, ++if no one named location was defined in server, ++but some one was used in an error_page directive.
++Thanks to Sergey Bochenkov. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++в обработке подзапросов; ++ошибка появилась в 0.7.25. ++ ++ ++in subrequest processing; ++the bug had appeared in 0.7.25. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++в обработке подзапросов. ++ ++ ++in subrequest processing. ++ ++ ++ ++ ++ ++теперь разрешаются POST'ы без строки "Content-Length" в заголовке запроса. ++ ++ ++now POSTs without "Content-Length" header line are allowed. ++ ++ ++ ++ ++ ++теперь директивы limit_req и limit_conn указывают причину запрета запроса. ++ ++ ++now the "limit_req" and "limit_conn" directives log a prohibition reason. ++ ++ ++ ++ ++ ++в параметре delete директивы geo. ++ ++ ++in the "delete" parameter of the "geo" directive. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директива if_modified_since. ++ ++ ++the "if_modified_since" directive. ++ ++ ++ ++ ++ ++nginx не обрабатывал ответ FastCGI-сервера, ++если перед ответом сервер передавал много сообщений в stderr. ++ ++ ++nginx did not process a FastCGI server response, ++if the server send too many messages to stderr before response. ++ ++ ++ ++ ++ ++переменные "$cookie_..." не работали в SSI and в перловом модуле. ++ ++ ++the "$cookie_..." variables did not work in the SSI and the perl module. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++параметры delete и ranges в директиве geo. ++ ++ ++the "delete" and "ranges" parameters in the "geo" directive. ++ ++ ++ ++ ++ ++ускорение загрузки geo-базы с большим числом значений. ++ ++ ++speeding up loading of geo base with large number of values. ++ ++ ++ ++ ++ ++уменьшение памяти, необходимой для загрузки geo-базы. ++ ++ ++decrease of memory required for geo base load. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++параметр none в директиве smtp_auth.
++Спасибо Максиму Дунину. ++
++ ++the "none" parameter in the "smtp_auth" directive.
++Thanks to Maxim Dounin. ++
++
++ ++ ++ ++переменные "$cookie_...". ++ ++ ++the "$cookie_..." variables. ++ ++ ++ ++ ++ ++директива directio не работала с файловой системой XFS. ++ ++ ++the "directio" directive did not work in XFS filesystem. ++ ++ ++ ++ ++ ++resolver не понимал большие DNS-ответы.
++Спасибо Zyb. ++
++ ++the resolver did not understand big DNS responses.
++Thanks to Zyb. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++Изменения в модуле ngx_http_limit_req_module. ++ ++ ++Changes in the ngx_http_limit_req_module. ++ ++ ++ ++ ++ ++поддержка EXSLT в модуле ngx_http_xslt_module.
++Спасибо Денису Латыпову. ++
++ ++the EXSLT support in the ngx_http_xslt_module.
++Thanks to Denis F. Latypoff. ++
++
++ ++ ++ ++совместимость с glibc 2.3.
++Спасибо Eric Benson и Максиму Дунину. ++
++ ++compatibility with glibc 2.3.
++Thanks to Eric Benson and Maxim Dounin. ++
++
++ ++ ++ ++nginx не запускался на MacOSX 10.4 и более ранних; ++ошибка появилась в 0.7.6. ++ ++ ++nginx could not run on MacOSX 10.4 and earlier; ++the bug had appeared in 0.7.6. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++Изменения в модуле ngx_http_gzip_filter_module. ++ ++ ++Changes in the ngx_http_gzip_filter_module. ++ ++ ++ ++ ++ ++модуль ngx_http_limit_req_module. ++ ++ ++the ngx_http_limit_req_module. ++ ++ ++ ++ ++ ++на платформах sparc и ppc рабочие процессы могли выходить по сигналу SIGBUS; ++ошибка появилась в 0.7.3.
++Спасибо Максиму Дунину. ++
++ ++worker processes might exit on a SIGBUS signal on sparc and ppc platforms; ++the bug had appeared in 0.7.3.
++Thanks to Maxim Dounin. ++
++
++ ++ ++ ++директивы вида "proxy_pass http://host/some:uri" не работали; ++ошибка появилась в 0.7.12. ++ ++ ++the "proxy_pass http://host/some:uri" directives did not work; ++the bug had appeared in 0.7.12. ++ ++ ++ ++ ++ ++при использовании HTTPS запросы могли завершаться с ошибкой "bad write retry". ++ ++ ++in HTTPS mode requests might fail with the "bad write retry" error. ++ ++ ++ ++ ++ ++модуль ngx_http_secure_link_module не работал внутри location'ов ++с именами меньше 3 символов. ++ ++ ++the ngx_http_secure_link_module did not work inside locations, ++whose names are less than 3 characters. ++ ++ ++ ++ ++ ++переменная $server_addr могла не иметь значения. ++ ++ ++$server_addr variable might have no value. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++обновление номера версии. ++ ++ ++version number update. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директива underscores_in_headers; ++теперь nginx по умолчанию не разрешает подчёркивания в именах строк ++в заголовке запроса клиента. ++ ++ ++the "underscores_in_headers" directive; ++now nginx does not allows underscores in a client request header line names. ++ ++ ++ ++ ++ ++модуль ngx_http_secure_link_module. ++ ++ ++the ngx_http_secure_link_module. ++ ++ ++ ++ ++ ++директива real_ip_header поддерживает любой заголовок. ++ ++ ++the "real_ip_header" directive supports any header. ++ ++ ++ ++ ++ ++директива log_subrequest. ++ ++ ++the "log_subrequest" directive. ++ ++ ++ ++ ++ ++переменная $realpath_root. ++ ++ ++the $realpath_root variable. ++ ++ ++ ++ ++ ++параметры http_502 и http_504 в директиве proxy_next_upstream. ++ ++ ++the "http_502" and "http_504" parameters of the "proxy_next_upstream" directive. ++ ++ ++ ++ ++ ++параметр http_503 в директивах proxy_next_upstream или fastcgi_next_upstream ++не работал. ++ ++ ++the "http_503" parameter of the "proxy_next_upstream" or ++"fastcgi_next_upstream" directives did not work. ++ ++ ++ ++ ++ ++nginx мог выдавать строку "Transfer-Encoding: chunked" для запросов HEAD. ++ ++ ++nginx might send a "Transfer-Encoding: chunked" header line for HEAD requests. ++ ++ ++ ++ ++ ++теперь accept-лимит зависит от числа worker_connections. ++ ++ ++now accept threshold depends on worker_connections. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директива directio теперь работает на Linux. ++ ++ ++now the "directio" directive works on Linux. ++ ++ ++ ++ ++ ++переменная $pid. ++ ++ ++the $pid variable. ++ ++ ++ ++ ++ ++оптимизация directio, появившаяся в 0.7.15, не работала при использовании ++open_file_cache. ++ ++ ++the "directio" optimization that had appeared in 0.7.15 did not work with ++open_file_cache. ++ ++ ++ ++ ++ ++access_log с переменными не работал на Linux; ++ошибка появилась в 0.7.7. ++ ++ ++the "access_log" with variables did not work on Linux; ++the bug had appeared in 0.7.7. ++ ++ ++ ++ ++ ++модуль ngx_http_charset_module не понимал название кодировки в кавычках, ++полученное от бэкенда. ++ ++ ++the ngx_http_charset_module did not understand quoted charset name ++received from backend. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++nginx не собирался на 64-битных платформах; ++ошибка появилась в 0.7.15. ++ ++ ++nginx could not be built on 64-bit platforms; ++the bug had appeared in 0.7.15. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++модуль ngx_http_random_index_module. ++ ++ ++the ngx_http_random_index_module. ++ ++ ++ ++ ++ ++директива directio оптимизирована для запросов файлов, начинающихся ++с произвольной позиции. ++ ++ ++the "directio" directive has been optimized for file requests starting ++from arbitrary position. ++ ++ ++ ++ ++ ++директива directio при необходимости запрещает использование sendfile. ++ ++ ++the "directio" directive turns off sendfile if it is necessary. ++ ++ ++ ++ ++ ++теперь nginx разрешает подчёркивания в именах строк в заголовке запроса клиента. ++ ++ ++now nginx allows underscores in a client request header line names. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++теперь директивы ssl_certificate и ssl_certificate_key не имеют ++значений по умолчанию. ++ ++ ++now the ssl_certificate and ssl_certificate_key directives have no ++default values. ++ ++ ++ ++ ++ ++директива listen поддерживает параметр ssl. ++ ++ ++the "listen" directive supports the "ssl" parameter. ++ ++ ++ ++ ++ ++теперь при переконфигурации nginx учитывает изменение временной зоны ++на FreeBSD и Linux. ++ ++ ++now nginx takes into account a time zone change while reconfiguration ++on FreeBSD and Linux. ++ ++ ++ ++ ++ ++параметры директивы listen, такие как backlog, rcvbuf и прочие, ++не устанавливались, если сервером по умолчанию был не первый сервер. ++ ++ ++the "listen" directive parameters such as "backlog", "rcvbuf", etc. ++were not set, if a default server was not the first one. ++ ++ ++ ++ ++ ++при использовании в качестве аргументов части URI, выделенного с помощью ++директивы rewrite, эти аргументы не экранировались. ++ ++ ++if URI part captured by a "rewrite" directive was used as a query string, ++then the query string was not escaped. ++ ++ ++ ++ ++ ++улучшения тестирования правильности конфигурационного файла. ++ ++ ++configuration file validity test improvements. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++nginx не собирался на Linux и Solaris; ++ошибка появилась в 0.7.12. ++ ++ ++nginx could not be built on Linux and Solaris; ++the bug had appeared in 0.7.12. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директива server_name поддерживает пустое имя "". ++ ++ ++the "server_name" directive supports empty name "". ++ ++ ++ ++ ++ ++директива gzip_disable поддерживает специальную маску msie6. ++ ++ ++the "gzip_disable" directive supports special "msie6" mask. ++ ++ ++ ++ ++ ++при использовании параметра max_fails=0 в upstream'е с несколькими ++серверами рабочий процесс выходил по сигналу SIGFPE.
++Спасибо Максиму Дунину. ++
++ ++if the "max_fails=0" parameter was used in upstream with several servers, ++then a worker process exited on a SIGFPE signal.
++Thanks to Maxim Dounin. ++
++
++ ++ ++ ++при перенаправлении запроса с помощью директивы error_page ++терялось тело запроса. ++ ++ ++a request body was dropped while redirection via an "error_page" directive. ++ ++ ++ ++ ++ ++при перенаправлении запроса с методом HEAD с помощью директивы error_page ++возвращался полный ответ. ++ ++ ++a full response was returned for request method HEAD ++while redirection via an "error_page" directive. ++ ++ ++ ++ ++ ++метод $r->header_in() не возвращал значения строк "Host", "User-Agent", ++и "Connection" из заголовка запроса; ++ошибка появилась в 0.7.0. ++ ++ ++the $r->header_in() method did not return value of the "Host", ++"User-Agent", and "Connection" request header lines; ++the bug had appeared in 0.7.0. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++теперь ngx_http_charset_module по умолчанию не работает MIME-типом text/css. ++ ++ ++now ngx_http_charset_module does not work by default with text/css MIME type. ++ ++ ++ ++ ++ ++теперь nginx возвращает код 405 для метода POST при запросе статического ++файла, только если файл существует. ++ ++ ++now nginx returns the 405 status code for POST method requesting a static file ++only if the file exists. ++ ++ ++ ++ ++ ++директива proxy_ssl_session_reuse. ++ ++ ++the "proxy_ssl_session_reuse" directive. ++ ++ ++ ++ ++ ++после перенаправления запроса с помощью "X-Accel-Redirect" ++директива proxy_pass без URI могла использовать оригинальный запрос. ++ ++ ++a "proxy_pass" directive without URI part might use original request ++after the "X-Accel-Redirect" redirection was used. ++ ++ ++ ++ ++ ++если у каталога были права доступа только на поиск файлов ++и первый индексный файл отсутствовал, то nginx возвращал ошибку 500. ++ ++ ++if a directory has search only rights and the first index file was absent, ++then nginx returned the 500 status code. ++ ++ ++ ++ ++ ++ошибок во вложенных location'ах; ++ошибки появились в 0.7.1. ++ ++ ++in inclusive locations; ++the bugs had appeared in 0.7.1. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ошибок в директивах addition_types, charset_types, ++gzip_types, ssi_types, sub_filter_types и xslt_types; ++ошибки появились в 0.7.9. ++ ++ ++in the "addition_types", "charset_types", ++"gzip_types", "ssi_types", "sub_filter_types", and "xslt_types" directives; ++the bugs had appeared in 0.7.9. ++ ++ ++ ++ ++ ++рекурсивной error_page для 500 ошибки. ++ ++ ++of recursive error_page for 500 status code. ++ ++ ++ ++ ++ ++теперь модуль ngx_http_realip_module устанавливает адрес не для ++всего keepalive соединения, а для каждого запроса по этому соединению. ++ ++ ++now the ngx_http_realip_module sets address not for whole keepalive connection, ++but for each request passed via the connection. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++теперь ngx_http_charset_module по умолчанию работает со следующими MIME-типами: ++text/html, text/css, text/xml, text/plain, text/vnd.wap.wml, ++application/x-javascript и application/rss+xml. ++ ++ ++now ngx_http_charset_module works by default with following MIME types: ++text/html, text/css, text/xml, text/plain, text/vnd.wap.wml, ++application/x-javascript, and application/rss+xml. ++ ++ ++ ++ ++ ++директивы charset_types и addition_types. ++ ++ ++the "charset_types" and "addition_types" directives. ++ ++ ++ ++ ++ ++теперь директивы gzip_types, ssi_types и sub_filter_types используют хэш. ++ ++ ++now the "gzip_types", "ssi_types", and "sub_filter_types" directives use hash. ++ ++ ++ ++ ++ ++модуль ngx_cpp_test_module. ++ ++ ++the ngx_cpp_test_module. ++ ++ ++ ++ ++ ++директива expires поддерживает суточное время. ++ ++ ++the "expires" directive supports daily time. ++ ++ ++ ++ ++ ++улучшения и исправления в модуле ngx_http_xslt_module.
++Спасибо Денису Латыпову и Максиму Дунину. ++
++ ++the ngx_http_xslt_module improvements and bug fixing.
++Thanks to Denis F. Latypoff and Maxim Dounin. ++
++
++ ++ ++ ++директива log_not_found не работала при поиске индексных файлов. ++ ++ ++the "log_not_found" directive did not work for index files tests. ++ ++ ++ ++ ++ ++HTTPS-соединения могли зависнуть, ++если использовались методы kqueue, epoll, rtsig или eventport; ++ошибка появилась в 0.7.7. ++ ++ ++HTTPS connections might hang, ++if kqueue, epoll, rtsig, or eventport methods were used; ++the bug had appeared in 0.7.7. ++ ++ ++ ++ ++ ++если в директивах server_name, valid_referers и map ++использовалась маска вида "*.domain.tld" и при этом полное имя ++вида "domain.tld" не было описано, то это имя попадало под маску; ++ошибка появилась в 0.3.18. ++ ++ ++if the "server_name", "valid_referers", and "map" directives used ++an "*.domain.tld" wildcard and exact name "domain.tld" was not set, ++then the exact name was matched by the wildcard; ++the bug had appeared in 0.3.18. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++модуль ngx_http_xslt_module. ++ ++ ++the ngx_http_xslt_module. ++ ++ ++ ++ ++ ++переменные "$arg_...". ++ ++ ++the "$arg_..." variables. ++ ++ ++ ++ ++ ++поддержка directio в Solaris.
++Спасибо Ivan Debnar. ++
++ ++Solaris directio support.
++Thanks to Ivan Debnar. ++
++
++ ++ ++ ++теперь, если FastCGI-сервер присылает строку "Location" в заголовке ответа ++без строки статуса, то nginx использует код статуса 302.
++Спасибо Максиму Дунину. ++
++ ++now if FastCGI server sends a "Location" header line without status line, ++then nginx uses 302 status code.
++Thanks to Maxim Dounin. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++теперь ошибка EAGAIN при вызове connect() не считается временной. ++ ++ ++now the EAGAIN error returned by connect() is not considered as temporary error. ++ ++ ++ ++ ++ ++значением переменной $ssl_client_cert теперь является сертификат, ++перед каждой строкой которого, кроме первой, вставляется символ табуляции; ++неизменённый сертификат доступен через переменную $ssl_client_raw_cert. ++ ++ ++now the $ssl_client_cert variable value is a certificate with TAB character ++intended before each line except first one; ++an unchanged certificate is available in the $ssl_client_raw_cert variable. ++ ++ ++ ++ ++ ++параметр ask директивы ssl_verify_client. ++ ++ ++the "ask" parameter in the "ssl_verify_client" directive. ++ ++ ++ ++ ++ ++улучшения в обработке byte-range.
++Спасибо Максиму Дунину. ++
++ ++byte-range processing improvements.
++Thanks to Maxim Dounin. ++
++
++ ++ ++ ++директива directio.
++Спасибо Jiang Hong. ++
++ ++the "directio" directive.
++Thanks to Jiang Hong. ++
++
++ ++ ++ ++поддержка sendfile() в MacOSX 10.5. ++ ++ ++MacOSX 10.5 sendfile() support. ++ ++ ++ ++ ++ ++в MacOSX и Cygwin при проверке location'ов теперь делается сравнение ++без учёта регистра символов; ++однако, сравнение ограничено только однобайтными locale'ями. ++ ++ ++now in MacOSX and Cygwin locations are tested in case insensitive mode; ++however, the compare is provided by single-byte locales only. ++ ++ ++ ++ ++ ++соединения почтового прокси-сервера зависали в режиме SSL, ++если использовались методы select, poll или /dev/poll. ++ ++ ++mail proxy SSL connections hanged, ++if select, poll, or /dev/poll methods were used. ++ ++ ++ ++ ++ ++ошибки при использовании кодировки UTF-8 в ngx_http_autoindex_module. ++ ++ ++UTF-8 encoding usage in the ngx_http_autoindex_module. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++теперь при использовании переменных в директиве access_log ++всегда проверяется существовании root'а для запроса. ++ ++ ++now if variables are used in the "access_log" directive ++a request root existence is always tested. ++ ++ ++ ++ ++ ++модуль ngx_http_flv_module не поддерживал несколько значений в ++аргументах запроса. ++ ++ ++the ngx_http_flv_module did not support several values in a query string. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++Исправления в поддержке переменных в директиве access_log; ++ошибки появились в 0.7.4. ++ ++ ++Bugfixes in variables support in the "access_log" directive; ++the bugs had appeared in 0.7.4. ++ ++ ++ ++ ++ ++nginx не собирался с параметром --without-http_gzip_module; ++ошибка появилась в 0.7.3.
++Спасибо Кириллу Коринскому. ++
++ ++nginx could not be built --without-http_gzip_module; ++the bug had appeared in 0.7.3.
++Thanks to Kirill A. Korinskiy. ++
++
++ ++ ++ ++при совместном использовании sub_filter и SSI ++ответы могли передаваться неверно. ++ ++ ++if sub_filter and SSI were used together, then responses might ++were transferred incorrectly. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++директива access_log поддерживает переменные. ++ ++ ++variables support in the "access_log" directive. ++ ++ ++ ++ ++ ++директива open_log_file_cache. ++ ++ ++the "open_log_file_cache" directive. ++ ++ ++ ++ ++ ++ключ -g. ++ ++ ++the -g switch. ++ ++ ++ ++ ++ ++поддержка строки "Expect" в заголовке запроса. ++ ++ ++the "Expect" request header line support. ++ ++ ++ ++ ++ ++большие включения в SSI могли передавались не полностью. ++ ++ ++large SSI inclusions might be truncated. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++MIME-тип для расширения rss изменён на "application/rss+xml". ++ ++ ++the "rss" extension MIME type has been changed to "application/rss+xml". ++ ++ ++ ++ ++ ++теперь директива "gzip_vary on" выдаёт строку ++"Vary: Accept-Encoding" ++в заголовке ответа и для несжатых ответов. ++ ++ ++now the "gzip_vary" directive turned on issues ++a "Vary: Accept-Encoding" ++header line for uncompressed responses too. ++ ++ ++ ++ ++ ++теперь при использовании протокола "https://" в директиве rewrite ++автоматически делается редирект. ++ ++ ++now the "rewrite" directive does a redirect automatically ++if the "https://" protocol is used. ++ ++ ++ ++ ++ ++директива proxy_pass не работала с протоколом HTTPS; ++ошибка появилась в 0.6.9. ++ ++ ++the "proxy_pass" directive did not work with the HTTPS protocol; ++the bug had appeared in 0.6.9. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++теперь nginx поддерживает шифры с обменом EDH-ключами. ++ ++ ++now nginx supports EDH key exchange ciphers. ++ ++ ++ ++ ++ ++директива ssl_dhparam. ++ ++ ++the "ssl_dhparam" directive. ++ ++ ++ ++ ++ ++переменная $ssl_client_cert.
++Спасибо Manlio Perillo. ++
++ ++the $ssl_client_cert variable.
++Thanks to Manlio Perillo. ++
++
++ ++ ++ ++после изменения URI с помощью директивы rewrite nginx не искал новый location; ++ошибка появилась в 0.7.1.
++Спасибо Максиму Дунину. ++
++ ++after changing URI via a "rewrite" directive nginx did not search ++a new location; ++the bug had appeared in 0.7.1.
++Thanks to Maxim Dounin. ++
++
++ ++ ++ ++nginx не собирался без библиотеки PCRE; ++ошибка появилась в 0.7.1. ++ ++ ++nginx could not be built without PCRE library; ++the bug had appeared in 0.7.1. ++ ++ ++ ++ ++ ++при редиректе запроса к каталогу с добавлением слэша nginx ++не добавлял аргументы из оригинального запроса. ++ ++ ++when a request to a directory was redirected with the slash added, ++nginx dropped a query string from the original request. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++теперь поиск location'а делается с помощью дерева. ++ ++ ++now locations are searched in a tree. ++ ++ ++ ++ ++ ++директива optimize_server_names упразднена в связи с появлением ++директивы server_name_in_redirect. ++ ++ ++the "optimize_server_names" directive was canceled ++due to the "server_name_in_redirect" directive introduction. ++ ++ ++ ++ ++ ++некоторые давно устаревшие директивы больше не поддерживаются. ++ ++ ++some long deprecated directives are not supported anymore. ++ ++ ++ ++ ++ ++параметр "none" в директиве ssl_session_cache; ++теперь этот параметр используется по умолчанию.
++Спасибо Rob Mueller. ++
++ ++the "none" parameter in the "ssl_session_cache" directive; ++now this is default parameter.
++Thanks to Rob Mueller. ++
++
++ ++ ++ ++рабочие процессы могли не реагировать на сигналы переконфигурации ++и ротации логов. ++ ++ ++worker processes might not catch reconfiguration and log rotation signals. ++ ++ ++ ++ ++ ++nginx не собирался на последних Fedora 9 Linux.
++Спасибо Roxis. ++
++ ++nginx could not be built on latest Fedora 9 Linux.
++Thanks to Roxis. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++теперь символы 0x00-0x1F, '"' и '\' в access_log записываются в виде \xXX.
++Спасибо Максиму Дунину. ++
++ ++now the 0x00-0x1F, '"' and '\' characters are escaped as \xXX in an ++access_log.
++Thanks to Maxim Dounin. ++
++
++ ++ ++ ++теперь nginx разрешает несколько строк "Host" в заголовке запроса. ++ ++ ++now nginx allows several "Host" request header line. ++ ++ ++ ++ ++ ++директива expires поддерживает флаг modified. ++ ++ ++the "modified" flag in the "expires" directive. ++ ++ ++ ++ ++ ++переменные $uid_got и $uid_set можно использовать на любой стадии обработки ++запроса. ++ ++ ++the $uid_got and $uid_set variables may be used at any request processing stage. ++ ++ ++ ++ ++ ++переменная $hostname.
++Спасибо Андрею Нигматулину. ++
++ ++the $hostname variable.
++Thanks to Andrei Nigmatulin. ++
++
++ ++ ++ ++поддержка DESTDIR.
++Спасибо Todd A. Fisher и Andras Voroskoi. ++
++ ++DESTDIR support.
++Thanks to Todd A. Fisher and Andras Voroskoi. ++
++
++ ++ ++ ++при использовании keepalive на Linux ++в рабочем процессе мог произойти segmentation fault. ++ ++ ++a segmentation fault might occur in worker process on Linux, ++if keepalive was enabled. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++nginx не обрабатывал ответ FastCGI-сервера, если строка заголовка ответа была ++в конце записи FastCGI; ++ошибка появилась в 0.6.2.
++Спасибо Сергею Серову. ++
++ ++nginx did not process FastCGI response ++if header was at the end of FastCGI record; ++the bug had appeared in 0.6.2.
++Thanks to Sergey Serov. ++
++
++ ++ ++ ++при удалении файла и использовании директивы open_file_cache_errors off ++в рабочем процессе мог произойти segmentation fault. ++ ++ ++a segmentation fault might occur in worker process if a file was deleted ++and the "open_file_cache_errors" directive was off. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++теперь, если маске, заданной в директиве include, не соответствует ++ни один файл, то nginx не выдаёт ошибку. ++ ++ ++now if an "include" directive pattern does not match any file, ++then nginx does not issue an error. ++ ++ ++ ++ ++ ++теперь время в директивах можно задавать без пробела, например, "1h50m". ++ ++ ++now the time in directives may be specified without spaces, ++for example, "1h50m". ++ ++ ++ ++ ++ ++утечек памяти, если директива ssl_verify_client имела значение on.
++Спасибо Chavelle Vincent. ++
++ ++memory leaks if the "ssl_verify_client" directive was on.
++Thanks to Chavelle Vincent. ++
++
++ ++ ++ ++директива sub_filter могла вставлять заменяемый текст в вывод. ++ ++ ++the "sub_filter" directive might set text to change into output. ++ ++ ++ ++ ++ ++директива error_page не воспринимала параметры в перенаправляемом URI. ++ ++ ++the "error_page" directive did not take into account arguments in ++redirected URI. ++ ++ ++ ++ ++ ++теперь при сборке с Cygwin nginx всегда открывает файлы в бинарном режиме. ++ ++ ++now nginx always opens files in binary mode under Cygwin. ++ ++ ++ ++ ++ ++nginx не собирался под OpenBSD; ++ошибка появилась в 0.6.15. ++ ++ ++nginx could not be built on OpenBSD; ++the bug had appeared in 0.6.15. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++модуль ngx_google_perftools_module. ++ ++ ++the ngx_google_perftools_module. ++ ++ ++ ++ ++ ++модуль ngx_http_perl_module не собирался на 64-битных платформах; ++ошибка появилась в 0.6.27. ++ ++ ++the ngx_http_perl_module could not be built on 64-bit platforms; ++the bug had appeared in 0.6.27. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++метод rtsig не собирался; ++ошибка появилась в 0.6.27. ++ ++ ++the rtsig method could not be built; ++the bug had appeared in 0.6.27. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++теперь на Linux 2.6.18+ по умолчанию не собирается метод rtsig. ++ ++ ++now by default the rtsig method is not built on Linux 2.6.18+. ++ ++ ++ ++ ++ ++теперь при перенаправлении запроса в именованный location с помощью ++директивы error_page метод запроса не изменяется. ++ ++ ++now a request method is not changed while redirection to a named location ++via an "error_page" directive. ++ ++ ++ ++ ++ ++директивы resolver и resolver_timeout в SMTP прокси-сервере. ++ ++ ++the "resolver" and "resolver_timeout" directives in SMTP proxy. ++ ++ ++ ++ ++ ++директива post_action поддерживает именованные location'ы. ++ ++ ++the "post_action" directive supports named locations. ++ ++ ++ ++ ++ ++при перенаправлении запроса из location'а c обработчиком proxy, FastCGI ++или memcached в именованный location со статическим обработчиком ++в рабочем процессе происходил segmentation fault. ++ ++ ++a segmentation fault occurred in worker process, ++if a request was redirected from proxy, FastCGI, or memcached location ++to static named locations. ++ ++ ++ ++ ++ ++браузеры не повторяли SSL handshake, если при первом handshake ++не оказалось правильного клиентского сертификата. ++
++Спасибо Александру Инюхину. ++
++ ++browsers did not repeat SSL handshake if there is no valid client certificate ++in first handshake. ++
++Thanks to Alexander V. Inyukhin. ++
++
++ ++ ++ ++при перенаправлении ошибок 495-497 с помощью директивы error_page ++без изменения кода ошибки nginx пытался выделить очень много памяти. ++ ++ ++if response code 495-497 was redirected via an "error_page" directive ++without code change, then nginx tried to allocate too many memory. ++ ++ ++ ++ ++ ++утечки памяти в долгоживущих небуфферизированных соединениях. ++ ++ ++memory leak in long-lived non buffered connections. ++ ++ ++ ++ ++ ++утечки памяти в resolver'е. ++ ++ ++memory leak in resolver. ++ ++ ++ ++ ++ ++при перенаправлении запроса из location'а c обработчиком proxy ++в другой location с обработчиком proxy ++в рабочем процессе происходил segmentation fault. ++ ++ ++a segmentation fault occurred in worker process, ++if a request was redirected from proxy, FastCGI, or memcached location ++to static named locations. ++ ++ ++ ++ ++ ++ошибки в кэшировании переменных $proxy_host и $proxy_port.
++Спасибо Сергею Боченкову. ++
++ ++in the $proxy_host and $proxy_port variables caching.
++Thanks to Sergey Bochenkov. ++
++
++ ++ ++ ++директива proxy_pass с переменными использовала порт, описанной в другой ++директиве proxy_pass без переменных, но с таким же именем хоста.
++Спасибо Сергею Боченкову. ++
++ ++a "proxy_pass" directive with variables used incorrectly the same port ++as in another "proxy_pass" directive with the same host name ++and without variables.
++Thanks to Sergey Bochenkov. ++
++
++ ++ ++ ++во время переконфигурации на некоторых 64-битном платформах в лог ++записывался alert "sendmsg() failed (9: Bad file descriptor)". ++ ++ ++an alert "sendmsg() failed (9: Bad file descriptor)" on some 64-bit platforms ++while reconfiguration. ++ ++ ++ ++ ++ ++при повторном использовании в SSI пустого block'а в качестве заглушки ++в рабочем процессе происходил segmentation fault. ++ ++ ++a segmentation fault occurred in worker process, ++if empty stub block was used second time in SSI. ++ ++ ++ ++ ++ ++ошибки при копировании части URI, содержащего экранированные символы, ++в аргументы. ++ ++ ++in copying URI part contained escaped symbols into arguments. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++директивы proxy_store и fastcgi_store не проверяли длину ответа. ++ ++ ++the "proxy_store" and "fastcgi_store" directives did not check ++a response length. ++ ++ ++ ++ ++ ++при использовании большого значения в директиве expires ++в рабочем процессе происходил segmentation fault.
++Спасибо Joaquin Cuenca Abela. ++
++ ++a segmentation fault occurred in worker process, ++if big value was used in a "expires" directive.
++Thanks to Joaquin Cuenca Abela. ++
++
++ ++ ++ ++nginx неверно определял длину строки кэша на Pentium 4.
++Спасибо Геннадию Махомеду. ++
++ ++nginx incorrectly detected cache line size on Pentium 4.
++Thanks to Gena Makhomed. ++
++
++ ++ ++ ++в проксированных подзапросах и подзапросах к FastCGI-серверу ++вместо метода GET использовался оригинальный метод клиента. ++ ++ ++in proxied or FastCGI subrequests a client original method was used ++instead of the GET method. ++ ++ ++ ++ ++ ++утечки сокетов в режиме HTTPS при использовании отложенного accept'а.
++Спасибо Ben Maurer. ++
++ ++socket leak in HTTPS mode if deferred accept was used.
++Thanks to Ben Maurer. ++
++
++ ++ ++ ++nginx выдавал ошибочное сообщение "SSL_shutdown() failed (SSL: )"; ++ошибка появилась в 0.6.23. ++ ++ ++nginx issued the bogus error message "SSL_shutdown() failed (SSL: )"; ++the bug had appeared in 0.6.23. ++ ++ ++ ++ ++ ++при использовании HTTPS запросы могли завершаться с ошибкой "bad write retry"; ++ошибка появилась в 0.6.23. ++ ++ ++in HTTPS mode requests might fail with the "bad write retry" error; ++the bug had appeared in 0.6.23. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++вместо специального параметра "*" в директиве server_name теперь ++используется директива server_name_in_redirect. ++ ++ ++now the "server_name_in_redirect" directive is used instead of ++the "server_name" directive's special "*" parameter. ++ ++ ++ ++ ++ ++в качестве основного имени в директиве server_name теперь ++можно использовать имена с масками и регулярными выражениями. ++ ++ ++now wildcard and regex names can be used as main name in ++a "server_name" directive. ++ ++ ++ ++ ++ ++директива satisfy_any заменена директивой satisfy. ++ ++ ++the "satisfy_any" directive was replaced by the "satisfy" directive. ++ ++ ++ ++ ++ ++после переконфигурации старые рабочие процесс могли сильно нагружать процессор ++при запуске под Linux OpenVZ. ++ ++ ++old worker processes might hog CPU after reconfiguration if they was run ++under Linux OpenVZ. ++ ++ ++ ++ ++ ++директива min_delete_depth. ++ ++ ++the "min_delete_depth" directive. ++ ++ ++ ++ ++ ++методы COPY и MOVE не работали с одиночными файлами. ++ ++ ++the COPY and MOVE methods did not work with single files. ++ ++ ++ ++ ++ ++модуль ngx_http_gzip_static_module не позволял работать модулю ++ngx_http_dav_module; ++ошибка появилась в 0.6.23. ++ ++ ++the ngx_http_gzip_static_module did not allow the ngx_http_dav_module to work; ++the bug had appeared in 0.6.23. ++ ++ ++ ++ ++ ++утечки сокетов в режиме HTTPS при использовании отложенного accept'а.
++Спасибо Ben Maurer. ++
++ ++socket leak in HTTPS mode if deferred accept was used.
++Thanks to Ben Maurer. ++
++
++ ++ ++ ++nginx не собирался без библиотеки PCRE; ++ошибка появилась в 0.6.23. ++ ++ ++nginx could not be built without PCRE library; ++the bug had appeared in 0.6.23. ++ ++ ++ ++
++ ++ ++ ++ ++ ++при использовании HTTPS в рабочем процессе мог произойти segmentation fault; ++ошибка появилась в 0.6.23. ++ ++ ++a segmentation fault might occur in worker process if HTTPS was used; ++the bug had appeared in 0.6.23. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++параметр "off" в директиве ssl_session_cache; ++теперь этот параметр используется по умолчанию. ++ ++ ++the "off" parameter in the "ssl_session_cache" directive; ++now this is default parameter. ++ ++ ++ ++ ++ ++директива open_file_cache_retest переименована в open_file_cache_valid. ++ ++ ++the "open_file_cache_retest" directive was renamed ++to the "open_file_cache_valid". ++ ++ ++ ++ ++ ++директива open_file_cache_min_uses. ++ ++ ++the "open_file_cache_min_uses" directive. ++ ++ ++ ++ ++ ++модуль ngx_http_gzip_static_module. ++ ++ ++the ngx_http_gzip_static_module. ++ ++ ++ ++ ++ ++директива gzip_disable. ++ ++ ++the "gzip_disable" directive. ++ ++ ++ ++ ++ ++директиву memcached_pass можно использовать внутри блока if. ++ ++ ++the "memcached_pass" directive may be used inside the "if" block. ++ ++ ++ ++ ++ ++если внутри одного location'а использовались директивы "memcached_pass" и "if", ++то в рабочем процессе происходил segmentation fault. ++ ++ ++a segmentation fault occurred in worker process, ++if the "memcached_pass" and "if" directives were used in the same location. ++ ++ ++ ++ ++ ++если при использовании директивы satisfy_any on" были заданы директивы ++не всех модулей доступа, то заданные директивы не проверялись. ++ ++ ++if a "satisfy_any on" directive was used and not all access and auth modules ++directives were set, then other given access and auth directives ++were not tested; ++ ++ ++ ++ ++ ++параметры, заданные регулярным выражением в директиве valid_referers, ++не наследовалась с предыдущего уровня. ++ ++ ++regex parameters in a "valid_referers" directive were not inherited ++from previous level. ++ ++ ++ ++ ++ ++директива post_action не работала, если запрос завершался с кодом 499. ++ ++ ++a "post_action" directive did run if a request was completed ++with 499 status code. ++ ++ ++ ++ ++ ++оптимизация использования 16K буфера для SSL-соединения.
++Спасибо Ben Maurer. ++
++ ++optimization of 16K buffer usage in a SSL connection.
++Thanks to Ben Maurer. ++
++
++ ++ ++ ++STARTTLS в режиме SMTP не работал.
++Спасибо Олегу Мотиенко. ++
++ ++the STARTTLS in SMTP mode did not work.
++Thanks to Oleg Motienko. ++
++
++ ++ ++ ++при использовании HTTPS запросы могли завершаться с ошибкой "bad write retry"; ++ошибка появилась в 0.5.13. ++ ++ ++in HTTPS mode requests might fail with the "bad write retry" error; ++the bug had appeared in 0.5.13. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++теперь все методы модуля ngx_http_perl_module ++возвращают значения, скопированные в память, выделенную perl'ом. ++ ++ ++now all ngx_http_perl_module methods return values copied to perl's ++allocated memory. ++ ++ ++ ++ ++ ++если nginx был собран с модулем ngx_http_perl_module, ++использовался perl до версии 5.8.6 и perl поддерживал потоки, ++то во время переконфигурации основной процесс аварийно выходил; ++ошибка появилась в 0.5.9.
++Спасибо Борису Жмурову. ++
++ ++if nginx was built with ngx_http_perl_module, ++the perl before 5.8.6 was used, and perl supported threads, ++then during reconfiguration the master process aborted; ++the bug had appeared in 0.5.9.
++Thanks to Boris Zhmurov. ++
++
++ ++ ++ ++в методы модуля ngx_http_perl_module ++могли передаваться неверные результаты выделения в регулярных выражениях. ++ ++ ++the ngx_http_perl_module methods may get invalid values of the regex captures. ++ ++ ++ ++ ++ ++если метод $r->has_request_body() вызывался для запроса, ++у которого небольшое тело запроса было уже полностью получено, ++то в рабочем процессе происходил segmentation fault. ++ ++ ++a segmentation fault occurred in worker process, ++if the $r->has_request_body() method was called for a request ++whose small request body was already received. ++ ++ ++ ++ ++ ++large_client_header_buffers не освобождались перед переходом в состояние ++keep-alive.
++Спасибо Олександру Штепе. ++
++ ++large_client_header_buffers did not freed before going to keep-alive state.
++Thanks to Olexander Shtepa. ++
++
++ ++ ++ ++в переменной $upstream_addr не записывался последний адрес; ++ошибка появилась в 0.6.18. ++ ++ ++the last address was missed in the $upstream_addr variable; ++the bug had appeared in 0.6.18. ++ ++ ++ ++ ++ ++директива fastcgi_catch_stderr не возвращала ошибку; ++теперь она возвращает ошибку 502, которую можно направить на следующий сервер ++с помощью "fastcgi_next_upstream invalid_header". ++ ++ ++the "fastcgi_catch_stderr" directive did return error code; ++now it returns 502 code, that can be rerouted to a next server using ++the "fastcgi_next_upstream invalid_header" directive. ++ ++ ++ ++ ++ ++при использовании директивы fastcgi_catch_stderr ++в основном процессе происходил segmentation fault; ++ошибка появилась в 0.6.10.
++Спасибо Manlio Perillo. ++
++ ++a segmentation fault occurred in master process ++if the "fastcgi_catch_stderr" directive was used; ++the bug had appeared in 0.6.10.
++Thanks to Manlio Perillo. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++если в значениях переменных директивы proxy_pass используются ++только IP-адреса, то указывать resolver не нужно. ++ ++ ++if variable values used in a "proxy_pass" directive contain IP-addresses only, ++then a "resolver" directive is not mandatory. ++ ++ ++ ++ ++ ++при использовании директивы proxy_pass c URI-частью ++в рабочем процессе мог произойти segmentation fault; ++ошибка появилась в 0.6.19. ++ ++ ++a segmentation fault might occur in worker process ++if a "proxy_pass" directive with URI-part was used; ++the bug had appeared in 0.6.19. ++ ++ ++ ++ ++ ++если resolver использовался на платформах, не поддерживающих метод kqueue, ++то nginx выдавал alert "name is out of response".
++Спасибо Андрею Нигматулину. ++
++ ++if resolver was used on platform that does not support kqueue, ++then nginx issued an alert "name is out of response".
++Thanks to Andrei Nigmatulin. ++
++
++ ++ ++ ++При использовании переменной $server_protocol в FastCGI-параметрах ++и запросе, длина которого была близка к значению директивы ++client_header_buffer_size, ++nginx выдавал alert "fastcgi: the request record is too big". ++ ++ ++if the $server_protocol was used in FastCGI parameters ++and a request line length was near to the "client_header_buffer_size" ++directive value, ++then nginx issued an alert "fastcgi: the request record is too big". ++ ++ ++ ++ ++ ++при обычном запросе версии HTTP/0.9 к HTTPS серверу nginx возвращал ++обычный ответ. ++ ++ ++if a plain text HTTP/0.9 version request was made to HTTPS server, ++then nginx returned usual response. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++при использовании директивы proxy_pass c URI-частью ++в рабочем процессе мог произойти segmentation fault; ++ошибка появилась в 0.6.19. ++ ++ ++a segmentation fault might occur in worker process ++if a "proxy_pass" directive with URI-part was used; ++the bug had appeared in 0.6.19. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++версия 0.6.18 не собиралась. ++ ++ ++the 0.6.18 version could not be built. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++теперь модуль ngx_http_userid_module в поле куки с номером процесса ++добавляет микросекунды на время старта. ++ ++ ++now the ngx_http_userid_module adds start time microseconds ++to the cookie field contains a pid value. ++ ++ ++ ++ ++ ++в error_log теперь записывается полная строка запроса вместо только URI. ++ ++ ++now the full request line instead of URI only is written to error_log. ++ ++ ++ ++ ++ ++директива proxy_pass поддерживает переменные. ++ ++ ++variables support in the "proxy_pass" directive. ++ ++ ++ ++ ++ ++директивы resolver и resolver_timeout. ++ ++ ++the "resolver" and "resolver_timeout" directives. ++ ++ ++ ++ ++ ++теперь директива "add_header last-modified ''" удаляет в заголовке ответа ++строку "Last-Modified". ++ ++ ++now the directive "add_header last-modified ''" deletes a "Last-Modified" ++response header line. ++ ++ ++ ++ ++ ++директива limit_rate не позволяла передавать на полной скорости, ++даже если был указан очень большой лимит. ++ ++ ++the "limit_rate" directive did not allow to use full throughput, ++even if limit value was very high. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++поддержка строки "If-Range" в заголовке запроса.
++Спасибо Александру Инюхину. ++
++ ++the "If-Range" request header line support.
++Thanks to Alexander V. Inyukhin. ++
++
++ ++ ++ ++при использовании директивы msie_refresh повторно экранировались ++уже экранированные символы; ++ошибка появилась в 0.6.4. ++ ++ ++URL double escaping in a redirect of the "msie_refresh" directive; ++the bug had appeared in 0.6.4. ++ ++ ++ ++ ++ ++директива autoindex не работала при использовании "alias /". ++ ++ ++the "autoindex" directive did not work with the "alias /" directive. ++ ++ ++ ++ ++ ++при использовании подзапросов ++в рабочем процессе мог произойти segmentation fault. ++ ++ ++a segmentation fault might occur in worker process if subrequests were used. ++ ++ ++ ++ ++ ++при использовании SSL и gzip большие ответы могли передаваться не полностью. ++ ++ ++the big responses may be transferred truncated if SSL and gzip were used. ++ ++ ++ ++ ++ ++если ответ проксированного сервера был версии HTTP/0.9, ++то переменная $status была равна 0. ++ ++ ++the $status variable was equal to 0 if a proxied server returned response ++in HTTP/0.9 version. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++теперь на Linux используется uname(2) вместо procfs.
++Спасибо Илье Новикову. ++
++ ++now the uname(2) is used on Linux instead of procfs.
++Thanks to Ilya Novikov. ++
++
++ ++ ++ ++если в директиве error_page использовался символ "?", то он экранировался ++при проксировании запроса; ++ошибка появилась в 0.6.11. ++ ++ ++if the "?" character was in a "error_page" directive, then it was escaped ++in a proxied request; ++the bug had appeared in 0.6.11. ++ ++ ++ ++ ++ ++совместимость с mget. ++ ++ ++compatibility with mget. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++совместимость с Cygwin.
++Спасибо Владимиру Кутакову. ++
++ ++Cygwin compatibility.
++Thanks to Vladimir Kutakov. ++
++
++ ++ ++ ++директива merge_slashes. ++ ++ ++the "merge_slashes" directive. ++ ++ ++ ++ ++ ++директива gzip_vary. ++ ++ ++the "gzip_vary" directive. ++ ++ ++ ++ ++ ++директива server_tokens. ++ ++ ++the "server_tokens" directive. ++ ++ ++ ++ ++ ++nginx не раскодировал URI в команде SSI include. ++ ++ ++nginx did not unescape URI in the "include" SSI command. ++ ++ ++ ++ ++ ++при использовании переменной в директивах charset или source_charset ++на старте или во время переконфигурации происходил segmentation fault, ++ ++ ++the segmentation fault was occurred on start or while reconfiguration ++if variable was used in the "charset" or "source_charset" directives. ++ ++ ++ ++ ++ ++nginx возвращал ошибку 400 на запросы вида ++"GET http://www.domain.com HTTP/1.0".
++Спасибо James Oakley. ++
++ ++nginx returned the 400 response on requests like ++"GET http://www.domain.com HTTP/1.0".
++Thanks to James Oakley. ++
++
++ ++ ++ ++после перенаправления запроса с телом запроса с помощью директивы ++error_page nginx пытался снова прочитать тело запроса; ++ошибка появилась в 0.6.7. ++ ++ ++if request with request body was redirected using the "error_page" directive, ++then nginx tried to read the request body again; ++the bug had appeared in 0.6.7. ++ ++ ++ ++ ++ ++в рабочем процессе происходил segmentation fault, если у сервера, ++обрабатывающему запрос, не был явно определён server_name; ++ошибка появилась в 0.6.7. ++ ++ ++a segmentation fault occurred in worker process ++if no server_name was explicitly defined for server processing request; ++the bug had appeared in 0.6.7. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++теперь по умолчанию команда SSI echo использует кодирование entity. ++ ++ ++now by default the "echo" SSI command uses entity encoding. ++ ++ ++ ++ ++ ++параметр encoding в команде SSI echo. ++ ++ ++the "encoding" parameter in the "echo" SSI command. ++ ++ ++ ++ ++ ++директиву access_log можно использовать внутри блока limit_except. ++ ++ ++the "access_log" directive may be used inside the "limit_except" block. ++ ++ ++ ++ ++ ++если все сервера апстрима оказывались недоступными, ++то до восстановления работоспособности ++у всех серверов вес становился равным одному; ++ошибка появилась в 0.6.6. ++ ++ ++if all upstream servers were failed, then all servers had got weight ++the was equal one until servers became alive; ++the bug had appeared in 0.6.6. ++ ++ ++ ++ ++ ++при использовании переменных $date_local и $date_gmt вне модуля ++ngx_http_ssi_filter_module в рабочем процессе происходил segmentation fault. ++ ++ ++a segmentation fault occurred in worker process ++if $date_local and $date_gmt were used outside the ngx_http_ssi_filter_module. ++ ++ ++ ++ ++ ++при использовании включённом отладочном логе ++в рабочем процессе мог произойти segmentation fault.
++Спасибо Андрею Нигматулину. ++
++ ++a segmentation fault might occur in worker process ++if debug log was enabled.
++Thanks to Andrei Nigmatulin. ++
++
++ ++ ++ ++ngx_http_memcached_module не устанавливал $upstream_response_time.
++Спасибо Максиму Дунину. ++
++ ++ngx_http_memcached_module did not set $upstream_response_time.
++Thanks to Maxim Dounin. ++
++
++ ++ ++ ++рабочий процесс мог зациклиться при использовании memcached. ++ ++ ++a worker process may got caught in an endless loop, if the memcached was used. ++ ++ ++ ++ ++ ++nginx распознавал параметры "close" и "keep-alive" в строке "Connection" ++в заголовке запроса только, если они были в нижнем регистре; ++ошибка появилась в 0.6.11. ++ ++ ++nginx supported low case only "close" and "keep-alive" values ++in the "Connection" request header line; ++the bug had appeared in 0.6.11. ++ ++ ++ ++ ++ ++sub_filter не работал с пустой строкой замены. ++ ++ ++sub_filter did not work with empty substitution. ++ ++ ++ ++ ++ ++в парсинге sub_filter. ++ ++ ++in sub_filter parsing. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++nginx не закрывал файл каталога для запроса HEAD, ++если использовался autoindex
++Спасибо Arkadiusz Patyk. ++
++ ++nginx did not close directory file on HEAD request if autoindex was used.
++Thanks to Arkadiusz Patyk. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++почтовый прокси-сервер разделён на три модуля: pop3, imap и smtp. ++ ++ ++mail proxy was split on three modules: pop3, imap and smtp. ++ ++ ++ ++ ++ ++параметры конфигурации --without-mail_pop3_module, ++--without-mail_imap_module и --without-mail_smtp_module. ++ ++ ++the --without-mail_pop3_module, --without-mail_imap_module, ++and --without-mail_smtp_module configuration parameters. ++ ++ ++ ++ ++ ++директивы smtp_greeting_delay и smtp_client_buffer модуля ngx_mail_smtp_module. ++ ++ ++the "smtp_greeting_delay" and "smtp_client_buffer" directives ++of the ngx_mail_smtp_module. ++ ++ ++ ++ ++ ++wildcard в конце имени сервера не работали; ++ошибка появилась в 0.6.9. ++ ++ ++the trailing wildcards did not work; ++the bug had appeared in 0.6.9. ++ ++ ++ ++ ++ ++при использовании разделяемой библиотеки PCRE, ++расположенной в нестандартном месте, nginx не запускался на Solaris. ++ ++ ++nginx could not start on Solaris if the shared PCRE library located ++in non-standard place was used. ++ ++ ++ ++ ++ ++директивы proxy_hide_header и fastcgi_hide_header не скрывали ++строки заголовка ответа с именем больше 32 символов.
++Спасибо Manlio Perillo. ++
++ ++the "proxy_hide_header" and "fastcgi_hide_header" directives did not ++hide response header lines whose name was longer than 32 characters.
++Thanks to Manlio Perillo. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++счётчик активных соединений всегда рос при использовании почтового ++прокси-сервера. ++ ++ ++active connection counter always increased if mail proxy was used. ++ ++ ++ ++ ++ ++если бэкенд возвращал только заголовок ответа при небуферизированном ++проксировании, то nginx закрывал соединение с бэкендом по таймауту. ++ ++ ++if backend returned response header only using non-buffered proxy, ++then nginx closed backend connection on timeout. ++ ++ ++ ++ ++ ++nginx не поддерживал несколько строк "Connection" в заголовке запроса. ++ ++ ++nginx did not support several "Connection" request header lines. ++ ++ ++ ++ ++ ++если в сервере апстрима был задан max_fails, то после первой же неудачной ++попытки вес сервера навсегда становился равным одному; ++ошибка появилась в 0.6.6. ++ ++ ++if the "max_fails" was set for upstream server, then after first ++failure server weight was always one; ++the bug had appeared in 0.6.6. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директивы open_file_cache, open_file_cache_retest и open_file_cache_errors. ++ ++ ++the "open_file_cache", "open_file_cache_retest", and "open_file_cache_errors" ++directives. ++ ++ ++ ++ ++ ++утечки сокетов; ++ошибка появилась в 0.6.7. ++ ++ ++socket leak; ++the bug had appeared in 0.6.7. ++ ++ ++ ++ ++ ++В строку заголовка ответа "Content-Type", указанную в методе ++$r->send_http_header(), не добавлялась кодировка, указанная в директиве charset. ++ ++ ++a charset set by the "charset" directive was not appended ++to the "Content-Type" header set by $r->send_http_header(). ++ ++ ++ ++ ++ ++при использовании метода /dev/poll ++в рабочем процессе мог произойти segmentation fault. ++ ++ ++a segmentation fault might occur in worker process ++if /dev/poll method was used. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++рабочий процесс мог зациклиться при использовании протокола HTTPS; ++ошибка появилась в 0.6.7. ++ ++ ++a worker process may got caught in an endless loop, ++if the HTTPS protocol was used; ++the bug had appeared in 0.6.7. ++ ++ ++ ++ ++ ++если сервер слушал на двух адресах или портах, то nginx не запускался ++при использовании wildcard в конце имени сервера. ++ ++ ++if server listened on two addresses or ports and trailing wildcard was used, ++then nginx did not run. ++ ++ ++ ++ ++ ++директива ip_hash могла неверно помечать сервера как нерабочие. ++ ++ ++the "ip_hash" directive might incorrectly mark servers as down. ++ ++ ++ ++ ++ ++nginx не собирался на amd64; ++ошибка появилась в 0.6.8. ++ ++ ++nginx could not be built on amd64; ++the bug had appeared in 0.6.8. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++теперь nginx пытается установить директивы worker_priority, ++worker_rlimit_nofile, worker_rlimit_core, worker_rlimit_sigpending ++без привилегий root'а. ++ ++ ++now nginx tries to set the "worker_priority", "worker_rlimit_nofile", ++"worker_rlimit_core", and "worker_rlimit_sigpending" without super-user ++privileges. ++ ++ ++ ++ ++ ++теперь nginx экранирует символы пробела и "%" при передаче запроса ++серверу аутентификации почтового прокси-сервера. ++ ++ ++now nginx escapes space and "%" in request to a mail proxy authentication ++server. ++ ++ ++ ++ ++ ++теперь nginx экранирует символ "%" в переменной $memcached_key. ++ ++ ++now nginx escapes "%" in $memcached_key variable. ++ ++ ++ ++ ++ ++при указании относительного пути к конфигурационному файлу в качестве ++параметра ключа -c nginx определял путь относительно конфигурационного префикса; ++ошибка появилась в 0.6.6. ++ ++ ++nginx used path relative to configuration prefix for non-absolute ++configuration file path specified in the "-c" key; ++the bug had appeared in 0.6.6. ++ ++ ++ ++ ++ ++nginx не работал на FreeBSD/sparc64. ++ ++ ++nginx did not work on FreeBSD/sparc64. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++теперь пути, указанные в директивах include, auth_basic_user_file, ++perl_modules, ssl_certificate, ssl_certificate_key и ++ssl_client_certificate, определяются относительно каталога конфигурационного ++файла nginx.conf, а не относительно префикса. ++ ++ ++now the paths specified in the "include", "auth_basic_user_file", ++"perl_modules", "ssl_certificate", "ssl_certificate_key", and ++"ssl_client_certificate" directives are relative to directory of ++nginx configuration file nginx.conf, but not to nginx prefix directory. ++ ++ ++ ++ ++ ++параметр --sysconfdir=PATH в configure упразднён. ++ ++ ++the --sysconfdir=PATH option in configure was canceled. ++ ++ ++ ++ ++ ++для обновления на лету версий 0.1.x создан специальный сценарий ++make upgrade1. ++ ++ ++the special make target "upgrade1" was defined for online upgrade of ++0.1.x versions. ++ ++ ++ ++ ++ ++директивы server_name и valid_referers поддерживают регулярные выражения. ++ ++ ++the "server_name" and "valid_referers" directives support regular expressions. ++ ++ ++ ++ ++ ++директива server в блоке upstream поддерживает параметр backup. ++ ++ ++the "server" directive in the "upstream" context supports ++the "backup" parameter. ++ ++ ++ ++ ++ ++модуль ngx_http_perl_module поддерживает метод $r->discard_request_body. ++ ++ ++the ngx_http_perl_module supports the $r->discard_request_body. ++ ++ ++ ++ ++ ++директива "add_header Last-Modified ..." меняет строку "Last-Modified" ++в заголовке ответа. ++ ++ ++the "add_header Last-Modified ..." directive changes the "Last-Modified" ++response header line. ++ ++ ++ ++ ++ ++если на запрос с телом возвращался ответ с кодом HTTP отличным от 200, ++и после этого запроса соединение переходило в состояние keep-alive, ++то на следующий запрос nginx возвращал 400. ++ ++ ++if a response different than 200 was returned to a request with body ++and connection went to the keep-alive state after the request, then ++nginx returned 400 for the next request. ++ ++ ++ ++ ++ ++если в директиве auth_http был задан неправильный адрес, то ++в рабочем процессе происходил segmentation fault. ++ ++ ++a segmentation fault occurred in worker process ++if invalid address was set in the "auth_http" directive. ++ ++ ++ ++ ++ ++теперь по умолчанию nginx использует значение 511 для listen backlog ++на всех платформах, кроме FreeBSD.
++Спасибо Jiang Hong. ++
++ ++now nginx uses default listen backlog value 511 on all platforms ++except FreeBSD.
++Thanks to Jiang Hong. ++
++
++ ++ ++ ++рабочий процесс мог зациклиться, если server в блоке upstream был помечен ++как down; ++ошибка появилась в 0.6.6. ++ ++ ++a worker process may got caught in an endless loop, if a "server" inside ++"upstream" block was marked as "down"; ++the bug had appeared in 0.6.6. ++ ++ ++ ++ ++ ++sendfilev() в Solaris теперь не используется при передаче тела запроса ++FastCGI-серверу через unix domain сокет. ++ ++ ++now Solaris sendfilev() is not used to transfer the client request body ++to FastCGI-server via the unix domain socket. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++параметр --sysconfdir=PATH в configure. ++ ++ ++the --sysconfdir=PATH option in configure. ++ ++ ++ ++ ++ ++именованные location'ы. ++ ++ ++named locations. ++ ++ ++ ++ ++ ++переменную $args можно устанавливать с помощью set. ++ ++ ++the $args variable can be set with the "set" directive. ++ ++ ++ ++ ++ ++переменная $is_args. ++ ++ ++the $is_args variable. ++ ++ ++ ++ ++ ++равномерное распределение запросов к апстримам с большими весами. ++ ++ ++fair big weight upstream balancer. ++ ++ ++ ++ ++ ++если клиент в почтовом прокси-сервере закрывал соединение, ++то nginx мог не закрывать соединение с бэкендом. ++ ++ ++if a client has closed connection to mail proxy ++ then nginx might not close connection to backend. ++ ++ ++ ++ ++ ++при использовании одного хоста в качестве бэкендов для протоколов HTTP и HTTPS ++без явного указания портов, nginx использовал только один порт—80 или 443. ++ ++ ++if the same host without specified port was used as backend for HTTP and HTTPS, ++then nginx used only one port—80 or 443. ++ ++ ++ ++ ++ ++nginx не собирался на Solaris/amd64 Sun Studio 11 и более ранними версиями; ++ошибка появилась в 0.6.4. ++ ++ ++fix building on Solaris/amd64 by Sun Studio 11 and early versions; ++the bug had appeared in 0.6.4. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++переменная $nginx_version.
++Спасибо Николаю Гречуху. ++
++ ++$nginx_version variable.
++Thanks to Nick S. Grechukh. ++
++
++ ++ ++ ++почтовый прокси-сервер поддерживает AUTHENTICATE в режиме IMAP.
++Спасибо Максиму Дунину. ++
++ ++the mail proxy supports AUTHENTICATE in IMAP mode.
++Thanks to Maxim Dounin. ++
++
++ ++ ++ ++почтовый прокси-сервер поддерживает STARTTLS в режиме SMTP.
++Спасибо Максиму Дунину. ++
++ ++the mail proxy supports STARTTLS in SMTP mode.
++Thanks to Maxim Dounin. ++
++
++ ++ ++ ++теперь nginx экранирует пробел в переменной $memcached_key. ++ ++ ++now nginx escapes space in $memcached_key variable. ++ ++ ++ ++ ++ ++nginx неправильно собирался Sun Studio на Solaris/amd64.
++Спасибо Jiang Hong. ++
++ ++nginx was incorrectly built by Sun Studio on Solaris/amd64.
++Thanks to Jiang Hong. ++
++
++ ++ ++ ++незначительных потенциальных ошибок.
++Спасибо Coverity's Scan. ++
++ ++of minor potential bugs.
++Thanks to Coverity's Scan. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++при использовании директивы msie_refresh был возможен XSS.
++Спасибо Максиму Богуку. ++
++ ++the "msie_refresh" directive allowed XSS.
++Thanks to Maxim Boguk. ++
++
++ ++ ++ ++директивы proxy_store и fastcgi_store изменены. ++ ++ ++the "proxy_store" and "fastcgi_store" directives were changed. ++ ++ ++ ++ ++ ++директивы proxy_store_access и fastcgi_store_access. ++ ++ ++the "proxy_store_access" and "fastcgi_store_access" directives. ++ ++ ++ ++ ++ ++nginx не работал на Solaris/sparc64, если был собран Sun Studio.
++Спасибо Андрею Нигматулину. ++
++ ++nginx did not work on Solaris/sparc64 if it was built by Sun Studio.
++Thanks to Andrei Nigmatulin. ++
++
++ ++ ++ ++обход ошибки в Sun Studio 12.
++Спасибо Jiang Hong. ++
++ ++for Sun Studio 12.
++Thanks to Jiang Hong. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++директивы proxy_store и fastcgi_store. ++ ++ ++the "proxy_store" and "fastcgi_store" directives. ++ ++ ++ ++ ++ ++при использовании директивы auth_http_header ++в рабочем процессе мог произойти segmentation fault.
++Спасибо Максиму Дунину. ++
++ ++a segmentation fault might occur in worker process ++if the "auth_http_header" directive was used.
++Thanks to Maxim Dounin. ++
++
++ ++ ++ ++если использовался метод аутентификации CRAM-MD5, но он не был разрешён, ++то в рабочем процессе происходил segmentation fault. ++ ++ ++a segmentation fault occurred in worker process ++if the CRAM-MD5 authentication method was used, but it was not enabled. ++ ++ ++ ++ ++ ++при использовании протокола HTTPS в директиве proxy_pass ++в рабочем процессе мог произойти segmentation fault. ++ ++ ++a segmentation fault might occur in worker process when ++the HTTPS protocol was used in the "proxy_pass" directive. ++ ++ ++ ++ ++ ++в рабочем процессе мог произойти segmentation fault, ++если использовался метод eventport. ++ ++ ++a segmentation fault might occur in worker process ++if the eventport method was used. ++ ++ ++ ++ ++ ++директивы proxy_ignore_client_abort и fastcgi_ignore_client_abort не работали; ++ошибка появилась в 0.5.13. ++ ++ ++the "proxy_ignore_client_abort" and "fastcgi_ignore_client_abort" directives ++did not work; ++the bug had appeared in 0.5.13. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++если заголовок ответа был разделён в FastCGI-записях, то nginx передавал ++клиенту мусор в таких заголовках. ++ ++ ++if the FastCGI header was split in records, ++then nginx passed garbage in the header to a client. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++в парсинге SSI. ++ ++ ++in SSI parsing. ++ ++ ++ ++ ++ ++при использовании удалённого подзапроса в SSI последующий ++подзапрос локального файла мог отдаваться клиенту в неверном порядке. ++ ++ ++if remote SSI subrequest was used, then posterior local file subrequest ++might transferred to client in wrong order. ++ ++ ++ ++ ++ ++большие включения в SSI, сохранённые во временные файлы, ++передавались не полностью. ++ ++ ++large SSI inclusions buffered in temporary files were truncated. ++ ++ ++ ++ ++ ++значение perl'овой переменной $$ модуля ngx_http_perl_module было равно ++номеру главного процесса. ++ ++ ++the perl $$ variable value in ngx_http_perl_module was equal to the master ++process identification number. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директивы "server_name", "map", and "valid_referers" поддерживают ++маски вида "www.example.*". ++ ++ ++the "server_name", "map", and "valid_referers" directives support ++the "www.example.*" wildcards. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++nginx не собирался с параметром --without-http_rewrite_module; ++ошибка появилась в 0.5.24. ++ ++ ++nginx could not be built with the --without-http_rewrite_module parameter; ++the bug had appeared in 0.5.24. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директива ssl_verify_client не работала, если запрос выполнялся ++по протоколу HTTP/0.9. ++ ++ ++the "ssl_verify_client" directive did not work if request was made ++using HTTP/0.9. ++ ++ ++ ++ ++ ++при использовании сжатия часть ответа могла передаваться несжатой; ++ошибка появилась в 0.5.23. ++ ++ ++a part of response body might be passed uncompressed if gzip was used; ++the bug had appeared in 0.5.23. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++модуль ngx_http_ssl_module поддерживает расширение TLS Server Name Indication. ++ ++ ++the ngx_http_ssl_module supports Server Name Indication TLS extension. ++ ++ ++ ++ ++ ++директива fastcgi_catch_stderr.
++Спасибо Николаю Гречуху, проект OWOX. ++
++ ++the "fastcgi_catch_stderr" directive.
++Thanks to Nick S. Grechukh, OWOX project. ++
++
++ ++ ++ ++на Линуксе в основном процессе происходил segmentation fault, ++если два виртуальных сервера должны bind()ится к пересекающимся портам. ++ ++ ++a segmentation fault occurred in master process if ++two virtual servers should bind() to the overlapping ports. ++ ++ ++ ++ ++ ++если nginx был собран с модулем ngx_http_perl_module и perl ++поддерживал потоки, то во время второй переконфигурации ++выдавались ошибки "panic: MUTEX_LOCK" и "perl_parse() failed". ++ ++ ++if nginx was built with ngx_http_perl_module and perl supported threads, ++then during second reconfiguration the error messages ++"panic: MUTEX_LOCK" and "perl_parse() failed" were issued. ++ ++ ++ ++ ++ ++в использовании протокола HTTPS в директиве proxy_pass. ++ ++ ++in the HTTPS protocol in the "proxy_pass" directive. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++большое тело запроса могло не передаваться бэкенду; ++ошибка появилась в 0.5.21. ++ ++ ++a big request body might not be passed to backend; ++the bug had appeared in 0.5.21. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++если внутри сервера описано больше примерно десяти location'ов, ++то location'ы, заданные с помощью регулярного выражения, ++могли выполняться не в том, порядке, в каком они описаны. ++ ++ ++if server has more than about ten locations, then regex locations ++might be chosen not in that order as they were specified. ++ ++ ++ ++ ++ ++на 64-битной платформе рабочий процесс мог зациклиться, если 33-тий ++по счёту или последующий бэкенд упал.
++Спасибо Антону Поварову. ++
++ ++a worker process may got caught in an endless loop on 64-bit platform, ++if the 33-rd or next in succession backend has failed.
++Thanks to Anton Povarov. ++
++
++ ++ ++ ++при использовании библиотеки PCRE на Solaris/sparc64 ++мог произойти bus error.
++Спасибо Андрею Нигматулину. ++
++ ++a bus error might occur on Solaris/sparc64 if the PCRE library was used.
++Thanks to Andrei Nigmatulin. ++
++
++ ++ ++ ++в использовании протокола HTTPS в директиве proxy_pass. ++ ++ ++in the HTTPS protocol in the "proxy_pass" directive. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++директива sendfile_max_chunk. ++ ++ ++the "sendfile_max_chunk" directive. ++ ++ ++ ++ ++ ++переменные "$http_...", "$sent_http_..." и "$upstream_http_..." ++можно менять директивой set. ++ ++ ++the "$http_...", "$sent_http_...", and "$upstream_http_..." variables ++may be changed using the "set" directive. ++ ++ ++ ++ ++ ++при использовании SSI-команды 'if expr="$var = /"' ++в рабочем процессе мог произойти segmentation fault. ++ ++ ++a segmentation fault might occur in worker process ++if the SSI command 'if expr="$var = /"' was used. ++ ++ ++ ++ ++ ++завершающая строка multipart range ответа передавалась неверно.
++Спасибо Evan Miller. ++
++ ++trailing boundary of multipart range response was transferred incorrectly.
++Thanks to Evan Miller. ++
++
++ ++ ++ ++nginx не работал на Solaris/sparc64, если был собран Sun Studio.
++Спасибо Андрею Нигматулину. ++
++ ++nginx did not work on Solaris/sparc64 if it was built by Sun Studio.
++Thanks to Andrei Nigmatulin. ++
++
++ ++ ++ ++модуль ngx_http_perl_module не собирался make в Solaris.
++Спасибо Андрею Нигматулину. ++
++ ++the ngx_http_perl_module could not be built by Solaris make.
++Thanks to Andrei Nigmatulin. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++значение переменной $request_time теперь записывается с точностью ++до миллисекунд. ++ ++ ++now the $request_time variable has millisecond precision. ++ ++ ++ ++ ++ ++метод $r->rflush в модуле ngx_http_perl_module переименован в $r->flush. ++ ++ ++the method $r->rflush of ngx_http_perl_module was renamed to the $r->flush. ++ ++ ++ ++ ++ ++переменная $upstream_addr. ++ ++ ++the $upstream_addr variable. ++ ++ ++ ++ ++ ++директивы proxy_headers_hash_max_size и proxy_headers_hash_bucket_size.
++Спасибо Володымыру Костырко. ++
++ ++the "proxy_headers_hash_max_size" and "proxy_headers_hash_bucket_size" ++directives.
++Thanks to Volodymyr Kostyrko. ++
++
++ ++ ++ ++при использовании sendfile и limit_rate на 64-битных платформах ++нельзя было передавать файлы больше 2G. ++ ++ ++the files more than 2G could not be transferred using sendfile and limit_rate ++on 64-bit platforms. ++ ++ ++ ++ ++ ++при использовании sendfile на 64-битном Linux нельзя было передавать файлы ++больше 2G. ++ ++ ++the files more than 2G could not be transferred using sendfile on 64-bit Linux. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++модуль ngx_http_sub_filter_module. ++ ++ ++the ngx_http_sub_filter_module. ++ ++ ++ ++ ++ ++переменные "$upstream_http_...". ++ ++ ++the "$upstream_http_..." variables. ++ ++ ++ ++ ++ ++теперь переменные $upstream_status и $upstream_response_time ++содержат данные о всех обращениях к апстримам, сделанным до X-Accel-Redirect. ++ ++ ++now the $upstream_status and $upstream_response_time variables ++keep data about all upstreams before X-Accel-Redirect. ++ ++ ++ ++ ++ ++если nginx был собран с модулем ngx_http_perl_module и perl ++не поддерживал multiplicity, то после первой переконфигурации ++и после получения любого сигнала ++в основном процессе происходил segmentation fault; ++ошибка появилась в 0.5.9. ++ ++ ++a segmentation fault occurred in master process ++after first reconfiguration and receiving any signal ++if nginx was built with ngx_http_perl_module and perl ++did not support multiplicity; ++the bug had appeared in 0.5.9. ++ ++ ++ ++ ++ ++если perl не поддерживал multiplicity, то после переконфигурации ++перловый код не работал; ++ошибка появилась в 0.3.38. ++ ++ ++if perl did not support multiplicity, then after reconfiguration ++perl code did not work; ++the bug had appeared in 0.3.38. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++теперь nginx для метода TRACE всегда возвращает код 405. ++ ++ ++now nginx always returns the 405 status for the TRACE method. ++ ++ ++ ++ ++ ++теперь nginx поддерживает директиву include внутри блока types. ++ ++ ++now nginx supports the "include" directive inside the "types" block. ++ ++ ++ ++ ++ ++использование переменной $document_root в директиве root и alias ++запрещено: оно вызывало рекурсивное переполнение стека. ++ ++ ++the $document_root variable usage in the "root" and "alias" directives ++is disabled: this caused recursive stack overflow. ++ ++ ++ ++ ++ ++в использовании протокола HTTPS в директиве proxy_pass. ++ ++ ++in the HTTPS protocol in the "proxy_pass" directive. ++ ++ ++ ++ ++ ++в некоторых случаях некэшируемые переменные (такие, как $uri) ++возвращали старое закэшированное значение. ++ ++ ++in some cases non-cacheable variables (such as $uri variable) ++returned old cached value. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++в качестве ключа для хэша в директиве ip_hash не использовалась сеть ++класса С.
++Спасибо Павлу Ярковому. ++
++ ++the C-class network was not used as hash key in the "ip_hash" directive.
++Thanks to Pavel Yarkovoy. ++
++
++ ++ ++ ++если в строке "Content-Type" в заголовке ответа бэкенда был указан charset ++и строка завершалась символом ";", ++то в рабочем процессе мог произойти segmentation fault; ++ошибка появилась в 0.3.50. ++ ++ ++a segmentation fault might occur in worker process ++if a charset was set in the "Content-Type" header line and the line ++has trailing ";"; ++the bug had appeared in 0.3.50. ++ ++ ++ ++ ++ ++ошибки "[alert] zero size buf" при работе с FastCGI-сервером, если ++тело запроса, записанное во временный файл, было кратно 32K. ++ ++ ++the "[alert] zero size buf" error when FastCGI server was used and ++a request body written in a temporary file was multiple of 32K. ++ ++ ++ ++ ++ ++nginx не собирался на Solaris без параметра --with-debug; ++ошибка появилась в 0.5.15. ++ ++ ++nginx could not be built on Solaris without the --with-debug option; ++the bug had appeared in 0.5.15. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++почтовый прокси-сервер поддерживает аутентифицированное SMTP-проксирование и ++директивы smtp_auth, smtp_capabilities и xclient.
++Спасибо Антону Южанинову и Максиму Дунину. ++
++ ++the mail proxy supports authenticated SMTP proxying and ++the "smtp_auth", "smtp_capabilities", and "xclient" directives.
++Thanks to Anton Yuzhaninov and Maxim Dounin. ++
++
++ ++ ++ ++теперь keep-alive соединения закрываются сразу же по получении сигнала ++переконфигурации. ++ ++ ++now the keep-alive connections are closed just after receiving ++the reconfiguration signal. ++ ++ ++ ++ ++ ++директивы imap и auth переименованы соответственно в mail и pop3_auth. ++ ++ ++the "imap" and "auth" directives were renamed ++to the "mail" and "pop3_auth" directives. ++ ++ ++ ++ ++ ++если использовался метод аутентификации CRAM-MD5 и не был разрешён метод APOP, ++то в рабочем процессе происходил segmentation fault. ++ ++ ++a segmentation fault occurred in worker process ++if the CRAM-MD5 authentication method was used ++and the APOP method was disabled. ++ ++ ++ ++ ++ ++при использовании директивы starttls only в протоколе POP3 nginx ++разрешал аутентификацию без перехода в режим SSL. ++ ++ ++if the "starttls only" directive was used in POP3 protocol, ++then nginx allowed authentication without switching to the SSL mode. ++ ++ ++ ++ ++ ++рабочие процессы не выходили после переконфигурации и не переоткрывали логи, ++если использовался метод eventport. ++ ++ ++worker processes did not exit after reconfiguration and ++did not rotate logs if the eventport method was used. ++ ++ ++ ++ ++ ++при использовании директивы ip_hash рабочий процесс мог зациклиться. ++ ++ ++a worker process may got caught in an endless loop, ++if the "ip_hash" directive was used. ++ ++ ++ ++ ++ ++теперь nginx не пишет в лог некоторые alert'ы, ++если используются методы eventport или /dev/poll. ++ ++ ++now nginx does not log some alerts if eventport or /dev/poll methods are used. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++nginx игнорировал лишние закрывающие скобки "}" в конце ++конфигурационного файла. ++ ++ ++nginx ignored superfluous closing "}" in the end of configuration file. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++методы COPY и MOVE. ++ ++ ++the COPY and MOVE methods. ++ ++ ++ ++ ++ ++модуль ngx_http_realip_module устанавливал мусор для запросов, ++переданных по keep-alive соединению. ++ ++ ++the ngx_http_realip_module set garbage for requests passed via ++keep-alive connection. ++ ++ ++ ++ ++ ++nginx не работал на 64-битном big-endian Linux.
++Спасибо Андрею Нигматулину. ++
++ ++nginx did not work on big-endian 64-bit Linux.
++Thanks to Andrei Nigmatulin. ++
++
++ ++ ++ ++при получении слишком длинной команды IMAP/POP3-прокси теперь сразу ++закрывает соединение, а не по таймауту. ++ ++ ++now when IMAP/POP3 proxy receives too long command it closes the connection ++right away, but not after timeout. ++ ++ ++ ++ ++ ++если при использовании метода epoll клиент закрывал преждевременно ++соединение со своей стороны, то nginx закрывал это соединение только ++по истечении таймаута на передачу. ++ ++ ++if the "epoll" method was used and a client closed a connection prematurely, ++then nginx closed the connection after a send timeout only. ++ ++ ++ ++ ++ ++nginx не собирался на платформах, отличных от i386, amd64, sparc и ppc; ++ошибка появилась в 0.5.8. ++ ++ ++nginx could not be built on platforms different from i386, amd64, sparc, ++and ppc; ++the bug had appeared in 0.5.8. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++nginx не собирался на платформах, отличных от i386, amd64, sparc и ppc; ++ошибка появилась в 0.5.8. ++ ++ ++nginx could not be built on platforms different from i386, amd64, sparc, ++and ppc; ++the bug had appeared in 0.5.8. ++ ++ ++ ++ ++ ++при использовании временных файлов в время работы с FastCGI-сервером ++в рабочем процессе мог произойти segmentation fault; ++ошибка появилась в 0.5.8. ++ ++ ++a segmentation fault might occur in worker process ++if the temporary files were used while working with FastCGI server; ++the bug had appeared in 0.5.8. ++ ++ ++ ++ ++ ++если переменная $fastcgi_script_name записывалась в лог, ++то в рабочем процессе мог произойти segmentation fault. ++ ++ ++a segmentation fault might occur in worker process ++if the $fastcgi_script_name variable was logged. ++ ++ ++ ++ ++ ++ngx_http_perl_module не собирался на Solaris. ++ ++ ++ngx_http_perl_module could not be built on Solaris. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++теперь configure определяет библиотеку PCRE в MacPorts.
++Спасибо Chris McGrath. ++
++ ++now configure detects system PCRE library in MacPorts.
++Thanks to Chris McGrath. ++
++
++ ++ ++ ++ответ был неверным, если запрашивалось несколько диапазонов; ++ошибка появилась в 0.5.6. ++ ++ ++the response was incorrect if several ranges were requested; ++the bug had appeared in 0.5.6. ++ ++ ++ ++ ++ ++директива create_full_put_path не могла создавать промежуточные каталоги, ++если не была установлена директива dav_access.
++Спасибо Evan Miller. ++
++ ++the "create_full_put_path" directive could not create the intermediate ++directories if no "dav_access" directive was set.
++Thanks to Evan Miller. ++
++
++ ++ ++ ++вместо кодов ошибок "400" и "408" в access_log мог записываться код "0". ++ ++ ++the "0" response code might be logged in the access_log instead of ++the "400" and "408" error codes. ++ ++ ++ ++ ++ ++при сборке с оптимизацией -O2 в рабочем процессе мог произойти ++segmentation fault. ++ ++ ++a segmentation fault might occur in worker process ++if nginx was built with -O2 optimization. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++во время обновления исполняемого файла новый процесс не наследовал ++слушающие сокеты; ++ошибка появилась в 0.5.9. ++ ++ ++while online executable file upgrade the new master process did not ++inherit the listening sockets; ++the bug had appeared in 0.5.9. ++ ++ ++ ++ ++ ++при сборке с оптимизацией -O2 в рабочем процессе мог произойти ++segmentation fault; ++ошибка появилась в 0.5.1. ++ ++ ++a segmentation fault might occur in worker process ++if nginx was built with -O2 optimization; ++the bug had appeared in 0.5.1. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++модуль ngx_http_memcached_module теперь в качестве ключа использует ++значение переменной $memcached_key. ++ ++ ++now the ngx_http_memcached_module uses the $memcached_key variable value ++as a key. ++ ++ ++ ++ ++ ++переменная $memcached_key. ++ ++ ++the $memcached_key variable. ++ ++ ++ ++ ++ ++параметр clean в директиве client_body_in_file_only. ++ ++ ++the "clean" parameter in the "client_body_in_file_only" directive. ++ ++ ++ ++ ++ ++директива env. ++ ++ ++the "env" directive. ++ ++ ++ ++ ++ ++директива sendfile работает внутри блока if. ++ ++ ++the "sendfile" directive is available inside the "if" block. ++ ++ ++ ++ ++ ++теперь при ошибке записи в access_log nginx записывает сообщение в error_log, ++но не чаще одного раза в минуту. ++ ++ ++now on failure of the writing to access nginx logs a message to error_log, ++but not more often than once a minute. ++ ++ ++ ++ ++ ++директива "access_log off" не всегда запрещала запись в лог. ++ ++ ++the "access_log off" directive did not always turn off the logging. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++если использовалась директива "client_body_in_file_only on" ++и тело запроса было небольшое, то мог произойти segmentation fault. ++ ++ ++a segmentation fault might occur if ++"client_body_in_file_only on" was used ++and a request body was small. ++ ++ ++ ++ ++ ++происходил segmentation fault, если использовались директивы ++"client_body_in_file_only on" ++и "proxy_pass_request_body off" ++или "fastcgi_pass_request_body off", ++и делался переход к следующему бэкенду. ++ ++ ++a segmentation fault occurred if "client_body_in_file_only on" ++and "proxy_pass_request_body off" ++or "fastcgi_pass_request_body off" ++directives were used, and nginx switched to a next upstream. ++ ++ ++ ++ ++ ++если при использовании директивы "proxy_buffering off" соединение с клиентом ++было неактивно, то оно закрывалось по таймауту, заданному директивой ++send_timeout; ++ошибка появилась в 0.4.7. ++ ++ ++if the "proxy_buffering off" directive was used and a client connection ++was non-active, then the connection was closed after send timeout; ++the bug had appeared in 0.4.7. ++ ++ ++ ++ ++ ++если при использовании метода epoll клиент закрывал преждевременно ++соединение со своей стороны, то nginx закрывал это соединение только ++по истечении таймаута на передачу. ++ ++ ++if the "epoll" method was used and a client closed a connection prematurely, ++then nginx closed the connection after a send timeout only. ++ ++ ++ ++ ++ ++ошибки "[alert] zero size buf" при работе с FastCGI-сервером. ++ ++ ++the "[alert] zero size buf" error when FastCGI server was used. ++ ++ ++ ++ ++ ++Исправление ошибок в директиве limit_zone. ++ ++ ++Bugfixes in the "limit_zone" directive. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++оптимизация использования памяти в ssl_session_cache. ++ ++ ++the ssl_session_cache storage optimization. ++ ++ ++ ++ ++ ++Исправление ошибок в директивах ssl_session_cache и limit_zone. ++ ++ ++Bugfixes in the "ssl_session_cache" and "limit_zone" directives. ++ ++ ++ ++ ++ ++на старте или во время переконфигурации происходил segmentation fault, ++если директивы ssl_session_cache или limit_zone использовались ++на 64-битных платформах. ++ ++ ++the segmentation fault was occurred on start or while reconfiguration ++if the "ssl_session_cache" or "limit_zone" directives were used ++on 64-bit platforms. ++ ++ ++ ++ ++ ++при использовании директив add_before_body или add_after_body происходил ++segmentation fault, если в заголовке ответа нет строки "Content-Type". ++ ++ ++a segmentation fault occurred if the "add_before_body" or "add_after_body" ++directives were used and there was no "Content-Type" header line in response. ++ ++ ++ ++ ++ ++библиотека OpenSSL всегда собиралась с поддержкой потоков.
++Спасибо Дену Иванову. ++
++ ++the OpenSSL library was always built with the threads support.
++Thanks to Den Ivanov. ++
++
++ ++ ++ ++совместимость библиотеки PCRE-6.5+ и компилятора icc. ++ ++ ++the PCRE-6.5+ library and the icc compiler compatibility. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++теперь модуль ngx_http_index_module игнорирует все методы, ++кроме GET, HEAD и POST. ++ ++ ++now the ngx_http_index_module ignores all methods except the GET, HEAD, and ++POST methods. ++ ++ ++ ++ ++ ++модуль ngx_http_limit_zone_module. ++ ++ ++the ngx_http_limit_zone_module. ++ ++ ++ ++ ++ ++переменная $binary_remote_addr. ++ ++ ++the $binary_remote_addr variable. ++ ++ ++ ++ ++ ++директивы ssl_session_cache модулей ngx_http_ssl_module и ngx_imap_ssl_module. ++ ++ ++the "ssl_session_cache" directives ++of the ngx_http_ssl_module and ngx_imap_ssl_module. ++ ++ ++ ++ ++ ++метод DELETE поддерживает рекурсивное удаление. ++ ++ ++the DELETE method supports recursive removal. ++ ++ ++ ++ ++ ++при использовании $r->sendfile() byte-ranges передавались неверно. ++ ++ ++the byte-ranges were transferred incorrectly if the $r->sendfile() was used. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ключ -v больше не выводит информацию о компиляторе. ++ ++ ++the -v switch does not show compiler information any more. ++ ++ ++ ++ ++ ++ключ -V. ++ ++ ++the -V switch. ++ ++ ++ ++ ++ ++директива worker_rlimit_core поддерживает указание размера в K, M и G. ++ ++ ++the "worker_rlimit_core" directive supports size in K, M, and G. ++ ++ ++ ++ ++ ++модуль nginx.pm теперь может устанавливаться непривилегированным пользователем. ++ ++ ++the nginx.pm module now could be installed by an unprivileged user. ++ ++ ++ ++ ++ ++при использовании методов $r->request_body или $r->request_body_file мог ++произойти segmentation fault. ++ ++ ++a segmentation fault might occur if the $r->request_body or ++$r->request_body_file methods were used. ++ ++ ++ ++ ++ ++ошибок, специфичных для платформы ppc. ++ ++ ++the ppc platform specific bugs. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директиву perl можно использовать внутри блока limit_except. ++ ++ ++the "perl" directive may be used inside the "limit_except" block. ++ ++ ++ ++ ++ ++модуль ngx_http_dav_module требовал строку "Date" в заголовке запроса ++для метода DELETE. ++ ++ ++the ngx_http_dav_module required the "Date" request header line ++for the DELETE method. ++ ++ ++ ++ ++ ++при использовании одного параметра в директиве dav_access nginx мог ++сообщить об ошибке в конфигурации. ++ ++ ++if one only parameter was used in the "dav_access" directive, then ++nginx might report about configuration error. ++ ++ ++ ++ ++ ++при использовании переменной $host мог произойти segmentation fault; ++ошибка появилась в 0.4.14. ++ ++ ++a segmentation fault might occur if the $host variable was used; ++the bug had appeared in 0.4.14. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++модуль ngx_http_perl_module поддерживает методы $r->status, $r->log_error ++и $r->sleep. ++ ++ ++the ngx_http_perl_module supports the $r->status, $r->log_error, ++and $r->sleep methods. ++ ++ ++ ++ ++ ++метод $r->variable поддерживает переменные, неописанные в конфигурации nginx'а. ++ ++ ++the $r->variable method supports variables that do not exist in nginx ++configuration. ++ ++ ++ ++ ++ ++метод $r->has_request_body не работал. ++ ++ ++the $r->has_request_body method did not work. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++если в директивах proxy_pass использовалось имя, указанное в upstream, ++то nginx пытался найти IP-адрес этого имени; ++ошибка появилась в 0.5.1. ++ ++ ++if the "proxy_pass" directive used the name of the "upstream" block, ++then nginx tried to resolve the name; ++the bug had appeared in 0.5.1. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директива post_action могла не работать после неудачного завершения запроса. ++ ++ ++the "post_action" directive might not run after a unsuccessful completion ++of a request. ++ ++ ++ ++ ++ ++обход ошибки в Eudora для Mac; ++ошибка появилась в 0.4.11.
++Спасибо Bron Gondwana. ++
++ ++for Eudora for Mac; ++the bug had appeared in 0.4.11.
++Thanks to Bron Gondwana. ++
++
++ ++ ++ ++при указании в директиве fastcgi_pass имени описанного upstream'а выдавалось ++сообщение "no port in upstream"; ++ошибка появилась в 0.5.0. ++ ++ ++if the "upstream" name was used in the "fastcgi_pass", then the message ++"no port in upstream" was issued; ++the bug had appeared in 0.5.0. ++ ++ ++ ++ ++ ++если в директивах proxy_pass и fastcgi_pass использовались одинаковых имена ++серверов, но с разными портами, то эти директивы использовали первый ++описанный порт; ++ошибка появилась в 0.5.0. ++ ++ ++if the "proxy_pass" and "fastcgi_pass" directives used the same servers but ++different ports, then these directives uses the first described port; ++the bug had appeared in 0.5.0. ++ ++ ++ ++ ++ ++если в директивах proxy_pass и fastcgi_pass использовались unix domain сокеты, ++то эти директивы использовали первый описанный сокет; ++ошибка появилась в 0.5.0. ++ ++ ++if the "proxy_pass" and "fastcgi_pass" directives used the unix domain sockets, ++then these directives used first described socket; ++the bug had appeared in 0.5.0. ++ ++ ++ ++ ++ ++ngx_http_auth_basic_module игнорировал пользователя, если он был указан ++в последней строке файла паролей и после пароля не было перевода строки, ++возврата каретки или символа ":". ++ ++ ++ngx_http_auth_basic_module ignored the user if it was in the last line in ++the password file and there was no the carriage return, the line feed, ++or the ":" symbol after the password. ++ ++ ++ ++ ++ ++переменная $upstream_response_time могла быть равна "0.000", хотя время ++обработки было больше 1 миллисекунды. ++ ++ ++the $upstream_response_time variable might be equal to "0.000", although ++response time was more than 1 millisecond. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++параметры в виде "%name" в директиве log_format больше не поддерживаются. ++ ++ ++the parameters in the "%name" form in the "log_format" directive ++are not supported anymore. ++ ++ ++ ++ ++ ++директивы proxy_upstream_max_fails, proxy_upstream_fail_timeout, ++fastcgi_upstream_max_fails, и fastcgi_upstream_fail_timeout, ++memcached_upstream_max_fails и memcached_upstream_fail_timeout ++больше не поддерживаются. ++ ++ ++the "proxy_upstream_max_fails", "proxy_upstream_fail_timeout", ++"fastcgi_upstream_max_fails", "fastcgi_upstream_fail_timeout", ++"memcached_upstream_max_fails", and "memcached_upstream_fail_timeout" ++directives are not supported anymore. ++ ++ ++ ++ ++ ++директива server в блоке upstream поддерживает параметры ++max_fails, fail_timeout и down. ++ ++ ++the "server" directive in the "upstream" context supports ++the "max_fails", "fail_timeout", and "down" parameters. ++ ++ ++ ++ ++ ++директива ip_hash в блоке upstream. ++ ++ ++the "ip_hash" directive inside the "upstream" block. ++ ++ ++ ++ ++ ++статус WAIT в строке "Auth-Status" в заголовке ответа сервера аутентификации ++IMAP/POP3 прокси. ++ ++ ++the WAIT status in the "Auth-Status" header line of the IMAP/POP3 proxy ++authentication server response. ++ ++ ++ ++ ++ ++nginx не собирался на 64-битных платформах; ++ошибка появилась в 0.4.14. ++ ++ ++nginx could not be built on 64-bit platforms; ++the bug had appeared in 0.4.14. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директива proxy_pass_error_message в IMAP/POP3 прокси. ++ ++ ++the "proxy_pass_error_message" directive in IMAP/POP3 proxy. ++ ++ ++ ++ ++ ++теперь configure определяет библиотеку PCRE на FreeBSD, Linux и NetBSD. ++ ++ ++now configure detects system PCRE library on FreeBSD, Linux, and NetBSD. ++ ++ ++ ++ ++ ++ngx_http_perl_module не работал с перлом, собранным с поддержкой потоков; ++ошибка появилась в 0.3.38. ++ ++ ++ngx_http_perl_module did not work with perl built with the threads support; ++the bug had appeared in 0.3.38. ++ ++ ++ ++ ++ ++ngx_http_perl_module не работал корректно, если перл вызывался рекурсивно. ++ ++ ++ngx_http_perl_module did not work if perl was called recursively. ++ ++ ++ ++ ++ ++nginx игнорировал имя сервера в строке запроса. ++ ++ ++nginx ignored a host name in a request line. ++ ++ ++ ++ ++ ++если FastCGI сервер передавал много в stderr, ++то рабочий процесс мог зациклиться. ++ ++ ++a worker process may got caught in an endless loop, ++if a FastCGI server sent too many data to the stderr. ++ ++ ++ ++ ++ ++при изменении системного времени переменная $upstream_response_time ++могла быть отрицательной. ++ ++ ++the $upstream_response_time variable may be negative if the system time ++was changed backward. ++ ++ ++ ++ ++ ++при использовании POP3 серверу аутентификации IMAP/POP3 прокси ++не передавался параметр Auth-Login-Attempt. ++ ++ ++the "Auth-Login-Attempt" parameter was not sent to ++IMAP/POP3 proxy authentication server when POP3 was used. ++ ++ ++ ++ ++ ++при ошибке соединения с сервером аутентификации IMAP/POP3 прокси ++мог произойти segmentation fault. ++ ++ ++a segmentation fault might occur if connect to IMAP/POP3 proxy ++authentication server failed. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директиву proxy_pass можно использовать внутри блока limit_except. ++ ++ ++the "proxy_pass" directive may be used inside the "limit_except" block. ++ ++ ++ ++ ++ ++директива limit_except поддерживает все WebDAV методы. ++ ++ ++the "limit_except" directive supports all WebDAV methods. ++ ++ ++ ++ ++ ++при использовании директивы add_before_body без директивы add_after_body ++ответ передавался не полностью. ++ ++ ++if the "add_before_body" directive was used without ++the "add_after_body" directive, then a response did not transferred complete. ++ ++ ++ ++ ++ ++большое тело запроса не принималось, если использовались метод epoll ++и deferred accept(). ++ ++ ++a large request body did not receive if the epoll method ++and the deferred accept() were used. ++ ++ ++ ++ ++ ++для ответов модуля ngx_http_autoindex_module не выставлялась кодировка; ++ошибка появилась в 0.3.50. ++ ++ ++a charset could not be set for ngx_http_autoindex_module responses; ++the bug had appeared in 0.3.50. ++ ++ ++ ++ ++ ++ошибки "[alert] zero size buf" при работе с FastCGI-сервером; ++ ++ ++the "[alert] zero size buf" error when FastCGI server was used; ++ ++ ++ ++ ++ ++параметр конфигурации --group= игнорировался.
++Спасибо Thomas Moschny. ++
++ ++the --group= configuration parameter was ignored.
++Thanks to Thomas Moschny. ++
++
++ ++ ++ ++50-й подзапрос в SSI ответе не работал; ++ошибка появилась в 0.3.50. ++ ++ ++the 50th subrequest in SSI response did not work; ++the bug had appeared in 0.3.50. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++модуль ngx_http_perl_module поддерживает метод $r->variable. ++ ++ ++the ngx_http_perl_module supports the $r->variable method. ++ ++ ++ ++ ++ ++при включении в ответ большого статического файла с помощью SSI ++ответ мог передаваться не полностью. ++ ++ ++if a big static file was included using SSI in a response, ++then the response may be transferred incomplete. ++ ++ ++ ++ ++ ++nginx не убирал "#fragment" в URI. ++ ++ ++nginx did not omit the "#fragment" part in URI. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++POP3 прокси поддерживает AUTH LOGIN PLAIN и CRAM-MD5. ++ ++ ++the POP3 proxy supports the AUTH LOGIN PLAIN and CRAM-MD5. ++ ++ ++ ++ ++ ++модуль ngx_http_perl_module поддерживает метод $r->allow_ranges. ++ ++ ++the ngx_http_perl_module supports the $r->allow_ranges method. ++ ++ ++ ++ ++ ++при включённой поддержке команды APOP в POP3 прокси могли ++не работать команды USER/PASS; ++ошибка появилась в 0.4.10. ++ ++ ++if the APOP was enabled in the POP3 proxy, then the USER/PASS commands ++might not work; ++the bug had appeared in 0.4.10. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++POP3 прокси поддерживает APOP. ++ ++ ++the POP3 proxy supports the APOP command. ++ ++ ++ ++ ++ ++при использовании методов select, poll и /dev/poll во время ожидания ++ответа от сервера аутентификации IMAP/POP3 прокси нагружал процессор. ++ ++ ++if the select, poll or /dev/poll methods were used, then while ++waiting authentication server response the IMAP/POP3 proxy hogged CPU. ++ ++ ++ ++ ++ ++при использовании переменной $server_addr в директиве map мог ++произойти segmentation fault. ++ ++ ++a segmentation fault might occur if the $server_addr variable was used ++in the "map" directive. ++ ++ ++ ++ ++ ++модуль ngx_http_flv_module не поддерживал byte ranges для полных ответов; ++ошибка появилась в 0.4.7. ++ ++ ++the ngx_http_flv_module did not support the byte ranges for full responses; ++the bug had appeared in 0.4.7. ++ ++ ++ ++ ++ ++nginx не собирался на Debian amd64; ++ошибка появилась в 0.4.9. ++ ++ ++nginx could not be built on Debian amd64; ++the bug had appeared in 0.4.9. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++параметр set в команде SSI include. ++ ++ ++the "set" parameter in the "include" SSI command. ++ ++ ++ ++ ++ ++модуль ngx_http_perl_module теперь проверяет версию модуля nginx.pm. ++ ++ ++the ngx_http_perl_module now tests the nginx.pm module version. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++если до команды SSI include с параметром wait выполнялась ещё ++одна команда SSI include, то параметр wait мог не работать. ++ ++ ++if an "include" SSI command were before another "include" SSI command ++with a "wait" parameter, then the "wait" parameter might not work. ++ ++ ++ ++ ++ ++модуль ngx_http_flv_module добавлял FLV-заголовок для полных ответов.
++Спасибо Алексею Ковырину. ++
++ ++the ngx_http_flv_module added the FLV header to the full responses.
++Thanks to Alexey Kovyrin. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++модуль ngx_http_flv_module. ++ ++ ++the ngx_http_flv_module. ++ ++ ++ ++ ++ ++переменная $request_body_file. ++ ++ ++the $request_body_file variable. ++ ++ ++ ++ ++ ++директивы charset и source_charset поддерживают переменные. ++ ++ ++the "charset" and "source_charset" directives support the variables. ++ ++ ++ ++ ++ ++если до команды SSI include с параметром wait выполнялась ещё ++одна команда SSI include, то параметр wait мог не работать. ++ ++ ++if an "include" SSI command were before another "include" SSI command ++with a "wait" parameter, then the "wait" parameter might not work. ++ ++ ++ ++ ++ ++при использовании директивы "proxy_buffering off" или при работе ++с memcached соединения могли не закрываться по таймауту. ++ ++ ++if the "proxy_buffering off" directive was used or while working with ++memcached the connections might not be closed on timeout. ++ ++ ++ ++ ++ ++nginx не запускался на 64-битных платформах, отличных от amd64, sparc64 и ppc64. ++ ++ ++nginx did not run on 64-bit platforms except amd64, sparc64, and ppc64. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++nginx не запускался на 64-битных платформах, отличных от amd64, sparc64 и ppc64. ++ ++ ++nginx did not run on 64-bit platforms except amd64, sparc64, and ppc64. ++ ++ ++ ++ ++ ++при запросе версии HTTP/1.1 nginx передавал ответ chunk'ами, ++если длина ответа в методе $r->headers_out("Content-Length", ...) ++была задана текстовой строкой. ++ ++ ++nginx sent the chunked response for HTTP/1.1 request,
++if its length was set by text string in ++the $r->headers_out("Content-Length", ...) method. ++
++
++ ++ ++ ++после перенаправления ошибки с помощью директивы error_page любая директива ++модуля ngx_http_rewrite_module возвращала эту ошибку; ++ошибка появилась в 0.4.4. ++ ++ ++after redirecting error by an "error_page" directive ++any ngx_http_rewrite_module directive returned this error code; ++the bug had appeared in 0.4.4. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++nginx не собирался на Linux и Solaris; ++ошибка появилась в 0.4.4. ++ ++ ++nginx could not be built on Linux and Solaris; ++the bug had appeared in 0.4.4. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++переменная $scheme. ++ ++ ++the $scheme variable. ++ ++ ++ ++ ++ ++директива expires поддерживает параметр max. ++ ++ ++the "expires" directive supports the "max" parameter. ++ ++ ++ ++ ++ ++директива include поддерживает маску "*".
++Спасибо Jonathan Dance. ++
++ ++the "include" directive supports the "*" mask.
++Thanks to Jonathan Dance. ++
++
++ ++ ++ ++директива return всегда изменяла код ответа, перенаправленного ++директивой error_page. ++ ++ ++the "return" directive always overrode the "error_page" response code ++redirected by the "error_page" directive. ++ ++ ++ ++ ++ ++происходил segmentation fault, если в методе PUT передавалось ++тело нулевой длины. ++ ++ ++a segmentation fault occurred if zero-length body was in PUT method. ++ ++ ++ ++ ++ ++при использовании переменных в директиве proxy_redirect редирект ++изменялся неверно. ++ ++ ++the redirect was changed incorrectly if the variables were used ++in the "proxy_redirect" directive. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++ошибку 499 теперь нельзя перенаправить с помощью директивы error_page. ++ ++ ++now the 499 error could not be redirected using an "error_page" directive. ++ ++ ++ ++ ++ ++поддержка Solaris 10 event ports. ++ ++ ++the Solaris 10 event ports support. ++ ++ ++ ++ ++ ++модуль ngx_http_browser_module. ++ ++ ++the ngx_http_browser_module. ++ ++ ++ ++ ++ ++при перенаправлении ошибки 400 проксированному серверу ++помощью директивы error_page мог произойти segmentation fault. ++ ++ ++a segmentation fault may occur while redirecting the 400 error ++to the proxied server using a "proxy_pass" directive. ++ ++ ++ ++ ++ ++происходил segmentation fault, если в директиве proxy_pass использовался ++unix domain сокет; ++ошибка появилась в 0.3.47. ++ ++ ++a segmentation fault occurred if an unix domain socket was used in ++a "proxy_pass" directive; ++the bug had appeared in 0.3.47. ++ ++ ++ ++ ++ ++SSI не работал с ответами memcached и небуферизированными проксированными ++ответами. ++ ++ ++SSI did work with memcached and nonbuffered responses. ++ ++ ++ ++ ++ ++обход ошибки PAUSE hardware capability в Sun Studio. ++ ++ ++of the Sun Studio PAUSE hardware capability bug. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++убрана поддержка флага O_NOATIME на Linux; ++ошибка появилась в 0.4.1. ++ ++ ++the O_NOATIME flag support on Linux was canceled; ++the bug had appeared in 0.4.1. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++совместимость с DragonFlyBSD.
++Спасибо Павлу Назарову. ++
++ ++the DragonFlyBSD compatibility.
++Thanks to Pavel Nazarov. ++
++
++ ++ ++ ++обход ошибки в sendfile() в 64-битном Linux при передаче файлов больше 2G. ++ ++ ++of bug in 64-bit Linux sendfile(), when file is more than 2G. ++ ++ ++ ++ ++ ++теперь на Linux nginx для статических запросов использует флаг O_NOATIME.
++Спасибо Yusuf Goolamabbas. ++
++ ++now on Linux nginx uses O_NOATIME flag for static requests.
++Thanks to Yusuf Goolamabbas. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++Изменение во внутреннем API: инициализация модулей HTTP перенесена из фазы ++init module в фазу HTTP postconfiguration. ++ ++ ++Change in internal API: the HTTP modules initialization was moved ++from the init module phase to the HTTP postconfiguration phase. ++ ++ ++ ++ ++ ++теперь тело запроса в модуле ngx_http_perl_module не считывается ++заранее: нужно явно инициировать чтение с помощью метода $r->has_request_body. ++ ++ ++now the request body is not read beforehand for the ngx_http_perl_module: ++it's required to start the reading using the $r->has_request_body method. ++ ++ ++ ++ ++ ++модуль ngx_http_perl_module поддерживает код возврата DECLINED. ++ ++ ++the ngx_http_perl_module supports the DECLINED return code. ++ ++ ++ ++ ++ ++модуль ngx_http_dav_module поддерживает входящую строку заголовка "Date" ++для метода PUT. ++ ++ ++the ngx_http_dav_module supports the incoming "Date" header line ++for the PUT method. ++ ++ ++ ++ ++ ++директива ssi работает внутри блока if. ++ ++ ++the "ssi" directive is available inside the "if" block. ++ ++ ++ ++ ++ ++происходил segmentation fault, если в директиве index использовалась ++переменные и при этом первое имя индексного файла было без переменных; ++ошибка появилась в 0.1.29. ++ ++ ++a segmentation fault occurred if there was an "index" directive with ++variables and the first index name was without variables; ++the bug had appeared in 0.1.29. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директива tcp_nodelay теперь по умолчанию включена. ++ ++ ++now the "tcp_nodelay" directive is turned on by default. ++ ++ ++ ++ ++ ++директива msie_refresh. ++ ++ ++the "msie_refresh" directive. ++ ++ ++ ++ ++ ++директива recursive_error_pages. ++ ++ ++the "recursive_error_pages" directive. ++ ++ ++ ++ ++ ++директива rewrite возвращала неправильный редирект, если редирект ++включал в себя выделенные закодированные символы из оригинального URI. ++ ++ ++the "rewrite" directive returned incorrect redirect, if the redirect ++had the captured escaped symbols from original URI. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++во время перенаправления ошибки рабочий процесс мог зациклиться; ++ошибка появилась в 0.3.59. ++ ++ ++a worker process may got caught in an endless loop ++while an error redirection; ++the bug had appeared in 0.3.59. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++теперь можно делать несколько перенаправлений через директиву error_page. ++ ++ ++now is possible to do several redirection using the "error_page" directive. ++ ++ ++ ++ ++ ++директива dav_access не поддерживала три параметра. ++ ++ ++the "dav_access" directive did not support three parameters. ++ ++ ++ ++ ++ ++директива error_page не изменяла строку "Content-Type" ++после перенаправления с помощью "X-Accel-Redirect"; ++ошибка появилась в 0.3.58. ++ ++ ++the "error_page" directive did not changes the "Content-Type" header line ++after the "X-Accel-Redirect" was used; ++the bug had appeared in 0.3.58. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директива error_page поддерживает переменные. ++ ++ ++the "error_page" directive supports the variables. ++ ++ ++ ++ ++ ++теперь на Linux используется интерфейс procfs вместо sysctl. ++ ++ ++now the procfs interface instead of sysctl is used on Linux. ++ ++ ++ ++ ++ ++теперь при использовании "X-Accel-Redirect" строка "Content-Type" наследуется ++из первоначального ответа. ++ ++ ++now the "Content-Type" header line is inherited from first response ++when the "X-Accel-Redirect" was used. ++ ++ ++ ++ ++ ++директива error_page не перенаправляла ошибку 413. ++ ++ ++the "error_page" directive did not redirect the 413 error. ++ ++ ++ ++ ++ ++завершающий "?" не удалял старые аргументы, если в переписанном URI ++не было новых аргументов. ++ ++ ++the trailing "?" did not remove old arguments if no new arguments ++were added to a rewritten URI. ++ ++ ++ ++ ++ ++nginx не запускался на 64-битной FreeBSD 7.0-CURRENT. ++ ++ ++nginx could not run on 64-bit FreeBSD 7.0-CURRENT. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++переменная $ssl_client_serial. ++ ++ ++the $ssl_client_serial variable. ++ ++ ++ ++ ++ ++в операторе "!-e" в директиве if.
++Спасибо Андриану Буданцову. ++
++ ++in the "!-e" operator of the "if" directive.
++Thanks to Andrian Budanstov. ++
++
++ ++ ++ ++при проверке клиентского сертификата nginx не передавал клиенту ++информацию о требуемых сертификатах. ++ ++ ++while a client certificate verification nginx did not send to a client ++the required certificates information. ++ ++ ++ ++ ++ ++переменная $document_root не поддерживала переменные в директиве root. ++ ++ ++the $document_root variable did not support the variables in the "root" ++directive. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++директива dav_access. ++ ++ ++the "dav_access" directive. ++ ++ ++ ++ ++ ++директива if поддерживает операторы "-d", "!-d", "-e", "!-e", "-x" и "!-x". ++ ++ ++the "if" directive supports the "-d", "!-d", "-e", "!-e", "-x", and "!-x" ++operators. ++ ++ ++ ++ ++ ++при записи в access_log некоторых передаваемых клиенту строк заголовков ++происходил segmentation fault, если запрос возвращал редирект. ++ ++ ++a segmentation fault occurred if a request returned a redirect and ++some sent to client header lines were logged in the access log. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++параметр stub в команде SSI include. ++ ++ ++the "stub" parameter in the "include" SSI command. ++ ++ ++ ++ ++ ++команда SSI block. ++ ++ ++the "block" SSI command. ++ ++ ++ ++ ++ ++скрипт unicode2nginx добавлен в contrib. ++ ++ ++the unicode2nginx script was added to contrib. ++ ++ ++ ++ ++ ++если root был задан только переменной, то корень задавался ++относительно префикса сервера. ++ ++ ++if a "root" was specified by variable only, then the root was relative ++to a server prefix. ++ ++ ++ ++ ++ ++если в запросе был "//" или "/.", и после этого закодированные ++символы в виде "%XX", то проксируемый запрос передавался незакодированным. ++ ++ ++if the request contained "//" or "/./" and escaped symbols after them, ++then the proxied request was sent unescaped. ++ ++ ++ ++ ++ ++метод $r->header_in("Cookie") модуля ngx_http_perl_module теперь возвращает ++все строки "Cookie" в заголовке запроса. ++ ++ ++the $r->header_in("Cookie") of the ngx_http_perl_module now returns ++all "Cookie" header lines. ++ ++ ++ ++ ++ ++происходил segmentation fault, если использовался ++"client_body_in_file_only on" ++и делался переход к следующему бэкенду. ++ ++ ++a segmentation fault occurred if "client_body_in_file_only on" ++was used and nginx switched to a next upstream. ++ ++ ++ ++ ++ ++при некоторых условиях во время переконфигурации коды символов ++внутри директивы charset_map могли считаться неверными; ++ошибка появилась в 0.3.50. ++ ++ ++on some condition while reconfiguration character codes ++inside the "charset_map" may be treated invalid; ++the bug had appeared in 0.3.50. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++nginx теперь записывает в лог информацию о подзапросах. ++ ++ ++nginx now logs the subrequest information to the error log. ++ ++ ++ ++ ++ ++директивы proxy_next_upstream, fastcgi_next_upstream и memcached_next_upstream ++поддерживают параметр off. ++ ++ ++the "proxy_next_upstream", "fastcgi_next_upstream", ++and "memcached_next_upstream" directives support the "off" parameter. ++ ++ ++ ++ ++ ++директива debug_connection поддерживает запись адресов в формате CIDR. ++ ++ ++the "debug_connection" directive supports the CIDR address form. ++ ++ ++ ++ ++ ++при перекодировании ответа проксированного сервера или сервера FastCGI ++в UTF-8 или наоборот ответ мог передаваться не полностью. ++ ++ ++if a response of proxied server or FastCGI server was converted from UTF-8 ++or back, then it may be transferred incomplete. ++ ++ ++ ++ ++ ++переменная $upstream_response_time содержала время только первого ++обращения к бэкенду. ++ ++ ++the $upstream_response_time variable had the time of the first ++request to a backend only. ++ ++ ++ ++ ++ ++nginx не собирался на платформе amd64; ++ошибка появилась в 0.3.53. ++ ++ ++nginx could not be built on amd64 platform; ++the bug had appeared in 0.3.53. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директива add_header добавляет строки в ответы с кодом 204, 301 и 302. ++ ++ ++the "add_header" directive adds the string to 204, 301, and 302 responses. ++ ++ ++ ++ ++ ++директива server в блоке upstream поддерживает параметр weight. ++ ++ ++the "server" directive in the "upstream" context supports ++the "weight" parameter. ++ ++ ++ ++ ++ ++директива server_name поддерживает маску "*". ++ ++ ++the "server_name" directive supports the "*" wildcard. ++ ++ ++ ++ ++ ++nginx поддерживает тело запроса больше 2G. ++ ++ ++nginx supports the request body size more than 2G. ++ ++ ++ ++ ++ ++если при использовании "satisfy_any on" клиент успешно проходил аутентификацию, ++в лог всё равно записалоcь сообщение "access forbidden by rule". ++ ++ ++if a client was successfully authorized using "satisfy_any on", then anyway ++the message "access forbidden by rule" was written in the log. ++ ++ ++ ++ ++ ++метод PUT мог ошибочно не создать файл и вернуть код 409. ++ ++ ++the "PUT" method may erroneously not create a file and return the 409 code. ++ ++ ++ ++ ++ ++если во время аутентификации IMAP/POP3 бэкенд возвращал ошибку, nginx ++продолжал проксирование. ++ ++ ++if the IMAP/POP3 backend returned an error, then nginx continued proxying ++anyway. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++восстановлено поведение модуля ngx_http_index_module для запросов "POST /": ++как в версии до 0.3.40, модуль теперь не выдаёт ошибку 405. ++ ++ ++the ngx_http_index_module behavior for the "POST /" requests is reverted ++to the 0.3.40 version state: the module now does not return the 405 error. ++ ++ ++ ++ ++ ++при использовании ограничения скорости рабочий процесс мог зациклиться; ++ошибка появилась в 0.3.37. ++ ++ ++the worker process may got caught in an endless loop if the limit rate was used; ++the bug had appeared in 0.3.37. ++ ++ ++ ++ ++ ++модуль ngx_http_charset_module записывал в лог ошибку "unknown charset", ++даже если перекодировка не требовалась; ++ошибка появилась в 0.3.50. ++ ++ ++ngx_http_charset_module logged "unknown charset" alert, even if the recoding ++was not needed; ++the bug had appeared in 0.3.50. ++ ++ ++ ++ ++ ++если в результате запроса PUT возвращался код 409, то временный файл ++не удалялся. ++ ++ ++if a code response of the PUT request was 409, then a temporary file ++was not removed. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++при некоторых условиях в SSI мог пропадать символы "<"; ++ошибка появилась в 0.3.50. ++ ++ ++the "<" symbols might disappeared some conditions in the SSI; ++the bug had appeared in 0.3.50. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директивы proxy_redirect_errors и fastcgi_redirect_errors ++переименованы соответственно в proxy_intercept_errors и ++fastcgi_intercept_errors. ++ ++ ++the "proxy_redirect_errors" and "fastcgi_redirect_errors" directives ++was renamed to the "proxy_intercept_errors" and ++"fastcgi_intercept_errors" directives. ++ ++ ++ ++ ++ ++модуль ngx_http_charset_module поддерживает перекодирование из ++однобайтных кодировок в UTF-8 и обратно. ++ ++ ++the ngx_http_charset_module supports the recoding from the single byte ++encodings to the UTF-8 encoding and back. ++ ++ ++ ++ ++ ++в режиме прокси и FastCGI поддерживается строка заголовка "X-Accel-Charset" ++в ответе бэкенда. ++ ++ ++the "X-Accel-Charset" response header line is supported in proxy ++and FastCGI mode. ++ ++ ++ ++ ++ ++символ "\" в парах "\"" и "\'" в SSI командах убирался, только если ++также использовался символ "$". ++ ++ ++the "\" escape symbol in the "\"" and "\'" pairs in the SSI command ++was removed only if the command also has the "$" symbol. ++ ++ ++ ++ ++ ++при некоторых условиях в SSI после вставки могла быть добавлена ++строка "<!--". ++ ++ ++the "<!--" string might be added on some conditions ++in the SSI after inclusion. ++ ++ ++ ++ ++ ++если в заголовке ответа была строка "Content-Length: 0", ++то при использовании небуферизированного проксировании не закрывалось соединение ++с клиентом. ++ ++ ++if the "Content-Length: 0" header line was in response, then ++in nonbuffered proxying mode the client connection was not closed. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++в директиве set. ++ ++ ++in the "set" directive. ++ ++ ++ ++ ++ ++при включении в ssi двух и более подзапросов, обрабатываемых через FastCGI, ++вместо вывода второго и остальных подзапросов в ответ включался вывод ++первого подзапроса. ++ ++ ++if two or more FastCGI subrequests was in SSI, then first subrequest output ++was included instead of second and following subrequests. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++теперь модуль ngx_http_charset_module работает для подзапросов, ++в ответах которых нет строки заголовка "Content-Type". ++ ++ ++now the ngx_http_charset_module works for subrequests, ++if the response has no "Content-Type" header line. ++ ++ ++ ++ ++ ++если в директиве proxy_pass не было URI, ++то директива "proxy_redirect default" добавляла в переписанный ++редирект в начало лишний слэш. ++ ++ ++if the "proxy_pass" directive has no URI part, ++then the "proxy_redirect default" directive add the unnecessary slash ++in start of the rewritten redirect. ++ ++ ++ ++ ++ ++внутренний редирект всегда превращал любой HTTP-метод в GET, ++теперь это делается только для редиректов, выполняемых с помощью ++X-Accel-Redirect, и у которых метод не равен HEAD; ++ошибка появилась в 0.3.42. ++ ++ ++the internal redirect always transform client's HTTP method to GET, ++now the transformation is made for the "X-Accel-Redirect" redirects only ++and if the method is not HEAD; ++the bug had appeared in 0.3.42. ++ ++ ++ ++ ++ ++модуль ngx_http_perl_module не собирался, если перл был с поддержкой потоков; ++ошибка появилась в 0.3.46. ++ ++ ++the ngx_http_perl_module could not be built, if the perl was built ++with the threads support; ++the bug had appeared in 0.3.46. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директива upstream. ++ ++ ++the "upstream" directive. ++ ++ ++ ++ ++ ++символ "\" в парах "\"" и "\'" в SSI командах теперь всегда убирается. ++ ++ ++now the "\" escape symbol in the "\"" and "\'" pairs in the SSI command ++is always removed. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директивы proxy_hide_header, proxy_pass_header, fastcgi_hide_header ++и fastcgi_pass_header. ++ ++ ++the "proxy_hide_header", "proxy_pass_header", "fastcgi_hide_header", ++and "fastcgi_pass_header" directives. ++ ++ ++ ++ ++ ++директивы proxy_pass_x_powered_by, fastcgi_x_powered_by и proxy_pass_server ++упразднены. ++ ++ ++the "proxy_pass_x_powered_by", "fastcgi_x_powered_by", and "proxy_pass_server" ++directives were canceled. ++ ++ ++ ++ ++ ++в режиме прокси поддерживается строка заголовка "X-Accel-Buffering" ++в ответе бэкенда. ++ ++ ++the "X-Accel-Buffering" response header line is supported in proxy mode. ++ ++ ++ ++ ++ ++ошибок и утечек памяти при переконфигурации в модуле ngx_http_perl_module. ++ ++ ++the reconfiguration bug and memory leaks in the ngx_http_perl_module. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директивы ssl_verify_client, ssl_verify_depth и ssl_client_certificate. ++ ++ ++the "ssl_verify_client", "ssl_verify_depth", and "ssl_client_certificate" ++directives. ++ ++ ++ ++ ++ ++теперь переменная $request_method возвращает метод только основного запроса. ++ ++ ++the $request_method variable now returns the main request method. ++ ++ ++ ++ ++ ++в таблице перекодировки koi-win изменены коды символа &deg;. ++ ++ ++the &deg; symbol codes were changed in koi-win conversion table. ++ ++ ++ ++ ++ ++в таблицу перекодировки koi-win добавлены символы евро и номера. ++ ++ ++the euro and N symbols were added to koi-win conversion table. ++ ++ ++ ++ ++ ++если nginx распределял запросы на несколько машин, то при падении ++одной из них запросы, предназначенные для этой машины, перенаправлялись только ++на одну машину вместо того, чтобы равномерно распределяться между остальными. ++ ++ ++if nginx distributed the requests among several backends and some backend ++failed, then requests intended for this backend was directed to one live ++backend only instead of being distributed among the rest. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++параметр wait в команде SSI include. ++ ++ ++the "wait" parameter in the "include" SSI command. ++ ++ ++ ++ ++ ++в таблицу перекодировки koi-win добавлены украинские и белорусские символы. ++ ++ ++the Ukrainian and Byelorussian characters were added to koi-win conversion ++table. ++ ++ ++ ++ ++ ++в SSI. ++ ++ ++in the SSI. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++в SSI. ++ ++ ++in the SSI. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++параметр bind в директиве listen в IMAP/POP3 прокси. ++ ++ ++the "bind" option of the "listen" directive in IMAP/POP3 proxy. ++ ++ ++ ++ ++ ++ошибки при использовании в директиве rewrite одного и того же ++выделения более одного раза. ++ ++ ++if the same capture in the "rewrite" directive was used more then once. ++ ++ ++ ++ ++ ++в лог не записывались переменные ++$sent_http_content_type, $sent_http_content_length, $sent_http_last_modified, ++$sent_http_connection, $sent_http_keep_alive и $sent_http_transfer_encoding. ++ ++ ++the $sent_http_content_type, $sent_http_content_length, ++$sent_http_last_modified, $sent_http_connection, $sent_http_keep_alive, ++and $sent_http_transfer_encoding variables were not written to access log. ++ ++ ++ ++ ++ ++переменная $sent_http_cache_control возвращала содержимое только одной ++строки "Cache-Control" в заголовке ответа. ++ ++ ++the $sent_http_cache_control returned value of the single "Cache-Control" ++response header line. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ключ -v. ++ ++ ++the -v switch. ++ ++ ++ ++ ++ ++при включении в SSI удалённых подзапросов ++мог произойти segmentation fault. ++ ++ ++the segmentation fault may occurred if the SSI page has remote subrequests. ++ ++ ++ ++ ++ ++в обработке FastCGI. ++ ++ ++in FastCGI handling. ++ ++ ++ ++ ++ ++если путь к перловым модулям не был указан с помощью ++--with-perl_modules_path=PATH или директивы perl_modules, ++то на старте происходил segmentation fault. ++ ++ ++if the perl modules path was not set using ++--with-perl_modules_path=PATH or the "perl_modules", then ++the segmentation fault was occurred. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++модуль ngx_http_dav_module поддерживает метод MKCOL. ++ ++ ++the ngx_http_dav_module supports the MKCOL method. ++ ++ ++ ++ ++ ++директива create_full_put_path. ++ ++ ++the "create_full_put_path" directive. ++ ++ ++ ++ ++ ++переменная $limit_rate. ++ ++ ++the "$limit_rate" variable. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директива uninitialized_variable_warn; уровень логгирования сообщения ++о неинициализированной переменной понижен с уровня alert на warn. ++ ++ ++the "uninitialized_variable_warn" directive; the logging level of the ++"uninitialized variable" message was lowered from "alert" to "warn". ++ ++ ++ ++ ++ ++директива override_charset. ++ ++ ++the "override_charset" directive. ++ ++ ++ ++ ++ ++при использовании неизвестной переменной в SSI-командах echo и if expr='$name' ++теперь не записывается в лог сообщение о неизвестной переменной. ++ ++ ++now if the unknown variable is used in the "echo" and "if expr='$name'" ++SSI-commands, then the "unknown variable" message is not logged. ++ ++ ++ ++ ++ ++счётчик активных соединений рос при превышении лимита соединений, ++заданного директивой worker_connections; ++ошибка появилась в 0.2.0. ++ ++ ++the active connection counter increased on the exceeding of the connection ++limit specified by the "worker_connections" directive; ++the bug had appeared in 0.2.0. ++ ++ ++ ++ ++ ++при некоторых условия ограничение скорости соединения могло не работать; ++ошибка появилась в 0.3.38. ++ ++ ++the limit rate might not work on some condition; ++the bug had appeared in 0.3.38. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++модуль ngx_http_dav_module. ++ ++ ++the ngx_http_dav_module. ++ ++ ++ ++ ++ ++оптимизация модуля ngx_http_perl_module.
++Спасибо Сергею Скворцову. ++
++ ++the ngx_http_perl_module optimizations.
++Thanks to Sergey Skvortsov. ++
++
++ ++ ++ ++модуль ngx_http_perl_module поддерживает метод $r->request_body_file. ++ ++ ++the ngx_http_perl_module supports the $r->request_body_file method. ++ ++ ++ ++ ++ ++директива client_body_in_file_only. ++ ++ ++the "client_body_in_file_only" directive. ++ ++ ++ ++ ++ ++теперь при переполнении диска nginx пытается писать access_log'и только ++раз в секунду.
++Спасибо Антону Южанинову и Максиму Дунину. ++
++ ++now on disk overflow nginx tries to write access logs once a second only.
++Thanks to Anton Yuzhaninov and Maxim Dounin. ++
++
++ ++ ++ ++теперь директива limit_rate точнее ограничивает скорость при значениях ++больше 100 Kbyte/s.
++Спасибо ForJest. ++
++ ++now the "limit_rate" directive more precisely limits rate if rate is more ++than 100 Kbyte/s.
++Thanks to ForJest. ++
++
++ ++ ++ ++IMAP/POP3 прокси теперь передаёт серверу авторизации символы "\r" и "\n" ++в логине и пароле в закодированном виде.
++Спасибо Максиму Дунину. ++
++ ++now the IMAP/POP3 proxy escapes the "\r" and "\n" symbols in login and ++password to pass authorization server.
++Thanks to Maxim Dounin. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++директива limit_except. ++ ++ ++the "limit_except" directive. ++ ++ ++ ++ ++ ++директива if поддерживает операторы "!~", "!~*", "-f" и "!-f". ++ ++ ++the "if" directive supports the "!~", "!~*", "-f", and "!-f" operators. ++ ++ ++ ++ ++ ++модуль ngx_http_perl_module поддерживает метод $r->request_body. ++ ++ ++the ngx_http_perl_module supports the $r->request_body method. ++ ++ ++ ++ ++ ++в модуле ngx_http_addition_filter_module. ++ ++ ++in the ngx_http_addition_filter_module. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++модуль ngx_http_addition_filter_module. ++ ++ ++the ngx_http_addition_filter_module. ++ ++ ++ ++ ++ ++директивы proxy_pass и fastcgi_pass можно использовать внутри блока if. ++ ++ ++the "proxy_pass" and "fastcgi_pass" directives may be used inside ++the "if" block. ++ ++ ++ ++ ++ ++директивы proxy_ignore_client_abort и fastcgi_ignore_client_abort. ++ ++ ++the "proxy_ignore_client_abort" and "fastcgi_ignore_client_abort" directives. ++ ++ ++ ++ ++ ++переменная $request_completion. ++ ++ ++the "$request_completion" variable. ++ ++ ++ ++ ++ ++модуль ngx_http_perl_module поддерживает методы $r->request_method и ++$r->remote_addr. ++ ++ ++the ngx_http_perl_module supports the $r->request_method and $r->remote_addr. ++ ++ ++ ++ ++ ++модуль ngx_http_ssi_module поддерживает команду elif. ++ ++ ++the ngx_http_ssi_module supports the "elif" command. ++ ++ ++ ++ ++ ++строка "\/" в начале выражения команды if модуля ngx_http_ssi_module ++воспринималась неверно. ++ ++ ++the "\/" string in the expression of the "if" command of the ++ngx_http_ssi_module was treated incorrectly. ++ ++ ++ ++ ++ ++в использовании регулярных выражениях в команде if модуля ngx_http_ssi_module. ++ ++ ++in the regular expressions in the "if" command of the ngx_http_ssi_module. ++ ++ ++ ++ ++ ++при задании относительного пути в директивах ++client_body_temp_path, proxy_temp_path, fastcgi_temp_path и perl_modules ++использовался каталог относительно текущего каталога, а не относительно ++префикса сервера. ++ ++ ++if the relative path was specified in the "client_body_temp_path", ++"proxy_temp_path", "fastcgi_temp_path", and "perl_modules" directives, ++then the directory was used relatively to a current path but not ++to a server prefix. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++accept-фильтр и TCP_DEFER_ACCEPT устанавливались только для первой ++директивы listen; ++ошибка появилась в 0.3.31. ++ ++ ++the accept-filter and the TCP_DEFER_ACCEPT option were set for first "listen" ++directive only; ++the bug had appeared in 0.3.31. ++ ++ ++ ++ ++ ++в директиве proxy_pass без URI при использовании в подзапросе. ++ ++ ++in the "proxy_pass" directive without the URI part in a subrequest. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директива add_header поддерживает переменные. ++ ++ ++the "add_header" directive supports the variables. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++параметр http_503 в директивах proxy_next_upstream или fastcgi_next_upstream. ++ ++ ++the "http_503" parameter of the "proxy_next_upstream" or ++"fastcgi_next_upstream" directives. ++ ++ ++ ++ ++ ++ngx_http_perl_module не работал со встроенным в конфигурационный файл кодом, ++если он не начинался сразу же с "sub". ++ ++ ++ngx_http_perl_module did not work with inlined in the configuration code, ++if it was not started with the "sub" word. ++ ++ ++ ++ ++ ++в директиве post_action. ++ ++ ++in the "post_action" directive. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++удаление отладочного логгирования на старте и при переконфигурации; ++ошибка появилась в 0.3.31. ++ ++ ++the debug logging on startup and reconfiguration time was removed; ++the bug had appeared in 0.3.31. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++теперь nginx передаёт неверные ответы проксированного бэкенда. ++ ++ ++now nginx passes the malformed proxied backend responses. ++ ++ ++ ++ ++ ++директивы listen поддерживают адрес в виде "*:порт". ++ ++ ++the "listen" directives support the address in the "*:port" form. ++ ++ ++ ++ ++ ++поддержка EVFILER_TIMER в MacOSX 10.4. ++ ++ ++the EVFILER_TIMER support in MacOSX 10.4. ++ ++ ++ ++ ++ ++обход ошибки обработки миллисекундных таймаутов kqueue в 64-битном ядре ++MacOSX.
++Спасибо Андрею Нигматулину. ++
++ ++for MacOSX 64-bit kernel kqueue millisecond timeout bug.
++Thanks to Andrei Nigmatulin. ++
++
++ ++ ++ ++если внутри одного сервера описаны несколько директив listen, слушающих на ++разных адресах, то имена серверов вида "*.domain.tld" работали только ++для первого адреса; ++ошибка появилась в 0.3.18. ++ ++ ++if there were several "listen" directives listening one various addresses ++inside one server, then server names like "*.domain.tld" worked for first ++address only; ++the bug had appeared in 0.3.18. ++ ++ ++ ++ ++ ++при использовании протокола HTTPS в директиве proxy_pass не передавались ++запросы с телом, записанным во временный файл. ++ ++ ++if the HTTPS protocol was used in the "proxy_pass" directive and ++the request body was in temporary file then the request was not transferred. ++ ++ ++ ++ ++ ++совместимость с perl 5.8.8. ++ ++ ++perl 5.8.8 compatibility. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++уровень записи в лог ошибки ECONNABORTED изменён на error с уровня crit. ++ ++ ++the ECONNABORTED error log level was changed to "error" from "crit". ++ ++ ++ ++ ++ ++модуль ngx_http_perl_module не собирался без модуля ngx_http_ssi_filter_module. ++ ++ ++the ngx_http_perl_module could not be build without ++the ngx_http_ssi_filter_module. ++ ++ ++ ++ ++ ++nginx не собирался на i386 платформе, если использовался PIC; ++ошибка появилась в 0.3.27. ++ ++ ++nginx could not be built on i386 platform, if the PIC was used; ++the bug had appeared in 0.3.27. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++теперь nginx использует меньше памяти, если PHP в режиме FastCGI передаёт ++большое количество предупреждений перед ответом. ++ ++ ++now nginx uses less memory, if PHP in FastCGI mode sends many warnings ++before the response. ++ ++ ++ ++ ++ ++в ответах 204 для запросов версии HTTP/1.1 выдавалась строка заголовка ++"Transfer-Encoding: chunked". ++ ++ ++the "Transfer-Encoding: chunked" header line was issued in the 204 responses ++for the HTTP/1.1 requests. ++ ++ ++ ++ ++ ++nginx возвращал 502 код ответа, если FastCGI сервер передавал полные строки ++заголовка ответа в отдельных FastCGI записях. ++ ++ ++nginx returned the 502 response, if the complete response header lines ++were transferred in a separate FastCGI records. ++ ++ ++ ++ ++ ++если в директиве post_action был указан проксируемый URI, то он выполнялся ++только после успешного завершения запроса. ++ ++ ++if the proxied URI was specified in the "post_action" directive, then it ran ++only after a successful completion of a request. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директива restrict_host_names упразднена. ++ ++ ++the "restrict_host_names" directive was canceled. ++ ++ ++ ++ ++ ++параметр конфигурации --with-cpu-opt=ppc64. ++ ++ ++the --with-cpu-opt=ppc64 configuration parameter. ++ ++ ++ ++ ++ ++при некоторых условиях проксированное соединение с клиентом завершалось ++преждевременно.
++Спасибо Владимиру Шутову. ++
++ ++on some condition the proxied connection with a client was terminated ++prematurely.
++Thanks to Vladimir Shutoff. ++
++
++ ++ ++ ++строка заголовка "X-Accel-Limit-Rate" не учитывалась для запросов, ++перенаправленных с помощью строки "X-Accel-Redirect". ++ ++ ++the "X-Accel-Limit-Rate" header line was not taken into account ++if the request was redirected using the "X-Accel-Redirect" header line. ++ ++ ++ ++ ++ ++директива post_action работала только после успешного завершения запроса. ++ ++ ++the "post_action" directive ran only after a successful completion of a request. ++ ++ ++ ++ ++ ++тело проксированного ответа, создаваемого директивой post_action, ++передавалось клиенту. ++ ++ ++the proxied response body generated by the "post_action" directive ++was transferred to a client. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++директивы variables_hash_max_size и variables_hash_bucket_size. ++ ++ ++the "variables_hash_max_size" and "variables_hash_bucket_size" directives. ++ ++ ++ ++ ++ ++переменная $body_bytes_sent доступна не только в директиве log_format. ++ ++ ++the $body_bytes_sent variable can be used not only in the "log_format" ++directive. ++ ++ ++ ++ ++ ++переменные $ssl_protocol и $ssl_cipher. ++ ++ ++the $ssl_protocol and $ssl_cipher variables. ++ ++ ++ ++ ++ ++определение размера строки кэша распространённых процессоров при старте. ++ ++ ++the cache line size detection for widespread CPUs at start time. ++ ++ ++ ++ ++ ++директива accept_mutex теперь поддерживается посредством fcntl(2) ++на платформах, отличных от i386, amd64, sparc64 и ppc. ++ ++ ++now the "accept_mutex" directive is supported using fcntl(2) ++on platforms different from i386, amd64, sparc64, and ppc. ++ ++ ++ ++ ++ ++директива lock_file и параметр автоконфигурации --with-lock-path=PATH. ++ ++ ++the "lock_file" directive and the --with-lock-path=PATH autoconfiguration ++directive. ++ ++ ++ ++ ++ ++при использовании протокола HTTPS в директиве proxy_pass не передавались ++запросы с телом. ++ ++ ++if the HTTPS protocol was used in the "proxy_pass" directive then ++the requests with the body was not transferred. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директива optimize_host_names переименована в optimize_server_names. ++ ++ ++the "optimize_host_names" directive was renamed to the "optimize_server_names". ++ ++ ++ ++ ++ ++при проксировании подзапроса в SSI бэкенду передавался URI основного запроса, ++если в директиве proxy_pass отсутствовал URI. ++ ++ ++if in the "proxy_pass" directive was no the URI part, then the main request ++URI was transferred to a backend while proxying the SSI subrequest. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++при неверной конфигурации на старте или во время переконфигурации происходил ++segmentation fault; ++ошибка появилась в 0.3.24. ++ ++ ++the segmentation fault was occurred on start or while reconfiguration ++if there was invalid configuration; ++the bug had appeared in 0.3.24. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++обход ошибки в kqueue во FreeBSD. ++ ++ ++for bug in FreeBSD kqueue. ++ ++ ++ ++ ++ ++ответ, создаваемый директивой post_action, теперь не передаётся клиенту. ++ ++ ++now a response generated by the "post_action" directive is not transferred ++to a client. ++ ++ ++ ++ ++ ++при использовании большого количества лог-файлов происходила утечка памяти. ++ ++ ++the memory leaks were occurring if many log files were used. ++ ++ ++ ++ ++ ++внутри одного location работала только первая директива proxy_redirect. ++ ++ ++the first "proxy_redirect" directive was working inside one location. ++ ++ ++ ++ ++ ++на 64-битных платформах при старте мог произойти segmentation fault, ++если использовалось большое количество имён в директивах server_name; ++ошибка появилась в 0.3.18. ++ ++ ++on 64-bit platforms segmentation fault may occurred on start ++if the many names were used in the "server_name" directives; ++the bug had appeared in 0.3.18. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директива optimize_host_names. ++ ++ ++the "optimize_host_names" directive. ++ ++ ++ ++ ++ ++ошибки при использовании переменных в директивах path и alias. ++ ++ ++in using of the variables in the "path" and "alias" directives. ++ ++ ++ ++ ++ ++модуль ngx_http_perl_module неправильно собирался на Linux и Solaris. ++ ++ ++the ngx_http_perl_module was incorrectly built on Linux and Solaris. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++модуль ngx_http_perl_module поддерживает методы $r->args и $r->unescape. ++ ++ ++the ngx_http_perl_module supports the $r->args and $r->unescape methods. ++ ++ ++ ++ ++ ++метод $r->query_string в модуле ngx_http_perl_module упразднён. ++ ++ ++the method $r->query_string of ngx_http_perl_module was canceled. ++ ++ ++ ++ ++ ++если в директиве valid_referers указаны только none или blocked, то ++происходил segmentation fault; ++ошибка появилась в 0.3.18. ++ ++ ++segmentation fault was occurred if the "none" or "blocked" values was ++specified in the "valid_referers" directive; ++the bug had appeared in 0.3.18. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++модуль ngx_http_perl_module. ++ ++ ++the ngx_http_perl_module. ++ ++ ++ ++ ++ ++директива valid_referers разрешает использовать рефереры совсем без URI. ++ ++ ++the "valid_referers" directive allows the referrers without URI part. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ошибки в обработке SSI. ++ ++ ++in SSI handling. ++ ++ ++ ++ ++ ++модуль ngx_http_memcached_module не поддерживал ключи в виде /uri?args. ++ ++ ++the ngx_http_memcached_module did not support the keys in the "/usr?args" form. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директивы path и alias поддерживают переменные. ++ ++ ++the "path" and "alias" directives support the variables. ++ ++ ++ ++ ++ ++теперь директива valid_referers опять учитывает URI. ++ ++ ++now the "valid_referers" directive again checks the URI part. ++ ++ ++ ++ ++ ++ошибки в обработке SSI. ++ ++ ++in SSI handling. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директива server_names поддерживает имена вида ".domain.tld". ++ ++ ++the "server_names" directive supports the ".domain.tld" names. ++ ++ ++ ++ ++ ++директива server_names использует хэш для имён вида "*.domain.tld" ++и более эффективный хэш для обычных имён. ++ ++ ++the "server_names" directive uses the hash for the "*.domain.tld" names ++and more effective hash for usual names. ++ ++ ++ ++ ++ ++директивы server_names_hash_max_size и server_names_hash_bucket_size. ++ ++ ++the "server_names_hash_max_size" and "server_names_hash_bucket_size" directives. ++ ++ ++ ++ ++ ++директивы server_names_hash и server_names_hash_threshold упразднены. ++ ++ ++the "server_names_hash" and "server_names_hash_threshold" directives ++were canceled. ++ ++ ++ ++ ++ ++директива valid_referers использует хэш для имён сайтов. ++ ++ ++the "valid_referers" directive uses the hash site names. ++ ++ ++ ++ ++ ++теперь директива valid_referers проверяет только имена сайтов без учёта URI. ++ ++ ++now the "valid_referers" directive checks the site names only without ++the URI part. ++ ++ ++ ++ ++ ++некоторые имена вида ".domain.tld" неверно обрабатывались модулем ++ngx_http_map_module. ++ ++ ++some ".domain.tld" names incorrectly processed by the ngx_http_map_module. ++ ++ ++ ++ ++ ++если конфигурационного файла не было, то происходил segmentation fault; ++ошибка появилась в 0.3.12. ++ ++ ++segmentation fault was occurred if configuration file did not exist; ++the bug had appeared in 0.3.12. ++ ++ ++ ++ ++ ++на 64-битных платформах при старте мог произойти segmentation fault; ++ошибка появилась в 0.3.16. ++ ++ ++on 64-bit platforms segmentation fault may occurred on start; ++the bug had appeared in 0.3.16. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++на Linux configure теперь проверяет наличие epoll и sendfile64() в ядре. ++ ++ ++now on Linux configure checks the presence of epoll and sendfile64() in kernel. ++ ++ ++ ++ ++ ++директива map поддерживает доменные имена в формате ".domain.tld". ++ ++ ++the "map" directive supports domain names in the ".domain.tld" form. ++ ++ ++ ++ ++ ++во время SSL handshake не иcпользовались таймауты; ++ошибка появилась в 0.2.4. ++ ++ ++the timeouts were not used in SSL handshake; ++the bug had appeared in 0.2.4. ++ ++ ++ ++ ++ ++в использовании протокола HTTPS в директиве proxy_pass. ++ ++ ++in the HTTPS protocol in the "proxy_pass" directive. ++ ++ ++ ++ ++ ++при использовании протокола HTTPS в директиве proxy_pass по умолчанию ++использовался порт 80. ++ ++ ++when the HTTPS protocol was used in the "proxy_pass" directive the port 80 ++was used by default. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++модуль ngx_http_map_module. ++ ++ ++the ngx_http_map_module. ++ ++ ++ ++ ++ ++директивы types_hash_max_size и types_hash_bucket_size. ++ ++ ++the "types_hash_max_size" and "types_hash_bucket_size" directives. ++ ++ ++ ++ ++ ++директива ssi_value_length. ++ ++ ++the "ssi_value_length" directive. ++ ++ ++ ++ ++ ++директива worker_rlimit_core. ++ ++ ++the "worker_rlimit_core" directive. ++ ++ ++ ++ ++ ++при сборке компиляторами icc 8.1 и 9.0 с оптимизацией для ++Pentium 4 номер соединения в логах всегда был равен 1. ++ ++ ++the connection number in logs was always 1 if nginx was built by the ++icc 8.1 or 9.0 compilers with optimization for Pentium 4. ++ ++ ++ ++ ++ ++команда config timefmt в SSI задавала неверный формат времени. ++ ++ ++the "config timefmt" SSI command set incorrect time format. ++ ++ ++ ++ ++ ++nginx не закрывал соединения с IMAP/POP3 бэкендом при использовании SSL ++соединений; ++ошибка появилась в 0.3.13.
++Спасибо Rob Mueller. ++
++ ++nginx did not close connection to IMAP/POP3 backend for the SSL ++connections; ++the bug had appeared in 0.3.13.
++Thanks to Rob Mueller. ++
++
++ ++ ++ ++segmentation fault мог произойти во время SSL shutdown; ++ошибка появилась в 0.3.13. ++ ++ ++segmentation fault may occurred in at SSL shutdown; ++the bug had appeared in 0.3.13. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++новой код 444 в директиве return для закрытия соединения. ++ ++ ++the new 444 code of the "return" directive to close connection. ++ ++ ++ ++ ++ ++директива so_keepalive в IMAP/POP3 прокси. ++ ++ ++the "so_keepalive" directive in IMAP/POP3 proxy. ++ ++ ++ ++ ++ ++nginx теперь вызывает abort() при обнаружении незакрытых соединений ++только при плавном выходе и включённой директиве debug_points. ++ ++ ++if there are unclosed connection nginx now calls abort() only on graceful ++quit and active "debug_points" directive. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++в ответе 304 передавалось тело ответа; ++ошибка появилась в 0.3.13. ++ ++ ++in the 304 response the body was transferred; ++the bug had appeared in 0.3.13. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++IMAP/POP3 прокси поддерживает STARTTLS и STLS. ++ ++ ++the IMAP/POP3 proxy supports STARTTLS and STLS. ++ ++ ++ ++ ++ ++IMAP/POP3 прокси не работала с методами select, poll и /dev/poll. ++ ++ ++the IMAP/POP3 proxy did not work with the select, poll, and /dev/poll methods. ++ ++ ++ ++ ++ ++ошибки в обработке SSI. ++ ++ ++in SSI handling. ++ ++ ++ ++ ++ ++sendfilev() в Solaris теперь не используется при передаче тела запроса ++FastCGI-серверу через unix domain сокет. ++ ++ ++now Solaris sendfilev() is not used to transfer the client request body ++to FastCGI-server via the unix domain socket. ++ ++ ++ ++ ++ ++директива auth_basic не запрещала аутентификацию; ++ошибка появилась в 0.3.11. ++ ++ ++the "auth_basic" directive did not disable the authorization; ++the bug had appeared in 0.3.11. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++если nginx был собран с модулем ngx_http_realip_module, то при использовании ++директивы "satisfy_any on" директивы доступа и аутентификации не работали. ++Модуль ngx_http_realip_module не собирался и не собирается по умолчанию. ++ ++ ++if nginx was built with the ngx_http_realip_module and the "satisfy_any on" ++directive was used, then access and authorization directives did not work. ++The ngx_http_realip_module was not built and is not built by default. ++ ++ ++ ++ ++ ++имя переменной "$time_gmt" изменено на "$time_local". ++ ++ ++the "$time_gmt" variable name was changed to "$time_local". ++ ++ ++ ++ ++ ++директивы proxy_header_buffer_size и fastcgi_header_buffer_size ++переименованы соответственно в proxy_buffer_size и fastcgi_buffer_size. ++ ++ ++the "proxy_header_buffer_size" and "fastcgi_header_buffer_size" directives ++was renamed to the "proxy_buffer_size" and "fastcgi_buffer_size" directives. ++ ++ ++ ++ ++ ++модуль ngx_http_memcached_module. ++ ++ ++the ngx_http_memcached_module. ++ ++ ++ ++ ++ ++директива proxy_buffering. ++ ++ ++the "proxy_buffering" directive. ++ ++ ++ ++ ++ ++изменение в работе с accept mutex при использовании метода rtsig; ++ошибка появилась в 0.3.0. ++ ++ ++the changes in accept mutex handling when the "rtsig" method was used; ++the bug had appeared in 0.3.0. ++ ++ ++ ++ ++ ++если клиент передал строку "Transfer-Encoding: chunked" в заголовке ++запроса, то nginx теперь выдаёт ошибку 411. ++ ++ ++if the client sent the "Transfer-Encoding: chunked" header line, then ++nginx returns the 411 error. ++ ++ ++ ++ ++ ++при наследовании директивы auth_basic с уровня http в строке ++"WWW-Authenticate" заголовка ответа выводился realm без текста "Basic realm". ++ ++ ++if the "auth_basic" directive was inherited from the http level, ++then the realm in the "WWW-Authenticate" header line was without ++the "Basic realm" text. ++ ++ ++ ++ ++ ++если в директиве access_log был явно указан формат combined, то в лог ++записывались пустые строки; ++ошибка появилась в 0.3.8. ++ ++ ++if the "combined" format was explicitly specified in the "access_log" directive, ++then the empty lines was written to the log; ++the bug had appeared in 0.3.8. ++ ++ ++ ++ ++ ++nginx не работал на платформе sparc под любыми OS, кроме Solaris. ++ ++ ++nginx did not run on the sparc platform under any OS except Solaris. ++ ++ ++ ++ ++ ++в директиве if теперь не нужно разделять пробелом строку в кавычках и ++закрывающую скобку. ++ ++ ++now it is not necessary to place space between the quoted string and closing ++bracket in the "if" directive. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++nginx не передавал при проксировании тело запроса и строки заголовка клиента; ++ошибка появилась в 0.3.10. ++ ++ ++nginx did not pass the client request headers and body while proxying; ++the bug had appeared in 0.3.10. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директива valid_referers и переменная $invalid_referer перенесены ++из модуля ngx_http_rewrite_module в новый модуль ngx_http_referer_module. ++ ++ ++the "valid_referers" directive and the "$invalid_referer" variable ++were moved to the new ngx_http_referer_module from the ngx_http_rewrite_module. ++ ++ ++ ++ ++ ++имя переменной "$apache_bytes_sent" изменено на "$body_bytes_sent". ++ ++ ++the "$apache_bytes_sent" variable name was changed to "$body_bytes_sent". ++ ++ ++ ++ ++ ++переменные "$sent_http_...". ++ ++ ++the "$sent_http_..." variables. ++ ++ ++ ++ ++ ++директива if поддерживает операции "=" и "!=". ++ ++ ++the "if" directive supports the "=" and "!=" operations. ++ ++ ++ ++ ++ ++директива proxy_pass поддерживает протокол HTTPS. ++ ++ ++the "proxy_pass" directive supports the HTTPS protocol. ++ ++ ++ ++ ++ ++директива proxy_set_body. ++ ++ ++the "proxy_set_body" directive. ++ ++ ++ ++ ++ ++директива post_action. ++ ++ ++the "post_action" directive. ++ ++ ++ ++ ++ ++модуль ngx_http_empty_gif_module. ++ ++ ++the ngx_http_empty_gif_module. ++ ++ ++ ++ ++ ++директива worker_cpu_affinity для Linux. ++ ++ ++the "worker_cpu_affinity" directive for Linux. ++ ++ ++ ++ ++ ++директива rewrite не раскодировала символы в редиректах в URI, ++теперь символы раскодируются, кроме символов %00-%25 и %7F-%FF. ++ ++ ++the "rewrite" directive did not unescape URI part in redirect, ++now it is unescaped except the %00-%25 and %7F-%FF characters. ++ ++ ++ ++ ++ ++nginx не собирался компилятором icc 9.0. ++ ++ ++nginx could not be built by the icc 9.0 compiler. ++ ++ ++ ++ ++ ++если для статического файла нулевого размера был разрешён SSI, ++то ответ передавался неверно при кодировании chunk'ами. ++ ++ ++if the SSI was enabled for zero size static file, then the chunked ++response was encoded incorrectly. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++nginx считал небезопасными URI, в которых между двумя слэшами ++находилось два любых символа; ++ошибка появилась в 0.3.8. ++ ++ ++nginx considered URI as unsafe if two any symbols was between two slashes; ++the bug had appeared in 0.3.8. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++nginx теперь проверят URI, полученные от бэкенда в строке "X-Accel-Redirect" ++в заголовке ответа, или в SSI файле на наличие путей "/../" и нулей. ++ ++ ++nginx now checks URI got from a backend in "X-Accel-Redirect" header line ++or in SSI file for the "/../" paths and zeroes. ++ ++ ++ ++ ++ ++nginx теперь не воспринимает пустое имя как правильное ++в строке "Authorization" в заголовке запроса. ++ ++ ++nginx now does not treat the empty user name in the "Authorization" header ++line as valid one. ++ ++ ++ ++ ++ ++директива ssl_session_timeout модулей ++ngx_http_ssl_module и ngx_imap_ssl_module. ++ ++ ++the "ssl_session_timeout" directives ++of the ngx_http_ssl_module and ngx_imap_ssl_module. ++ ++ ++ ++ ++ ++директива auth_http_header модуля ngx_imap_auth_http_module. ++ ++ ++the "auth_http_header" directive of the ngx_imap_auth_http_module. ++ ++ ++ ++ ++ ++директива add_header. ++ ++ ++the "add_header" directive. ++ ++ ++ ++ ++ ++модуль ngx_http_realip_module. ++ ++ ++the ngx_http_realip_module. ++ ++ ++ ++ ++ ++новые переменные для использования в директиве log_format: ++$bytes_sent, $apache_bytes_sent, $status, $time_gmt, ++$uri, $request_time, $request_length, ++$upstream_status, $upstream_response_time, ++$gzip_ratio, ++$uid_got, $uid_set, ++$connection, $pipe и $msec. ++Параметры в виде "%name" скоро будут упразднены. ++ ++ ++the new variables to use in the "log_format" directive: ++$bytes_sent, $apache_bytes_sent, $status, $time_gmt, ++$uri, $request_time, $request_length, ++$upstream_status, $upstream_response_time, ++$gzip_ratio, ++$uid_got, $uid_set, ++$connection, $pipe, and $msec. ++The parameters in the "%name" form will be canceled soon. ++ ++ ++ ++ ++ ++в директиве "if" ложными значениями переменных теперь являются ++пустая строка "" и строки, начинающиеся на "0". ++ ++ ++now the false variable values in the "if" directive are the empty string "" ++and string starting with "0". ++ ++ ++ ++ ++ ++при работает с проксированными или FastCGI-серверами nginx мог оставлять ++открытыми соединения и временные файлы с запросами клиентов. ++ ++ ++while using proxied or FastCGI-server nginx may leave connections ++and temporary files with client requests in open state. ++ ++ ++ ++ ++ ++рабочие процессы не сбрасывали буферизированные логи при плавном выходе. ++ ++ ++the worker processes did not flush the buffered logs on graceful exit. ++ ++ ++ ++ ++ ++если URI запроса изменялось с помощью rewrite, а затем запрос проксировался ++в location, заданном регулярным выражением, то бэкенду передавался ++неверный запрос; ++ошибка появилась в 0.2.6. ++ ++ ++if the request URI was changes by the "rewrite" directive and the request ++was proxied in location given by regular expression, then the incorrect ++request was transferred to backend; ++the bug had appeared in 0.2.6. ++ ++ ++ ++ ++ ++директива expires не удаляла уже установленную строку заголовка "Expires". ++ ++ ++the "expires" directive did not remove the previous "Expires" header. ++ ++ ++ ++ ++ ++при использовании метода rtsig и нескольких рабочих процессах nginx ++мог перестать принимать запросы. ++ ++ ++nginx may stop to accept requests if the "rtsig" method and several worker ++processes were used. ++ ++ ++ ++ ++ ++в SSI командах неверно обрабатывались строки "\"" и "\'". ++ ++ ++the "\"" and "\'" escape symbols were incorrectly handled in SSI commands. ++ ++ ++ ++ ++ ++если ответ заканчивался сразу же после SSI команды, то при использовании ++сжатия ответ передавался не до конца или не передавался вообще. ++ ++ ++if the response was ended just after the SSI command and gzipping was used, ++then the response did not transferred complete or did not transferred at all. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директива access_log поддерживает параметр buffer=. ++ ++ ++the "access_log" supports the "buffer=" parameter. ++ ++ ++ ++ ++ ++nginx не собирался на платформах, отличных от i386, amd64, sparc и ppc; ++ошибка появилась в 0.3.2. ++ ++ ++nginx could not be built on platforms different from i386, amd64, sparc, ++and ppc; ++the bug had appeared in 0.3.2. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++IMAP/POP3 прокси теперь не передаёт серверу авторизации пустой логин. ++ ++ ++now the IMAP/POP3 proxy do not send the empty login to authorization server. ++ ++ ++ ++ ++ ++директива log_format поддерживает переменные в виде $name. ++ ++ ++the "log_format" supports the variables in the $name form. ++ ++ ++ ++ ++ ++если хотя бы в одном сервере не было описано ни одной директивы listen, то ++nginx не слушал на 80 порту; ++ошибка появилась в 0.3.3. ++ ++ ++if at least in one server was no the "listen" directive, then nginx did not ++listen on the 80 port; ++the bug had appeared in 0.3.3. ++ ++ ++ ++ ++ ++если в директиве proxy_pass отсутствовал URI, то всегда использовался порт 80. ++ ++ ++if the URI part is omitted in "proxy_pass" directive, the 80 port was ++always used. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++если логин IMAP/POP3 менялся сервером авторизации, то мог произойти ++segmentation fault; ++ошибка появилась в 0.2.2. ++ ++ ++the segmentation fault may occurred if the IMAP/POP3 login was changed ++by authorization server; ++the bug had appeared in 0.2.2. ++ ++ ++ ++ ++ ++accept mutex не работал, все соединения обрабатывались одним рабочим процессом; ++ошибка появилась в 0.3.3. ++ ++ ++the accept mutex did not work and all connections were handled by one process; ++the bug had appeared in 0.3.3. ++ ++ ++ ++ ++ ++при использовании метода rtsig и директивы timer_resolution ++не работали таймауты. ++ ++ ++the timeout did not work if the "rtsig" method and the "timer_resolution" ++directive were used. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++nginx не собирался на Linux 2.4+ и MacOS X; ++ошибка появилась в 0.3.3. ++ ++ ++nginx could not be built on Linux 2.4+ and MacOS X; ++the bug had appeared in 0.3.3. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++параметры "bl" и "af" директивы listen переименованы в "backlog" ++и "accept_filter". ++ ++ ++the "bl" and "af" parameters of the "listen" directive was renamed to ++the "backlog" and "accept_filter". ++ ++ ++ ++ ++ ++параметры "rcvbuf" и "sndbuf" в директиве listen. ++ ++ ++the "rcvbuf" and "sndbuf" parameters of the "listen" directive. ++ ++ ++ ++ ++ ++параметр лога $msec теперь не требует дополнительного системного ++вызова gettimeofday(). ++ ++ ++the "$msec" log parameter does not require now the additional ++the gettimeofday() system call. ++ ++ ++ ++ ++ ++ключ -t теперь проверяет директивы listen. ++ ++ ++the -t switch now tests the "listen" directives. ++ ++ ++ ++ ++ ++если в директиве listen был указан неверный адрес, то nginx после ++сигнала -HUP оставлял открытый сокет в состоянии CLOSED. ++ ++ ++if the invalid address was specified in the "listen" directive, then ++after the -HUP signal nginx left an open socket in the CLOSED state. ++ ++ ++ ++ ++ ++для индексных файлов, содержащих в имени переменную, мог неверно выставляться ++тип mime по умолчанию; ++ошибка появилась в 0.3.0. ++ ++ ++the mime type may be incorrectly set to default value for index file with ++variable in the name; ++the bug had appeared in 0.3.0. ++ ++ ++ ++ ++ ++директива timer_resolution. ++ ++ ++the "timer_resolution" directive. ++ ++ ++ ++ ++ ++параметр лога $upstream_response_time в миллисекундах. ++ ++ ++the millisecond "$upstream_response_time" log parameter. ++ ++ ++ ++ ++ ++временный файл с телом запроса клиента теперь удаляется сразу после того, ++как клиенту передан заголовок ответа. ++ ++ ++a temporary file with client request body now is removed just after ++the response header was transferred to a client. ++ ++ ++ ++ ++ ++совместимость с OpenSSL 0.9.6. ++ ++ ++OpenSSL 0.9.6 compatibility. ++ ++ ++ ++ ++ ++пути к файлам с SSL сертификатом и ключом не могли быть относительными. ++ ++ ++the SSL certificate and key file paths could not be relative. ++ ++ ++ ++ ++ ++директива ssl_prefer_server_ciphers не работала для модуля ngx_imap_ssl_module. ++ ++ ++the "ssl_prefer_server_ciphers" directive did not work in ++the ngx_imap_ssl_module. ++ ++ ++ ++ ++ ++директива ssl_protocols позволяла задать только один протокол. ++ ++ ++the "ssl_protocols" directive allowed to specify the single protocol only. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++поддержка Sun Studio 10 C compiler. ++ ++ ++the Sun Studio 10 C compiler support. ++ ++ ++ ++ ++ ++директивы proxy_upstream_max_fails, proxy_upstream_fail_timeout, ++fastcgi_upstream_max_fails и fastcgi_upstream_fail_timeout. ++ ++ ++the "proxy_upstream_max_fails", "proxy_upstream_fail_timeout", ++"fastcgi_upstream_max_fails", and "fastcgi_upstream_fail_timeout" ++directives. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++во время переполнения очереди сигналов при использовании метода rtsig ++происходил segmentation fault; ++ошибка появилась в 0.2.0. ++ ++ ++the segmentation fault occurred when the signal queue overflowed ++if the "rtsig" method was used; ++the bug had appeared in 0.2.0. ++ ++ ++ ++ ++ ++корректная обработка пар "\\", "\"", "\'" и "\$" в SSI. ++ ++ ++correct handling of the "\\", "\"", "\'", and "\$" pairs in SSI. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++убрано десятидневное ограничение времени работы рабочего процесса. ++Ограничение было введено из-за переполнения миллисекундных таймеров. ++ ++ ++the 10-days live time limit of worker process was eliminated. ++The limit was introduced because of millisecond timers overflow. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++с 60 до 10 секунд уменьшено время повторного обращения к бэкенду ++при использовании распределения нагрузки. ++ ++ ++while using load-balancing the time before the failed backend retry ++was decreased from 60 to 10 seconds. ++ ++ ++ ++ ++ ++директива proxy_pass_unparsed_uri упразднена, оригинальный запрос теперь ++передаётся, если в директиве proxy_pass отсутствует URI. ++ ++ ++the "proxy_pass_unparsed_uri" was canceled, the original URI now passed, ++if the URI part is omitted in "proxy_pass" directive. ++ ++ ++ ++ ++ ++директива error_page поддерживает редиректы и позволяет более гибко ++менять код ошибки. ++ ++ ++the "error_page" directive supports redirects and allows more flexible ++to change an error code. ++ ++ ++ ++ ++ ++в проксированных подзапросах теперь игнорируется переданный charset. ++ ++ ++the charset in the "Content-Type" header line now is ignored ++in proxied subrequests. ++ ++ ++ ++ ++ ++если после изменения URI в блоке if для запроса не находилась ++новая конфигурация, то правила модуля ngx_http_rewrite_module выполнялись ++снова. ++ ++ ++if the URI was changed in the "if" block and request did not found ++new configuration, then the ngx_http_rewrite_module rules ran again. ++ ++ ++ ++ ++ ++если директива set устанавливала переменную модуля ngx_http_geo_module ++в какой-либо части конфигурации, то эта переменная не была доступна в ++других частях конфигурации и выдавалась ошибка "using uninitialized variable"; ++ошибка появилась в 0.2.2. ++ ++ ++if the "set" directive set the ngx_http_geo_module variable in some ++configuration part, the this variable was not available in other ++configuration parts and the "using uninitialized variable" error was occurred; ++the bug had appeared in 0.2.2. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++дублирующее значение переменной модуля ngx_http_geo_module теперь ++выдаёт предупреждение и изменяет старое значение. ++ ++ ++the duplicate value of the ngx_http_geo_module variable now causes ++the warning and changes old value. ++ ++ ++ ++ ++ ++модуль ngx_http_ssi_module поддерживает команду set. ++ ++ ++the ngx_http_ssi_module supports the "set" command. ++ ++ ++ ++ ++ ++модуль ngx_http_ssi_module поддерживает параметр file в команде include. ++ ++ ++the ngx_http_ssi_module supports the "file" parameter in the "include" command. ++ ++ ++ ++ ++ ++модуль ngx_http_ssi_module поддерживает подстановку значений переменных ++в выражениях команды if. ++ ++ ++the ngx_http_ssi_module supports the variable value substitutions in ++expressions of the "if" command. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++модуль ngx_http_ssi_module поддерживает выражения ++"$var=text", "$var!=text", "$var=/text/" и "$var!=/text/" ++в команде if. ++ ++ ++the ngx_http_ssi_module supports ++"$var=text", "$var!=text", "$var=/text/", and "$var!=/text/" expressions ++in the "if" command. ++ ++ ++ ++ ++ ++ошибки при проксировании location без слэша в конце; ++ошибка появилась в 0.1.44. ++ ++ ++in proxying location without trailing slash; ++the bug had appeared in 0.1.44. ++ ++ ++ ++ ++ ++при использовании метода rtsig мог произойти segmentation fault; ++ошибка появилась в 0.2.0. ++ ++ ++the segmentation fault may occurred if the "rtsig" method was used; ++the bug had appeared in 0.2.0. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++nginx не собирался без параметра --with-debug; ++ошибка появилась в 0.2.2. ++ ++ ++nginx could not be built without the --with-debug option; ++the bug had appeared in 0.2.2. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++команда config errmsg в модуле ngx_http_ssi_module. ++ ++ ++the "config errmsg" command of the ngx_http_ssi_module. ++ ++ ++ ++ ++ ++переменные модуля ngx_http_geo_module можно переопределять директивой set. ++ ++ ++the ngx_http_geo_module variables can be overridden by the "set" directive. ++ ++ ++ ++ ++ ++директивы ssl_protocols и ssl_prefer_server_ciphers модулей ++ngx_http_ssl_module и ngx_imap_ssl_module. ++ ++ ++the "ssl_protocols" and "ssl_prefer_server_ciphers" directives ++of the ngx_http_ssl_module and ngx_imap_ssl_module. ++ ++ ++ ++ ++ ++ошибка в модуле ngx_http_autoindex_module при показе длинных имён файлов; ++ ++ ++the ngx_http_autoindex_module did not show correctly the long file names; ++ ++ ++ ++ ++ ++модуль ngx_http_autoindex_module теперь не показывает файлы, ++начинающиеся на точку. ++ ++ ++the ngx_http_autoindex_module now do not show the files starting by dot. ++ ++ ++ ++ ++ ++если SSL handshake завершался с ошибкой, то это могло привести также ++к закрытию другого соединения.
++Спасибо Rob Mueller. ++
++ ++if the SSL handshake failed then another connection may be closed too.
++Thanks to Rob Mueller. ++
++
++ ++ ++ ++экспортные версии MSIE 5.x не могли соединиться по HTTPS. ++ ++ ++the export versions of MSIE 5.x could not connect via HTTPS. ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++если все бэкенды, используемые для балансировки нагрузки, оказывались ++в нерабочем состоянии после одной ошибки, то nginx мог зациклится; ++ошибка появилась в 0.2.0. ++ ++ ++if all backend using in load-balancing failed after one error, then ++nginx may got caught in an endless loop; ++the bug had appeared in 0.2.0. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++Изменились имена pid-файлов, используемые во время обновления исполняемого ++файла. Ручное переименование теперь не нужно. ++Старый основной процесс добавляет к своему pid-файл суффикс ".oldbin" ++и запускает новый исполняемый файл. ++Новый основной процесс создаёт обычный pid-файл без суффикса ".newbin". ++Если новый основной процесс выходит, то старый процесс переименовывает свой ++pid-файл c суффиксом ".oldbin" в pid-файл без суффикса. ++При обновлении с версии 0.1.х до 0.2.0 нужно учитывать, что оба ++процесса—старый 0.1.x и новый 0.2.0—используют pid-файл ++без суффиксов. ++ ++ ++The pid-file names used during online upgrade was changed and now is not ++required a manual rename operation. ++The old master process adds the ".oldbin" suffix to its pid-file and ++executes a new binary file. ++The new master process creates usual pid-file without the ".newbin" suffix. ++If the master process exits, then old master process renames back ++its pid-file with the ".oldbin" suffix to the pid-file without suffix. ++ ++ ++ ++ ++ ++директива worker_connections, новое название директивы connections; ++директива теперь задаёт максимальное число соединений, ++а не максимально возможный номер дескриптора для сокета. ++ ++ ++the "worker_connections" directive, new name of the "connections" directive; ++now the directive specifies maximum number of connections, ++but not maximum socket descriptor number. ++ ++ ++ ++ ++ ++SSL поддерживает кэширование сессий в пределах одного рабочего процесса. ++ ++ ++SSL supports the session cache inside one worker process. ++ ++ ++ ++ ++ ++директива satisfy_any. ++ ++ ++the "satisfy_any" directive. ++ ++ ++ ++ ++ ++модули ngx_http_access_module и ngx_http_auth_basic_module не работают ++для подзапросов. ++ ++ ++the ngx_http_access_module and ngx_http_auth_basic_module do not run ++for subrequests. ++ ++ ++ ++ ++ ++директивы worker_rlimit_nofile и worker_rlimit_sigpending. ++ ++ ++the "worker_rlimit_nofile" and "worker_rlimit_sigpending" directives. ++ ++ ++ ++ ++ ++если все бэкенды, используемые для балансировки нагрузки, оказывались ++в нерабочем состоянии после одной ошибки, то nginx не обращался к ним ++в течение 60 секунд. ++ ++ ++if all backend using in load-balancing failed after one error, then ++nginx did not try do connect to them during 60 seconds. ++ ++ ++ ++ ++ ++в парсинге аргументов IMAP/POP3 команд.
++Спасибо Rob Mueller. ++
++ ++in IMAP/POP3 command argument parsing.
++Thanks to Rob Mueller. ++
++
++ ++ ++ ++ошибки при использовании SSL в IMAP/POP3 прокси. ++ ++ ++errors while using SSL in IMAP/POP3 proxy. ++ ++ ++ ++ ++ ++ошибки при использовании SSI и сжатия. ++ ++ ++errors while using SSI and gzipping. ++ ++ ++ ++ ++ ++в ответах 304 не добавлялись строки заголовка ответа "Expires" и ++"Cache-Control".
++Спасибо Александру Кукушкину. ++
++ ++the "Expires" and "Cache-Control" header lines were omitted ++from the 304 responses.
++Thanks to Alexandr Kukushkin. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++директива ssl_engine упразднена в модуле ngx_http_ssl_module и ++перенесена на глобальный уровень. ++ ++ ++the "ssl_engine" directive was canceled in the ngx_http_ssl_module ++and now is introduced at global level. ++ ++ ++ ++ ++ ++ответы с подзапросами, включённые с помощью SSI, не передавались ++через SSL соединение. ++ ++ ++the responses with SSI subrequests did not transferred via SSL connection. ++ ++ ++ ++ ++ ++Разные исправления в IMAP/POP3 прокси. ++ ++ ++Various bug fixes in the IMAP/POP3 proxy. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++IMAP/POP3 прокси поддерживает SSL. ++ ++ ++the IMAP/POP3 proxy supports SSL. ++ ++ ++ ++ ++ ++директива proxy_timeout модуля ngx_imap_proxy_module. ++ ++ ++the "proxy_timeout" directive of the ngx_imap_proxy_module. ++ ++ ++ ++ ++ ++директива userid_mark. ++ ++ ++the "userid_mark" directive. ++ ++ ++ ++ ++ ++значение переменной $remote_user определяется независимо от того, ++используется ли авторизация или нет. ++ ++ ++the $remote_user variable value is determined independently of ++authorization use. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++listen(2) backlog в директиве listen можно менять по сигналу -HUP. ++ ++ ++the listen(2) backlog in the "listen" directive ++can be changed using the -HUP signal. ++ ++ ++ ++ ++ ++скрипт geo2nginx.pl добавлен в contrib. ++ ++ ++the geo2nginx.pl script was added to contrib. ++ ++ ++ ++ ++ ++параметры FastCGI с пустым значениями теперь передаются серверу. ++ ++ ++the FastCGI parameters with the empty values now are passed to a server. ++ ++ ++ ++ ++ ++ ++ ++если в ответе проксированного сервера или FastCGI сервера была строка ++"Cache-Control", то при использовании директивы expires происходил ++segmentation fault или рабочий процесс мог зациклится; ++в режиме прокси ошибка появилась в 0.1.29. ++ ++ ++the segmentation fault occurred or the worker process may got caught ++in an endless loop if the proxied or FastCGI server sent the "Cache-Control" ++header line and the "expires" directive was used; ++in the proxied mode the bug had appeared in 0.1.29. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++если URI запроса получался нулевой длины после обработки модулем ++ngx_http_rewrite_module, то в модуле ngx_http_proxy_module происходил ++segmentation fault или bus error. ++ ++ ++if the request URI had a zero length after the processing in ++the ngx_http_proxy_module, then the segmentation fault or bus error occurred ++in the ngx_http_proxy_module. ++ ++ ++ ++ ++ ++директива limit_rate не работала внутри блока if; ++ошибка появилась в 0.1.38. ++ ++ ++the "limit_rate" directive did not work inside the "if" block; ++the bug had appeared in 0.1.38. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++если переменная использовалась в файле конфигурации, ++то она не могла использоваться в SSI. ++ ++ ++if the variable was used in the configuration file, ++then it can not be used in SSI. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++если клиент слал очень длинную строку заголовка, то в логе не помещалась ++информация, связанная с этим запросом. ++ ++ ++if a client sent too long header line, then the request information ++did not logged in the error log. ++ ++ ++ ++ ++ ++при использовании "X-Accel-Redirect" не передавалась строка "Set-Cookie"; ++ошибка появилась в 0.1.39. ++ ++ ++the "Set-Cookie" header line was not transferred when the "X-Accel-Redirect" ++was used; ++the bug had appeared in 0.1.39. ++ ++ ++ ++ ++ ++при использовании "X-Accel-Redirect" не передавалась строка ++"Content-Disposition". ++ ++ ++the "Content-Disposition" header line was not transferred when ++the "X-Accel-Redirect" was used. ++ ++ ++ ++ ++ ++по сигналу SIGQUIT основной процесс не закрывал сокеты, на которых он слушал. ++ ++ ++the master process did not close the listen socket on the SIGQUIT signal. ++ ++ ++ ++ ++ ++после обновления исполняемого файла на лету на Linux и Solaris ++название процесса в команде ps становилось короче. ++ ++ ++after on-line upgrade on Linux and Solaris the process name ++became shorter in the "ps" command. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++Изменения в модуле ngx_http_charset_module: ++директива default_charset упразднена; ++директива charset задаёт кодировку ответа; ++директива source_charset задаёт только исходную кодировку. ++ ++ ++The changes in the ngx_http_charset_module: ++the "default_charset" directive was canceled; ++the "charset" directive sets the response charset; ++the "source_charset" directive sets the source charset only. ++ ++ ++ ++ ++ ++при перенаправлении ошибки 401, полученной от бэкенда, не передавалась ++строка заголовка "WWW-Authenticate". ++ ++ ++the backend "WWW-Authenticate" header line did not transferred while ++the 401 response code redirecting. ++ ++ ++ ++ ++ ++модули ngx_http_proxy_module и ngx_http_fastcgi_module могли закрыть ++соединение до того, как что-нибудь было передано клиенту; ++ошибка появилась в 0.1.38. ++ ++ ++the ngx_http_proxy_module and ngx_http_fastcgi_module may close ++a connection before anything was transferred to a client; ++the bug had appeared in 0.1.38. ++ ++ ++ ++ ++ ++обработка ошибки инициализации в crypt_r() в Linux glibc. ++ ++ ++the Linux glibc crypt_r() initialization bug. ++ ++ ++ ++ ++ ++модуль ngx_http_ssi_module не поддерживал относительные URI в ++команде include virtual. ++ ++ ++the ngx_http_ssi_module did not support the relative URI in ++the "include virtual" command. ++ ++ ++ ++ ++ ++если в строке заголовка ответа бэкенда была строка "Location", ++которую nginx не должен был изменять, то в ответе передавалось тело 500 ошибки; ++ошибка появилась в 0.1.29. ++ ++ ++if the backend response had the "Location" header line and nginx ++should not rewrite this line, then the 500 code response body was transferred; ++the bug had appeared in 0.1.29. ++ ++ ++ ++ ++ ++некоторые директивы модулей ngx_http_proxy_module и ngx_http_fastcgi_module ++не наследовались с уровня server на уровень location; ++ошибка появилась в 0.1.29. ++ ++ ++some directives of the ngx_http_proxy_module and ngx_http_fastcgi_module ++were not inherited from the server to the location level; ++the bug had appeared in 0.1.29. ++ ++ ++ ++ ++ ++модуль ngx_http_ssl_module не поддерживал цепочки сертификатов. ++ ++ ++the ngx_http_ssl_module did not support the certificate chain. ++ ++ ++ ++ ++ ++ошибка в модуле ngx_http_autoindex_module при показе длинных имён файлов; ++ошибка появилась в 0.1.38. ++ ++ ++the ngx_http_autoindex_module did not show correctly the long file names; ++the bug had appeared in 0.1.38. ++ ++ ++ ++ ++ ++Исправления в IMAP/POP3 прокси при взаимодействии с бэкендом на стадии login. ++ ++ ++Bugfixes in IMAP/POP3 proxy in interaction with a backend at the login state. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директива limit_rate поддерживается в режиме прокси и FastCGI. ++ ++ ++the "limit_rate" directive is supported in proxy and FastCGI mode. ++ ++ ++ ++ ++ ++в режиме прокси и FastCGI поддерживается строка заголовка "X-Accel-Limit-Rate" ++в ответе бэкенда. ++ ++ ++the "X-Accel-Limit-Rate" response header line is supported in proxy ++and FastCGI mode. ++ ++ ++ ++ ++ ++директива break. ++ ++ ++the "break" directive. ++ ++ ++ ++ ++ ++директива log_not_found. ++ ++ ++the "log_not_found" directive. ++ ++ ++ ++ ++ ++при перенаправлении запроса с помощью строки заголовка "X-Accel-Redirect" ++не изменялся код ответа. ++ ++ ++the response status code was not changed when request was redirected ++by the ""X-Accel-Redirect" header line. ++ ++ ++ ++ ++ ++переменные, установленные директивой set не могли использоваться в SSI. ++ ++ ++the variables set by the "set" directive could not be used in SSI. ++ ++ ++ ++ ++ ++при включении в SSI более одного удалённого подзапроса ++мог произойти segmentation fault. ++ ++ ++the segmentation fault may occurred if the SSI page has more than one ++remote subrequest. ++ ++ ++ ++ ++ ++если статусная строка в ответе бэкенда передавалась в двух пакетах, то ++nginx считал ответ неверным; ++ошибка появилась в 0.1.29. ++ ++ ++nginx treated the backend response as invalid if the status line in the ++header was transferred in two packets; ++the bug had appeared in 0.1.29. ++ ++ ++ ++ ++ ++директива ssi_types. ++ ++ ++the "ssi_types" directive. ++ ++ ++ ++ ++ ++директива autoindex_exact_size. ++ ++ ++the "autoindex_exact_size" directive. ++ ++ ++ ++ ++ ++модуль ngx_http_autoindex_module не поддерживал длинные имена файлов в UTF-8. ++ ++ ++the ngx_http_autoindex_module did not support the long file names in UTF-8. ++ ++ ++ ++ ++ ++IMAP/POP3 прокси. ++ ++ ++the IMAP/POP3 proxy. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++в конце файла nginx.pid теперь добавляется "\n". ++ ++ ++now the "\n" is added to the end of the "nginx.pid" file. ++ ++ ++ ++ ++ ++при включении большого количества вставок или нескольких больших вставок ++с помощью SSI ответ мог передаваться не полностью. ++ ++ ++the responses may be transferred not completely, ++if many parts or the big parts were included by SSI. ++ ++ ++ ++ ++ ++если все бэкенды возвращали ответ 404, то при использовании параметра http_404 ++в директивах proxy_next_upstream или fastcgi_next_upstream, nginx ++начинал запрашивать все бэкенды снова. ++ ++ ++if all backends had returned the 404 response and the "http_404" parameter of ++the "proxy_next_upstream" or "fastcgi_next_upstream" directives was used, ++then nginx started to request all backends again. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++если в заголовке запроса есть дублирующиеся строки "Host", "Connection", ++"Content-Length" и "Authorization", то nginx теперь выдаёт ошибку 400. ++ ++ ++if the request header has duplicate the "Host", "Connection", "Content-Length", ++or "Authorization" lines, then nginx now returns the 400 error. ++ ++ ++ ++ ++ ++директива post_accept_timeout упразднена. ++ ++ ++the "post_accept_timeout" directive was canceled. ++ ++ ++ ++ ++ ++параметры default, af=, bl=, deferred и bind в директиве listen. ++ ++ ++the "default", "af=", "bl=", "deferred", and "bind" parameters ++of the "listen" directive. ++ ++ ++ ++ ++ ++поддержка accept фильтров во FreeBSD. ++ ++ ++the FreeBSD accept filters support. ++ ++ ++ ++ ++ ++поддержка TCP_DEFER_ACCEPT в Linux. ++ ++ ++the Linux TCP_DEFER_ACCEPT support. ++ ++ ++ ++ ++ ++модуль ngx_http_autoindex_module не поддерживал имена файлов в UTF-8. ++ ++ ++the ngx_http_autoindex_module did not support the file names in UTF-8. ++ ++ ++ ++ ++ ++после добавления новый лог-файл ротация этого лога по сигналу -USR1 ++выполнялась, только если переконфигурировать nginx два раза по сигналу -HUP. ++ ++ ++the new log file can be rotated by the -USR1 signal only if ++the reconfiguration by the -HUP signal was made twice. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директива working_directory. ++ ++ ++the "working_directory" directive. ++ ++ ++ ++ ++ ++директива port_in_redirect. ++ ++ ++the "port_in_redirect" directive. ++ ++ ++ ++ ++ ++если заголовок ответа бэкенда не помещался в один пакет, то ++происходил segmentation fault; ++ошибка появилась в 0.1.29. ++ ++ ++the segmentation fault was occurred if the backend response header was in ++several packets; ++the bug had appeared in 0.1.29. ++ ++ ++ ++ ++ ++если было сконфигурировано более 10 серверов или в сервере не описана ++директива "listen", ++то при запуске мог произойти segmentation fault. ++ ++ ++if more than 10 servers were configured or some server did not use the ++"listen" directive, then the segmentation fault was occurred on the start. ++ ++ ++ ++ ++ ++если ответ не помещался во временный файл, ++то мог произойти segmentation fault. ++ ++ ++the segmentation fault might occur if the response was bigger than ++the temporary file. ++ ++ ++ ++ ++ ++nginx возвращал ошибку 400 на запросы вида ++"GET http://www.domain.com/uri HTTP/1.0"; ++ошибка появилась в 0.1.28. ++ ++ ++nginx returned the 400 response on requests like ++"GET http://www.domain.com/uri HTTP/1.0"; ++the bug had appeared in 0.1.28. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++при включении больших ответов с помощью SSI рабочий процесс мог зациклиться. ++ ++ ++the worker process may got caught in an endless loop if the big response ++part were include by SSI. ++ ++ ++ ++ ++ ++переменные, устанавливаемые директивой "set", не были доступны в SSI. ++ ++ ++the variables set by the "set" directive were not available in SSI. ++ ++ ++ ++ ++ ++директива autoindex_localtime. ++ ++ ++the "autoindex_localtime" directive. ++ ++ ++ ++ ++ ++пустое значение в директиве proxy_set_header запрещает передачу заголовка. ++ ++ ++the empty value of the "proxy_set_header" directive forbids the client ++request header line passing. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++nginx не собирался с параметром --without-pcre; ++ошибка появилась в 0.1.29. ++ ++ ++nginx could not be built with the --without-pcre parameter; ++the bug had appeared in 0.1.29. ++ ++ ++ ++ ++ ++3, 5, 7 и 8 директив proxy_set_header на одном уровне вызывали ++bus fault при запуске. ++ ++ ++3, 4, 7, and 8 the "proxy_set_header" directives in one level cause ++the bus fault on start up. ++ ++ ++ ++ ++ ++в редиректах внутри HTTPS сервера был указан протокол HTTP. ++ ++ ++the HTTP protocol was specified in the HTTPS redirects. ++ ++ ++ ++ ++ ++если директива rewrite использовала выделения внутри директивы if, то ++возвращалась ошибка 500. ++ ++ ++if the "rewrite" directive used the captures inside the "if" directive, then ++the 500 error code was returned. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++в редиректах, выдаваемых с помощью директивы rewrite, не передавались аргументы; ++ошибка появилась в 0.1.29. ++ ++ ++the arguments were omitted in the redirects, issued by the "rewrite" directive; ++the bug had appeared in 0.1.29. ++ ++ ++ ++ ++ ++директива if поддерживает выделения в регулярных выражениях. ++ ++ ++the "if" directive supports the captures in regular expressions. ++ ++ ++ ++ ++ ++директива set поддерживает переменные и выделения из регулярных выражений. ++ ++ ++the "set" directive supports the variables and the captures of regular ++expressions. ++ ++ ++ ++ ++ ++в режиме прокси и FastCGI поддерживается строка заголовка "X-Accel-Redirect" ++в ответе бэкенда. ++ ++ ++the "X-Accel-Redirect" response header line is supported in proxy and FastCGI ++mode. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++при использовании SSL ответ мог передаваться не до конца. ++ ++ ++the response encrypted by SSL may not transferred complete. ++ ++ ++ ++ ++ ++ошибки при обработке SSI в ответе, полученного от FastCGI-сервера. ++ ++ ++errors while processing FastCGI response by SSI. ++ ++ ++ ++ ++ ++ошибки при использовании SSI и сжатия. ++ ++ ++errors while using SSI and gzipping. ++ ++ ++ ++ ++ ++редирект с кодом 301 передавался без тела ответа; ++ошибка появилась в 0.1.30. ++ ++ ++the redirect with the 301 code was transferred without response body; ++the bug had appeared in 0.1.30. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++при использовании SSI рабочий процесс мог зациклиться. ++ ++ ++the worker process may got caught in an endless loop if the SSI was used. ++ ++ ++ ++ ++ ++при использовании SSL ответ мог передаваться не до конца. ++ ++ ++the response encrypted by SSL may not transferred complete. ++ ++ ++ ++ ++ ++если длина части ответа, полученного за один раз от проксируемого или ++FastCGI сервера была равна 500 байт, то nginx возвращал код ответа 500; ++в режиме прокси ошибка появилась только в 0.1.29. ++ ++ ++if the length of the response part received at once from proxied ++or FastCGI server was equal to 500, then nginx returns the 500 response code; ++in proxy mode the bug had appeared in 0.1.29 only. ++ ++ ++ ++ ++ ++nginx не считал неверными директивы с 8-ю или 9-ю параметрами. ++ ++ ++nginx did not consider the directives with 8 or 9 parameters as invalid. ++ ++ ++ ++ ++ ++директива return может возвращать код ответа 204. ++ ++ ++the "return" directive can return the 204 response code. ++ ++ ++ ++ ++ ++директива ignore_invalid_headers. ++ ++ ++the "ignore_invalid_headers" directive. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++модуль ngx_http_ssi_module поддерживает команду include virtual. ++ ++ ++the ngx_http_ssi_module supports "include virtual" command. ++ ++ ++ ++ ++ ++модуль ngx_http_ssi_module поддерживает условную команду вида ++'if expr="$NAME"' и команды else и endif. ++Допускается только один уровень вложенности. ++ ++ ++the ngx_http_ssi_module supports the condition command like ++'if expr="$NAME"' and "else" and "endif" commands. ++Only one nested level is supported. ++ ++ ++ ++ ++ ++модуль ngx_http_ssi_module поддерживает две переменные DATE_LOCAL и DATE_GMT ++и команду config timefmt. ++ ++ ++the ngx_http_ssi_module supports the DATE_LOCAL and DATE_GMT variables ++and "config timefmt" command. ++ ++ ++ ++ ++ ++директива ssi_ignore_recycled_buffers. ++ ++ ++the "ssi_ignore_recycled_buffers" directive. ++ ++ ++ ++ ++ ++если переменная QUERY_STRING не была определена, то в команде echo ++не ставилось значение по умолчанию. ++ ++ ++the "echo" command did not show the default value for the empty QUERY_STRING ++variable. ++ ++ ++ ++ ++ ++модуль ngx_http_proxy_module полностью переписан. ++ ++ ++the ngx_http_proxy_module was rewritten. ++ ++ ++ ++ ++ ++директивы proxy_redirect, proxy_pass_request_headers, ++proxy_pass_request_body и proxy_method. ++ ++ ++the "proxy_redirect", "proxy_pass_request_headers", ++"proxy_pass_request_body", and "proxy_method" directives. ++ ++ ++ ++ ++ ++директива proxy_set_header. ++Директива proxy_x_var упразднена и должна быть заменена директивой ++proxy_set_header. ++ ++ ++the "proxy_set_header" directive. ++The "proxy_x_var" was canceled and must be replaced with the proxy_set_header ++directive. ++ ++ ++ ++ ++ ++директива proxy_preserve_host упразднена и должна быть заменена директивами ++"proxy_set_header Host $host" и "proxy_redirect off" ++или директивой "proxy_set_header Host $host:$proxy_port" ++и соответствующими ей директивами proxy_redirect. ++ ++ ++the "proxy_preserve_host" is canceled and must be replaced with ++the "proxy_set_header Host $host" and the "proxy_redirect off" directives, ++the "proxy_set_header Host $host:$proxy_port" directive ++and the appropriate proxy_redirect directives. ++ ++ ++ ++ ++ ++директива proxy_set_x_real_ip упразднена и должна быть заменена директивой ++"proxy_set_header X-Real-IP $remote_addr". ++ ++ ++the "proxy_set_x_real_ip" is canceled and must be replaced with ++the "proxy_set_header X-Real-IP $remote_addr" directive. ++ ++ ++ ++ ++ ++директива proxy_add_x_forwarded_for упразднена и должна быть заменена ++директивой ++"proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for". ++ ++ ++the "proxy_add_x_forwarded_for" is canceled and must be replaced with ++the "proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for" ++directive. ++ ++ ++ ++ ++ ++директива proxy_set_x_url упразднена и должна быть заменена директивой ++"proxy_set_header X-URL http://$host:$server_port$request_uri". ++ ++ ++the "proxy_set_x_url" is canceled and must be replaced with ++the "proxy_set_header X-URL http://$host:$server_port$request_uri" ++directive. ++ ++ ++ ++ ++ ++директива fastcgi_param. ++ ++ ++the "fastcgi_param" directive. ++ ++ ++ ++ ++ ++директивы fastcgi_root, fastcgi_set_var и fastcgi_params упразднены ++и должны быть замены директивами fastcgi_param. ++ ++ ++the "fastcgi_root", "fastcgi_set_var" and "fastcgi_params" directive ++are canceled and must be replaced with the fastcgi_param directives. ++ ++ ++ ++ ++ ++директива index может использовать переменные. ++ ++ ++the "index" directive can use the variables. ++ ++ ++ ++ ++ ++директива index может быть указана на уровне http и server. ++ ++ ++the "index" directive can be used at http and server levels. ++ ++ ++ ++ ++ ++только последний параметр в директиве index может быть абсолютным. ++ ++ ++the last index only in the "index" directive can be absolute. ++ ++ ++ ++ ++ ++в директиве rewrite могут использоваться переменные. ++ ++ ++the "rewrite" directive can use the variables. ++ ++ ++ ++ ++ ++директива internal. ++ ++ ++the "internal" directive. ++ ++ ++ ++ ++ ++переменные CONTENT_LENGTH, CONTENT_TYPE, REMOTE_PORT, SERVER_ADDR, ++SERVER_PORT, SERVER_PROTOCOL, DOCUMENT_ROOT, SERVER_NAME, ++REQUEST_METHOD, REQUEST_URI и REMOTE_USER. ++ ++ ++the CONTENT_LENGTH, CONTENT_TYPE, REMOTE_PORT, SERVER_ADDR, ++SERVER_PORT, SERVER_PROTOCOL, DOCUMENT_ROOT, SERVER_NAME, ++REQUEST_METHOD, REQUEST_URI, and REMOTE_USER variables. ++ ++ ++ ++ ++ ++nginx теперь передаёт неверные строки в заголовках запроса клиента и ++ответа бэкенда. ++ ++ ++nginx now passes the invalid lines in a client request headers ++or a backend response header. ++ ++ ++ ++ ++ ++если бэкенд долго не передавал ответ и send_timeout был меньше, чем ++proxy_read_timeout, то клиенту возвращался ответ 408. ++ ++ ++if the backend did not transfer response for a long time and ++the "send_timeout" was less than "proxy_read_timeout", then nginx ++returned the 408 response. ++ ++ ++ ++ ++ ++если бэкенд передавал неверную строку в заголовке ответа, то происходил ++segmentation fault; ++ошибка появилась в 0.1.26. ++ ++ ++the segmentation fault was occurred if the backend sent an invalid line ++in response header; ++the bug had appeared in 0.1.26. ++ ++ ++ ++ ++ ++при использовании отказоустойчивой конфигурации в FastCGI мог ++происходить segmentation fault. ++ ++ ++the segmentation fault may occurred in FastCGI fault tolerance configuration. ++ ++ ++ ++ ++ ++директива expires не удаляла уже установленные строки заголовка ++"Expires" и "Cache-Control". ++ ++ ++the "expires" directive did not remove the previous "Expires" and ++"Cache-Control" headers. ++ ++ ++ ++ ++ ++nginx не учитывал завершающую точку в строке заголовка запроса "Host". ++ ++ ++nginx did not take into account trailing dot in "Host" header line. ++ ++ ++ ++ ++ ++модуль ngx_http_auth_module не работал на Linux. ++ ++ ++the ngx_http_auth_module did not work under Linux. ++ ++ ++ ++ ++ ++директива rewrite неверно работала, если в запросе присутствовали аргументы. ++ ++ ++the rewrite directive worked incorrectly, if the arguments were in a request. ++ ++ ++ ++ ++ ++nginx не собирался на MacOS X. ++ ++ ++nginx could not be built on MacOS X. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++при проксировании больших файлов nginx сильно нагружал процессор. ++ ++ ++nginx hogs CPU while proxying the huge files. ++ ++ ++ ++ ++ ++nginx не собирался gcc 4.0 на Linux. ++ ++ ++nginx could not be built by gcc 4.0 on Linux. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++параметр blocked в директиве valid_referers. ++ ++ ++the "blocked" parameter of the "valid_referers" directive. ++ ++ ++ ++ ++ ++ошибки обработки заголовка запроса теперь записываются на уровне ++info, в лог также записывается имя сервера и строки заголовка ++запроса "Host" и "Referer". ++ ++ ++the errors while handling the request header now logged at "info" level. ++The server name and the "Host" and "Referer" header lines also logged. ++ ++ ++ ++ ++ ++при записи ошибок в лог записывается также строка заголовка запроса "Host". ++ ++ ++the "Host" header line is also logged in error log. ++ ++ ++ ++ ++ ++директива proxy_pass_unparsed_uri. ++Специальная обработка символов "://" в URI, введённая в версии 0.1.11, ++теперь упразднена. ++ ++ ++the proxy_pass_unparsed_uri directive. ++The special handling of the "://" symbols in URI, appeared in 0.1.11 version, ++now is canceled. ++ ++ ++ ++ ++ ++nginx не собирался на FreeBSD и Linux, если был указан параметр конфигурации ++--without-ngx_http_auth_basic_module. ++ ++ ++nginx could not be built on FreeBSD and Linux, if the ++--without-ngx_http_auth_basic_module configuration parameter was used. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++неверные строки заголовка, переданные клиентом, теперь игнорируется и ++записываются в error_log на уровне info. ++ ++ ++the invalid client header lines are now ignored and logged at the info level. ++ ++ ++ ++ ++ ++при записи ошибок в лог записывается также имя сервера, при обращении ++к которому произошла ошибка. ++ ++ ++the server name is also logged in error log. ++ ++ ++ ++ ++ ++модуль ngx_http_auth_basic_module и директивы auth_basic и ++auth_basic_user_file. ++ ++ ++the ngx_http_auth_basic_module module and the auth_basic and ++auth_basic_user_file directives. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++nginx не работал на Linux parisc. ++ ++ ++nginx did run on Linux parisc. ++ ++ ++ ++ ++ ++nginx теперь не запускается под FreeBSD, если значение ++sysctl kern.ipc.somaxconn слишком большое. ++ ++ ++nginx now does not start under FreeBSD if the sysctl kern.ipc.somaxconn ++value is too big. ++ ++ ++ ++ ++ ++если модуль ngx_http_index_module делал внутреннее перенаправление запроса ++в модули ngx_http_proxy_module или ngx_http_fastcgi_module, то файл индекса ++не закрывался после обслуживания запроса. ++ ++ ++if a request was internally redirected by the ngx_http_index_module ++module to the ngx_http_proxy_module or ngx_http_fastcgi_module modules, ++then the index file was not closed after request completion. ++ ++ ++ ++ ++ ++директива proxy_pass может использоваться в location, заданных регулярным ++выражением. ++ ++ ++the "proxy_pass" can be used in location with regular expression. ++ ++ ++ ++ ++ ++модуль ngx_http_rewrite_filter_module поддерживает условия вида ++"if ($HTTP_USER_AGENT ~ MSIE)". ++ ++ ++the ngx_http_rewrite_filter_module module supports the condition like ++"if ($HTTP_USER_AGENT ~ MSIE)". ++ ++ ++ ++ ++ ++nginx очень медленно запускался при большом количестве адресов и ++использовании текстовых значений в директиве geo. ++ ++ ++nginx started too slow if the large number of addresses and text values ++were used in the "geo" directive. ++ ++ ++ ++ ++ ++имя переменной в директиве geo нужно указывать, как $name. ++Прежний вариант без "$" пока работает, но вскоре будет убран. ++ ++ ++a variable name must be declared as "$name" in the "geo" directive. ++The previous variant without "$" is still supported, but will be removed soon. ++ ++ ++ ++ ++ ++параметр лога "%{VARIABLE}v". ++ ++ ++the "%{VARIABLE}v" logging parameter. ++ ++ ++ ++ ++ ++директива "set $name value". ++ ++ ++the "set $name value" directive. ++ ++ ++ ++ ++ ++совместимость с gcc 4.0. ++ ++ ++gcc 4.0 compatibility. ++ ++ ++ ++ ++ ++параметр автоконфигурации --with-openssl-opt=OPTIONS. ++ ++ ++the --with-openssl-opt=OPTIONS autoconfiguration directive. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++модуль ngx_http_ssi_filter_module поддерживает переменные ++QUERY_STRING и DOCUMENT_URI. ++ ++ ++the ngx_http_ssi_filter_module supports the QUERY_STRING and DOCUMENT_URI ++variables. ++ ++ ++ ++ ++ ++модуль ngx_http_autoindex_module мог выдавать ответ 404 ++на существующий каталог, если этот каталог был указан как alias. ++ ++ ++the ngx_http_autoindex_module may some times return the 404 response ++for existent directory, if this directory was used in "alias" directive. ++ ++ ++ ++ ++ ++модуль ngx_http_ssi_filter_module неправильно работал при больших ++ответах. ++ ++ ++the ngx_http_ssi_filter_module ran incorrectly for large responses. ++ ++ ++ ++ ++ ++отсутствие строки заголовка "Referer" всегда считалось правильным referrer'ом. ++ ++ ++the lack of the "Referer" header line was always accounted as valid referrer. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++модуль ngx_http_ssi_filter_module и ++директивы ssi, ssi_silent_errors и ssi_min_file_chunk. ++Поддерживаются команды 'echo var="HTTP_..." default=""' и ++'echo var="REMOTE_ADDR"'. ++ ++ ++the ngx_http_ssi_filter_module and ++the ssi, ssi_silent_errors, and ssi_min_file_chunk directives. ++The 'echo var="HTTP_..." default=""' and 'echo var="REMOTE_ADDR"' commands ++are supported. ++ ++ ++ ++ ++ ++параметр лога %request_time. ++ ++ ++the %request_time log parameter. ++ ++ ++ ++ ++ ++если запрос пришёл без строки заголовка "Host", то директива ++proxy_preserve_host устанавливает в качестве этого заголовка первое имя ++сервера из директивы server_name. ++ ++ ++if the request has no the "Host" header line, then the "proxy_preserve_host" ++directive set this header line to the first server name of the "server_name" ++directive. ++ ++ ++ ++ ++ ++nginx не собирался на платформах, отличных от i386, amd64, sparc и ppc; ++ошибка появилась в 0.1.22. ++ ++ ++nginx could not be built on platforms different from i386, amd64, sparc, ++and ppc; ++the bug had appeared in 0.1.22. ++ ++ ++ ++ ++ ++модуль ngx_http_autoindex_module теперь показывает информацию не о ++символическом линке, а о файле или каталоге, на который он указывает. ++ ++ ++the ngx_http_autoindex_module now shows the information not about the symlink, ++but about file or directory it points to. ++ ++ ++ ++ ++ ++если клиенту ничего не передавалось, то параметр %apache_length ++записывал в лог отрицательную длину заголовка ответа. ++ ++ ++the %apache_length parameter logged the negative length ++of the response header if the no response was transferred to a client. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++модуль ngx_http_stub_status_module показывал неверную статистику ++для обработанных соединений, если использовалось проксирование ++или FastCGI-сервер. ++ ++ ++the ngx_http_stub_status_module showed incorrect handled connections ++statistics if the proxying or FastCGI server were used. ++ ++ ++ ++ ++ ++на Linux и Solaris установочные пути были неверно заключены в кавычки; ++ошибка появилась в 0.1.21. ++ ++ ++the installation paths were incorrectly quoted on Linux and Solaris; ++the bug had appeared in 0.1.21. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++модуль ngx_http_stub_status_module показывал неверную статистику ++при использовании метода rtsig или при использовании нескольких ++рабочих процессов на SMP машине. ++ ++ ++the ngx_http_stub_status_module showed incorrect statistics ++if "rtsig" method was used or if several worker process ran on SMP. ++ ++ ++ ++ ++ ++nginx не собирался компилятором icc под Линуксом или ++если библиотека zlib-1.2.x собиралась из исходных текстов. ++ ++ ++nginx could not be built by the icc compiler on Linux or ++if the zlib-1.2.x library was building from sources. ++ ++ ++ ++ ++ ++nginx не собирался под NetBSD 2.0. ++ ++ ++nginx could not be built on NetBSD 2.0. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++новые параметры script_filename и remote_port в директиве fastcgi_params. ++ ++ ++the new "script_filename" and "remote_port" parameters ++of the fastcgi_params directive. ++ ++ ++ ++ ++ ++неправильно обрабатывался поток stderr от FastCGI-сервера. ++ ++ ++the FastCGI stderr stream was handled incorrectly. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++если в запросе есть нуль, то для локальных запросов теперь возвращается ++ошибка 404. ++ ++ ++now, if request contains the zero, then the 404 error is returned ++for the local requests. ++ ++ ++ ++ ++ ++nginx не собирался под NetBSD 2.0. ++ ++ ++nginx could not be built on NetBSD 2.0. ++ ++ ++ ++ ++ ++во время чтения тела запроса клиента в SSL соединении мог произойти таймаут. ++ ++ ++the timeout may occur while reading of the client request body ++via SSL connections. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++для совместимости с Solaris 10 в директивах devpoll_events и devpoll_changes ++значения по умолчанию уменьшены с 512 до 32. ++ ++ ++the default values of the devpoll_events and the devpoll_changes directives ++changed from 512 to 32 to be compatible with Solaris 10. ++ ++ ++ ++ ++ ++директивы proxy_set_x_var и fastcgi_set_var не наследовались. ++ ++ ++the proxy_set_x_var and fastcgi_set_var directives were not inherited. ++ ++ ++ ++ ++ ++в директиве rewrite, возвращающей редирект, аргументы присоединялись ++к URI через символ "&" вместо "?". ++ ++ ++in a redirect rewrite directive arguments were concatenated with URI ++by an "&" rather than a "?". ++ ++ ++ ++ ++ ++строки для модуля ngx_http_geo_module без символа ";" во включённом файле ++игнорировались. ++ ++ ++the lines without trailing ";" in the file being included ++by the ngx_http_geo_module were silently ignored. ++ ++ ++ ++ ++ ++модуль ngx_http_stub_status_module. ++ ++ ++the ngx_http_stub_status_module. ++ ++ ++ ++ ++ ++неизвестный формат лог-файла в директиве access_log вызывал segmentation fault. ++ ++ ++the unknown log format in the access_log directive caused ++the segmentation fault. ++ ++ ++ ++ ++ ++новый параметр document_root в директиве fastcgi_params. ++ ++ ++the new "document_root" parameter of the fastcgi_params directive. ++ ++ ++ ++ ++ ++директива fastcgi_redirect_errors. ++ ++ ++the fastcgi_redirect_errors directive. ++ ++ ++ ++ ++ ++новый модификатор break в директиве rewrite позволяет прекратить ++цикл rewrite/location и устанавливает текущую конфигурацию для запроса. ++ ++ ++the new "break" modifier of the "rewrite" directive allows to stop ++the rewrite/location cycle and sets the current configuration to the request. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++модуль ngx_http_rewrite_module полностью переписан. ++Теперь можно делать редиректы, возвращать коды ошибок ++и проверять переменные и рефереры. ++Эти директивы можно использовать внутри location. ++Директива redirect упразднена. ++ ++ ++the ngx_http_rewrite_module was rewritten from the scratch. ++Now it is possible to redirect, to return the error codes, ++to check the variables and referrers. The directives can be used ++inside locations. ++The redirect directive was canceled. ++ ++ ++ ++ ++ ++модуль ngx_http_geo_module. ++ ++ ++the ngx_http_geo_module. ++ ++ ++ ++ ++ ++директивы proxy_set_x_var и fastcgi_set_var. ++ ++ ++the proxy_set_x_var and fastcgi_set_var directives. ++ ++ ++ ++ ++ ++конфигурация location с модификатором "=" могла использоваться ++в другом location. ++ ++ ++the location configuration with "=" modifier may be used in another ++location. ++ ++ ++ ++ ++ ++правильный тип ответа выставлялся только для запросов, у которых в расширении ++были только маленькие буквы. ++ ++ ++the correct content type was set only for requests that use small caps letters ++in extension. ++ ++ ++ ++ ++ ++если для location установлен proxy_pass или fastcgi_pass, и доступ ++к нему запрещался, а ошибка перенаправлялась на статическую страницу, ++то происходил segmentation fault. ++ ++ ++if the proxy_pass or fastcgi_pass directives were set in the location, ++and access was denied, and the error was redirected to a static page, ++then the segmentation fault occurred. ++ ++ ++ ++ ++ ++если в проксированном ответе в заголовке "Location" передавался ++относительный URL, то к нему добавлялось имя хоста и слэш; ++ошибка появилась в 0.1.14. ++ ++ ++if in a proxied "Location" header was a relative URL, ++then a host name and a slash were added to them; ++the bug had appeared in 0.1.14. ++ ++ ++ ++ ++ ++на Linux в лог не записывался текст системной ошибки. ++ ++ ++the system error message was not logged on Linux. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++если ответ передавался chunk'ами, то при запросе HEAD выдавался ++завершающий chunk. ++ ++ ++if the response were transferred by chunks, then on the HEAD request ++the final chunk was issued. ++ ++ ++ ++ ++ ++заголовок "Connection: keep-alive" выдавался, даже если директива ++keepalive_timeout запрещала использование keep-alive. ++ ++ ++the "Connection: keep-alive" header were issued, even if the ++keepalive_timeout directive forbade the keep-alive use. ++ ++ ++ ++ ++ ++ошибки в модуле ngx_http_fastcgi_module вызывали segmentation fault. ++ ++ ++the errors in the ngx_http_fastcgi_module caused the segmentation faults. ++ ++ ++ ++ ++ ++при использовании SSL сжатый ответ мог передаваться не до конца. ++ ++ ++the compressed response encrypted by SSL may not transferred complete. ++ ++ ++ ++ ++ ++опции TCP_NODELAY, TCP_NOPUSH и TCP_CORK, специфичные для TCP сокетов, ++не используются для unix domain сокетов. ++ ++ ++the TCP-specific TCP_NODELAY, TCP_NOPUSH, and TCP_CORK options, ++are not used for the unix domain sockets. ++ ++ ++ ++ ++ ++директива rewrite поддерживает перезаписывание аргументов. ++ ++ ++the rewrite directive supports the arguments rewriting. ++ ++ ++ ++ ++ ++на запрос POST с заголовком "Content-Length: 0" возвращался ответ 400; ++ошибка появилась в 0.1.14. ++ ++ ++the response code 400 was returned for the POST request with the ++"Content-Length: 0" header; ++the bug had appeared in 0.1.14. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ошибка соединения с FastCGI-сервером вызывала segmentation fault. ++ ++ ++the error while the connecting to the FastCGI server caused ++segmentation fault. ++ ++ ++ ++ ++ ++корректная обработка регулярного выражения, в котором число ++выделенных частей не совпадает с числом подстановок. ++ ++ ++the correct handling of the regular expression, that ++has different number of the captures and substitutions. ++ ++ ++ ++ ++ ++location, который передаётся FastCGI-серверу, может быть задан ++с помощью регулярного выражения. ++ ++ ++the location, that is passed to the FastCGI server, can be ++regular expression. ++ ++ ++ ++ ++ ++параметр FastCGI REQUEST_URI теперь передаётся вместе с аргументами ++и в том виде, в котором был получен от клиента. ++ ++ ++the FastCGI's parameter REQUEST_URI is now passed with the arguments ++and in the original state. ++ ++ ++ ++ ++ ++для использования регулярных выражений в location нужно было ++собирать nginx вместе с ngx_http_rewrite_module. ++ ++ ++the ngx_http_rewrite_module module was required to be built to use ++the regular expressions in locations. ++ ++ ++ ++ ++ ++если бэкенд слушал на 80-ом порту, то при использовании директивы ++"proxy_preserve_host on" в заголовке "Host" указывался ++также порт 80; ++ошибка появилась в 0.1.14. ++ ++ ++the directive "proxy_preserve_host on" adds port 80 ++to the "Host" headers, if upstream listen on port 80; ++the bug had appeared in 0.1.14. ++ ++ ++ ++ ++ ++если задать одинаковые пути в параметрах автоконфигурации ++--http-client-body-temp-path=PATH и --http-proxy-temp-path=PATH ++или --http-client-body-temp-path=PATH и --http-fastcgi-temp-path=PATH, ++то происходил segmentation fault. ++ ++ ++the same paths in autoconfiguration parameters ++--http-client-body-temp-path=PATH and --http-proxy-temp-path=PATH, ++or --http-client-body-temp-path=PATH and --http-fastcgi-temp-path=PATH ++caused segmentation fault. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++параметры автоконфигурации ++--http-client-body-temp-path=PATH, ++--http-proxy-temp-path=PATH ++и --http-fastcgi-temp-path=PATH ++ ++ ++the autoconfiguration directives: ++--http-client-body-temp-path=PATH, ++--http-proxy-temp-path=PATH, ++and --http-fastcgi-temp-path=PATH ++ ++ ++ ++ ++ ++имя каталога с временными файлами, содержащие тело запроса клиента, ++задаётся директивой client_body_temp_path, ++по умолчанию <prefix>/client_body_temp. ++ ++ ++the directory name for the temporary files with the client request body ++is specified by directive client_body_temp_path, ++by default it is <prefix>/client_body_temp. ++ ++ ++ ++ ++ ++модуль ngx_http_fastcgi_module и директивы ++fastcgi_pass, ++fastcgi_root, ++fastcgi_index, ++fastcgi_params, ++fastcgi_connect_timeout, ++fastcgi_send_timeout, ++fastcgi_read_timeout, ++fastcgi_send_lowat, ++fastcgi_header_buffer_size, ++fastcgi_buffers, ++fastcgi_busy_buffers_size, ++fastcgi_temp_path, ++fastcgi_max_temp_file_size, ++fastcgi_temp_file_write_size, ++fastcgi_next_upstream ++и fastcgi_x_powered_by. ++ ++ ++ ++the ngx_http_fastcgi_module and the directives: ++fastcgi_pass, ++fastcgi_root, ++fastcgi_index, ++fastcgi_params, ++fastcgi_connect_timeout, ++fastcgi_send_timeout, ++fastcgi_read_timeout, ++fastcgi_send_lowat, ++fastcgi_header_buffer_size, ++fastcgi_buffers, ++fastcgi_busy_buffers_size, ++fastcgi_temp_path, ++fastcgi_max_temp_file_size, ++fastcgi_temp_file_write_size, ++fastcgi_next_upstream, ++and fastcgi_x_powered_by. ++ ++ ++ ++ ++ ++ошибка "[alert] zero size buf"; ++ошибка появилась в 0.1.3. ++ ++ ++the "[alert] zero size buf" error; ++the bug had appeared in 0.1.3. ++ ++ ++ ++ ++ ++в директиве proxy_pass нужно обязательно указывать URI после имени хоста. ++ ++ ++the URI must be specified after the host name in the proxy_pass directive. ++ ++ ++ ++ ++ ++если в URI встречался символ %3F, то он считался началом строки аргументов. ++ ++ ++the %3F symbol in the URI was considered as the argument string start. ++ ++ ++ ++ ++ ++поддержка unix domain сокетов в модуле ngx_http_proxy_module. ++ ++ ++the unix domain sockets support in the ngx_http_proxy_module. ++ ++ ++ ++ ++ ++директивы ssl_engine и ssl_ciphers.
++Спасибо Сергею Скворцову за SSL-акселератор. ++
++ ++the ssl_engine and ssl_ciphers directives.
++Thanks to Sergey Skvortsov for SSL-accelerator. ++
++
++ ++
++ ++ ++ ++ ++ ++ ++директивы server_names_hash и server_names_hash_threshold. ++ ++ ++the server_names_hash and server_names_hash_threshold directives. ++ ++ ++ ++ ++ ++имена *.domain.tld в директиве server_name не работали. ++ ++ ++the *.domain.tld names in the "server_name" directive did not work. ++ ++ ++ ++ ++ ++параметр лога %request_length записывал неверную длину. ++ ++ ++the %request_length log parameter logged the incorrect length. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++параметр лога %request_length. ++ ++ ++the %request_length log parameter. ++ ++ ++ ++ ++ ++при использовании /dev/poll, select и poll на платформах, где возможны ++ложные срабатывания указанных методов, могли быть длительные задержки ++при обработке запроса по keep-alive соединению. ++Наблюдалось по крайней мере на Solaris с использованием /dev/poll. ++ ++ ++when using the /dev/poll, select and poll on the platforms, where ++these methods may do the false reports, there may be the long delay when ++the request was passed via the keep-alive connection. ++It may be at least on Solaris when using the /dev/poll. ++ ++ ++ ++ ++ ++директива send_lowat игнорируется на Linux, так как Linux не поддерживает ++опцию SO_SNDLOWAT. ++ ++ ++the send_lowat directive is ignored on Linux because Linux does not support ++the SO_SNDLOWAT option. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директива worker_priority. ++ ++ ++the worker_priority directive. ++ ++ ++ ++ ++ ++под FreeBSD директивы tcp_nopush и tcp_nodelay вместе влияют на передачу ++ответа. ++ ++ ++both tcp_nopush and tcp_nodelay directives affect the transferred response. ++ ++ ++ ++ ++ ++nginx не вызывал initgroups().
++Спасибо Андрею Ситникову и Андрею Нигматулину. ++
++ ++nginx did not call initgroups().
++Thanks to Andrew Sitnikov and Andrei Nigmatulin. ++
++
++ ++ ++ ++ngx_http_auto_index_module теперь выдаёт размер файлов в байтах. ++ ++ ++now the ngx_http_autoindex_module shows the file size in the bytes. ++ ++ ++ ++ ++ ++ngx_http_auto_index_module возвращал ошибку 500, если в каталоге есть ++битый symlink. ++ ++ ++the ngx_http_autoindex_module returned the 500 error if the broken symlink ++was in a directory. ++ ++ ++ ++ ++ ++файлы больше 4G не передавались с использованием sendfile. ++ ++ ++the files bigger than 4G could not be transferred using sendfile. ++ ++ ++ ++ ++ ++если бэкенд резолвился в несколько адресов и при ожидании от него ответа ++происходила ошибка, то процесс зацикливался. ++ ++ ++if the backend was resolved to several backends and there was an error while ++the response waiting then process may got caught in an endless loop. ++ ++ ++ ++ ++ ++при использовании метода /dev/poll рабочий процесс мог завершиться ++с сообщением "unknown cycle". ++ ++ ++the worker process may exit with the "unknown cycle" message when the /dev/poll ++method was used. ++ ++ ++ ++ ++ ++ошибки "close() channel failed". ++ ++ ++"close() channel failed" errors. ++ ++ ++ ++ ++ ++автоматическое определение групп nobody и nogroup. ++ ++ ++the autodetection of the "nobody" and "nogroup" groups. ++ ++ ++ ++ ++ ++директива send_lowat не работала на Linux. ++ ++ ++the send_lowat directive did not work on Linux. ++ ++ ++ ++ ++ ++если в конфигурации не было раздела events, то происходил segmentation fault. ++ ++ ++the segmentation fault occurred if there was no events section ++in configuration. ++ ++ ++ ++ ++ ++nginx не собирался под OpenBSD. ++ ++ ++nginx could not be built on OpenBSD. ++ ++ ++ ++ ++ ++двойные слэшы в "://" в URI превращались в ":/". ++ ++ ++the double slashes in "://" in the URI were converted to ":/". ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++если в запросе без аргументов есть "//", "/./", "/../" или "%XX", ++то терялся последний символ в строке запроса; ++ошибка появилась в 0.1.9. ++ ++ ++if the request without arguments contains "//", "/./", "/../" or "%XX" ++then the last character in the request line was lost; ++the bug had appeared in 0.1.9. ++ ++ ++ ++ ++ ++исправление в версии 0.1.9 для файлов больше 2G на Linux не работало. ++ ++ ++the fix in 0.1.9 for the files bigger than 2G on Linux did not work. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++если в запросе есть "//", "/./", "/../" или "%XX", то проксируемый ++запрос передавался без аргументов. ++ ++ ++the proxied request was sent without arguments if the request contains ++"//", "/./", "/../" or "%XX". ++ ++ ++ ++ ++ ++при сжатии больших ответов иногда они передавались не полностью. ++ ++ ++the large compressed responses may be transferred not completely. ++ ++ ++ ++ ++ ++не передавались файлы больше 2G на Linux, неподдерживающем sendfile64(). ++ ++ ++the files bigger than 2G was not transferred on Linux that does not support ++sendfile64(). ++ ++ ++ ++ ++ ++на Linux при конфигурации сборки нужно было обязательно использовать ++параметр --with-poll_module; ++ошибка появилась в 0.1.8. ++ ++ ++while the build configuration on Linux the --with-poll_module parameter ++was required; ++the bug had appeared in 0.1.8. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ошибка в модуле ngx_http_autoindex_module при показе длинных имён файлов. ++ ++ ++in the ngx_http_autoindex_module if the long file names were in the listing. ++ ++ ++ ++ ++ ++модификатор "^~" в директиве location. ++ ++ ++the "^~" modifier in the location directive. ++ ++ ++ ++ ++ ++директива proxy_max_temp_file_size. ++ ++ ++the proxy_max_temp_file_size directive. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++при использовании sendfile, если передаваемый файл менялся, то мог ++произойти segmentation fault на FreeBSD; ++ошибка появилась в 0.1.5. ++ ++ ++on FreeBSD the segmentation fault may occur if the size of the transferred ++file was changed; ++the bug had appeared in 0.1.5. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++при некоторых комбинациях директив location c регулярными выражениями ++использовалась конфигурация не из того location. ++ ++ ++some location directive combinations with the regular expressions caused ++the wrong configuration choose. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++на Solaris и Linux могло быть очень много сообщений "recvmsg() returned ++not enough data". ++ ++ ++on Solaris and Linux there may be too many "recvmsg() returned not enough data" ++alerts. ++ ++ ++ ++ ++ ++в режиме прокси без использования sendfile на Solaris возникала ++ошибка "writev() failed (22: Invalid argument)". ++На других платформах, не поддерживающих sendfile, процесс зацикливался. ++ ++ ++there were the "writev() failed (22: Invalid argument)" errors on ++Solaris in proxy mode without sendfile. On other platforms that do not ++support sendfile at all the process got caught in an endless loop. ++ ++ ++ ++ ++ ++при использовании sendfile в режиме прокси на Solaris возникал ++segmentation fault. ++ ++ ++segmentation fault on Solaris in proxy mode and using sendfile. ++ ++ ++ ++ ++ ++segmentation fault на Solaris. ++ ++ ++segmentation fault on Solaris. ++ ++ ++ ++ ++ ++обновление исполняемого файла на лету не работало на Linux. ++ ++ ++on-line upgrade did not work on Linux. ++ ++ ++ ++ ++ ++в списке файлов, выдаваемом модулем ngx_http_autoindex_module, ++не перекодировались пробелы, кавычки и знаки процента. ++ ++ ++the ngx_http_autoindex_module module did not escape the spaces, ++the quotes, and the percent signs in the directory listing. ++ ++ ++ ++ ++ ++уменьшение операций копирования. ++ ++ ++the decrease of the copy operations. ++ ++ ++ ++ ++ ++директива userid_p3p. ++ ++ ++the userid_p3p directive. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ошибка в модуле ngx_http_autoindex_module. ++ ++ ++in the ngx_http_autoindex_module. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++модуль ngx_http_autoindex_module и директива autoindex. ++ ++ ++the ngx_http_autoindex_module and the autoindex directive. ++ ++ ++ ++ ++ ++директива proxy_set_x_url. ++ ++ ++the proxy_set_x_url directive. ++ ++ ++ ++ ++ ++модуль проксировании мог привести к зацикливанию, если не использовался ++sendfile. ++ ++ ++proxy module may get caught in an endless loop when sendfile is not used. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++параметры --user=USER, --group=GROUP и --with-ld-opt=OPTIONS в configure. ++ ++ ++the --user=USER, --group=GROUP, and --with-ld-opt=OPTIONS options in configure. ++ ++ ++ ++ ++ ++директива server_name поддерживает *.domain.tld. ++ ++ ++the server_name directive supports *.domain.tld. ++ ++ ++ ++ ++ ++улучшена переносимость на неизвестные платформы. ++ ++ ++the portability improvements. ++ ++ ++ ++ ++ ++нельзя переконфигурировать nginx, если конфигурационный файл указан ++в командной строке; ++ошибка появилась в 0.1.1. ++ ++ ++if configuration file was set in command line, the reconfiguration ++was impossible; ++the bug had appeared in 0.1.1. ++ ++ ++ ++ ++ ++модуль проксировании мог привести к зацикливанию, если не использовался ++sendfile. ++ ++ ++proxy module may get caught in an endless loop when sendfile is not used. ++ ++ ++ ++ ++ ++при использовании sendfile текст ответа не перекодировался ++согласно директивам модуля charset; ++ошибка появилась в 0.1.1. ++ ++ ++with sendfile the response was not recoded according to the charset ++module directives; ++the bug had appeared in 0.1.1. ++ ++ ++ ++ ++ ++очень редкая ошибка при обработке kqueue. ++ ++ ++very seldom bug in the kqueue processing. ++ ++ ++ ++ ++ ++модуль сжатия сжимал уже сжатые ответы, полученные при проксировании. ++ ++ ++the gzip module compressed the proxied responses that was already compressed. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++директива gzip_types. ++ ++ ++the gzip_types directive. ++ ++ ++ ++ ++ ++директива tcp_nodelay. ++ ++ ++the tcp_nodelay directive. ++ ++ ++ ++ ++ ++директива send_lowat работает не только на платформах, поддерживающих ++kqueue NOTE_LOWAT, но и на всех, поддерживающих SO_SNDLOWAT. ++ ++ ++the send_lowat directive is working not only on OSes that support ++kqueue NOTE_LOWAT, but also on OSes that support SO_SNDLOWAT. ++ ++ ++ ++ ++ ++эмуляция setproctitle() для Linux и Solaris. ++ ++ ++the setproctitle() emulation for Linux and Solaris. ++ ++ ++ ++ ++ ++ошибка при переписывании заголовка "Location" при проксировании. ++ ++ ++the "Location" header rewrite bug fixed while the proxying. ++ ++ ++ ++ ++ ++ошибка в модуле ngx_http_chunked_module, приводившая к зацикливанию. ++ ++ ++the ngx_http_chunked_module module may get caught in an endless loop. ++ ++ ++ ++ ++ ++ошибки в модуле /dev/poll. ++ ++ ++the /dev/poll module bugs fixed. ++ ++ ++ ++ ++ ++при проксировании и использовании временных файлов ответы портились. ++ ++ ++the responses were corrupted when the temporary files were used ++while the proxying. ++ ++ ++ ++ ++ ++бэкенду передавались запросы с неперекодированными символами. ++ ++ ++the unescaped requests were passed to the backend. ++ ++ ++ ++ ++ ++на Linux 2.4 при конфигурации сборки нужно было обязательно использовать ++параметр --with-poll_module. ++ ++ ++while the build configuration on Linux 2.4 the --with-poll_module parameter ++was required. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++Первая публично доступная версия. ++ ++ ++The first public version. ++ ++ ++ ++ ++ ++ ++
+diff -urN nginx-1.24.0/docs/xsls/changes.xsls nginx/docs/xsls/changes.xsls +--- nginx-1.24.0/docs/xsls/changes.xsls 1970-01-01 05:30:00.000000000 +0530 ++++ nginx/docs/xsls/changes.xsls 2023-03-04 13:25:23.478122500 +0530 +@@ -0,0 +1,134 @@ ++X:stylesheet { ++ ++X:output method="text"; ++ ++X:param lang="'en'"; ++X:param configuration="'../xml/change_log_conf.xml'"; ++ ++X:var conf = "document($configuration)/configuration"; ++X:var start = "$conf/start"; ++X:var indent = "$conf/indent"; ++X:var max = "$conf/length"; ++X:var br = {<br>} ++ ++ ++X:template = "/" { !! "change_log"; } ++X:template = "change_log" { !! "changes"; } ++ ++ ++X:template = "changes" { ++ X:text { } ++ ++ !{substring(concat($conf/changes[@lang=$lang]/title, ++ //change_log/@title, ++ ' ', @ver, ++ ' '), ++ 1, $conf/changes[@lang=$lang]/length)} ++ ++ X:if "$lang='ru'" { ++ !{substring(@date, 9, 2)} ++ X:text {.} ++ !{substring(@date, 6, 2)} ++ X:text {.} ++ !{substring(@date, 1, 4)} ++ } ++ ++ X:if "$lang='en'" { ++ !{substring(@date, 9, 2)} ++ !{$conf/changes[@lang=$lang]/month[number(substring(current()/@date, ++ 6, 2))]} ++ !{substring(@date, 1, 4)} ++ } ++ ++ X:text { } ++ ++ !! "change"; ++ ++ X:text { } ++} ++ ++ ++X:template = "change" { ++ X:var prefix = "$conf/changes[@lang=$lang]/*[local-name(.)=current()/@type]" ++ ++ X:var postfix = { X:if "$prefix" { X:text {: } } } ++ ++ !! "para[@lang=$lang]" (prefix = "concat($start, $prefix, $postfix)"); ++} ++ ++ ++X:template para(prefix) = "para" { ++ X:var text = { !!; } ++ ++ X:text { } ++ ++ !wrap(text = "normalize-space($text)", ++ prefix = { X:if "position() = 1" { !{$prefix} } else { !{$indent} } }) ++} ++ ++ ++X:template wrap(text, prefix) { ++ X:if "$text" { ++ X:var offset = { ++ X:choose { ++ X:when "starts-with($text, concat($br, ' '))" { ++ !{string-length($br) + 2} ++ } ++ X:when "starts-with($text, $br)" { ++ !{string-length($br) + 1} ++ } ++ X:otherwise { ++ 1 ++ } ++ } ++ } ++ ++ X:var length = { ++ !length(text = "substring($text, $offset)", ++ prefix = "string-length($prefix)", ++ length = "$max") ++ } ++ ++ !{$prefix} ++ ++ !{normalize-space(translate(substring($text, $offset, $length), ++ ' ', ' '))} ++ ++ X:text { } ++ ++ !wrap(text = "substring($text, $length + $offset)", prefix = "$indent") ++ } ++} ++ ++ ++X:template length(text, prefix, length) { ++ X:var break = "substring-before(substring($text, 1, ++ $length - $prefix + string-length($br)), ++ $br)" ++ ++ X:choose { ++ X:when "$break" { !{string-length($break)} } ++ ++ X:when "$length = 0" { !{$max - $prefix} } ++ ++ X:when "string-length($text) + $prefix <= $length" { ++ !{$length - $prefix} ++ } ++ ++ X:when "substring($text, $length - $prefix + 1, 1) = ' '" { ++ !{$length - $prefix + 1} ++ } ++ ++ X:otherwise { ++ !length(text = "$text", prefix = "$prefix", length = "$length - 1") ++ } ++ } ++} ++ ++ ++X:template = "at" {@} ++X:template = "br" { !{$br} } ++X:template = "nobr" { !{translate(., ' ', ' ')} } ++ ++ ++} +diff -urN nginx-1.24.0/docs/xslt/changes.xslt nginx/docs/xslt/changes.xslt +--- nginx-1.24.0/docs/xslt/changes.xslt 1970-01-01 05:30:00.000000000 +0530 ++++ nginx/docs/xslt/changes.xslt 2023-03-04 13:25:23.479171700 +0530 +@@ -0,0 +1,128 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++<br> ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ . ++ ++ . ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ : ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ 1 ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++@ ++ ++ ++ ++ ++ diff --git a/thirdparty/nginx/patches/nginx_src_core.patch b/thirdparty/nginx/patches/nginx_src_core.patch new file mode 100644 index 000000000..e056ecd41 --- /dev/null +++ b/thirdparty/nginx/patches/nginx_src_core.patch @@ -0,0 +1,4943 @@ +diff -urN nginx/src/core/ngx_log.c nginx/src/core/ngx_log.c +--- nginx/src/core/ngx_log.c 2023-07-26 17:25:13.691656100 +0530 ++++ nginx/src/core/ngx_log.c 2023-10-01 21:39:22.880121200 +0530 +@@ -86,7 +86,7 @@ + + static const char *debug_levels[] = { + "debug_core", "debug_alloc", "debug_mutex", "debug_event", +- "debug_http", "debug_mail", "debug_stream" ++ "debug_http", "debug_mail", "debug_stream", "debug_zimbra" // Zimbra customizations added debug_zimbra for logging (Jira Tickets: Part of first commit:https://github.com/Zimbra/packages/commit/2b59af7543b67542dfdb0ea21844f0caf73b0cd0) + }; + + +diff -urN nginx/src/core/ngx_log.h nginx/src/core/ngx_log.h +--- nginx/src/core/ngx_log.h 2023-07-26 17:25:13.689661500 +0530 ++++ nginx/src/core/ngx_log.h 2023-10-01 21:39:22.878112400 +0530 +@@ -30,14 +30,18 @@ + #define NGX_LOG_DEBUG_HTTP 0x100 + #define NGX_LOG_DEBUG_MAIL 0x200 + #define NGX_LOG_DEBUG_STREAM 0x400 +- ++// Zimbra customizations start here (Jira Tickets: ) ++#define NGX_LOG_DEBUG_ZIMBRA 0x800 ++// Zimbra customizations end here + /* + * do not forget to update debug_levels[] in src/core/ngx_log.c + * after the adding a new debug level + */ + + #define NGX_LOG_DEBUG_FIRST NGX_LOG_DEBUG_CORE +-#define NGX_LOG_DEBUG_LAST NGX_LOG_DEBUG_STREAM ++// Zimbra customizations start here: instead of NGX_LOG_DEBUG_STREAM as last debug logs we have mentioned ZIMBRA logs as last one (Jira Tickets:Part of first commit:https://github.com/Zimbra/packages/commit/2b59af7543b67542dfdb0ea21844f0caf73b0cd0 ) ++#define NGX_LOG_DEBUG_LAST NGX_LOG_DEBUG_ZIMBRA ++// Zimbra customizations end here + #define NGX_LOG_DEBUG_CONNECTION 0x80000000 + #define NGX_LOG_DEBUG_ALL 0x7ffffff0 + +diff -urN nginx/src/core/ngx_memcache.c nginx/src/core/ngx_memcache.c +--- nginx/src/core/ngx_memcache.c 1970-01-01 05:30:00.000000000 +0530 ++++ nginx/src/core/ngx_memcache.c 2023-03-04 13:25:59.822406800 +0530 +@@ -0,0 +1,2185 @@ ++/* ++ * ***** BEGIN LICENSE BLOCK ***** ++ * Zimbra Collaboration Suite Server ++ * Copyright (C) 2011 Zimbra Software, LLC. ++ * ++ * The contents of this file are subject to the Zimbra Public License ++ * Version 1.4 ("License"); you may not use this file except in ++ * compliance with the License. You may obtain a copy of the License at ++ * http://www.zimbra.com/license. ++ * ++ * Software distributed under the License is distributed on an "AS IS" ++ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. ++ * ***** END LICENSE BLOCK ***** ++ */ ++ ++#include ++#include ++ ++#define MC_INVALID_HASH ((ngx_uint_t) - 1) ++#define MC_REQ_POOL_SIZE 1024 ++#define SHA256_KEY_LENGTH 64 ++ ++static int mc_sndbuf_len = 256 * 1024; ++ ++ngx_str_t NGX_EMPTY_STR = ngx_string(""); ++ ++/* config-related function prototypes */ ++static char *ngx_memcache_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); ++static void *ngx_memcache_create_conf(ngx_cycle_t *cycle); ++static char *ngx_memcache_init_conf(ngx_cycle_t *cycle, void *conf); ++static ngx_int_t ngx_memcache_init_process(ngx_cycle_t *cycle); ++static char * ++ngx_memcache_servers (ngx_conf_t *cf, ngx_command_t *cmd, void *conf); ++static char * ++ngx_memcache_ttl (ngx_conf_t *cf, ngx_command_t *cmd, void *conf); ++ ++/* memcache protocol response handler prototype */ ++typedef ngx_int_t (*mcp_handler) ++ (u_char *stream, size_t size, mc_workqueue_t *wq, size_t *consumed); ++ ++/* memcache protocol response processing functions */ ++static ngx_int_t ngx_memcache_process_add_ok ++ (u_char *stream, size_t size, mc_workqueue_t *wq, size_t *consumed); ++static ngx_int_t ngx_memcache_process_add_failed ++ (u_char *stream, size_t size, mc_workqueue_t *wq, size_t *consumed); ++static ngx_int_t ngx_memcache_process_add_invalid ++ (u_char *stream, size_t size, mc_workqueue_t *wq, size_t *consumed); ++static ngx_int_t ngx_memcache_process_delete_ok ++ (u_char *stream, size_t size, mc_workqueue_t *wq, size_t *consumed); ++static ngx_int_t ngx_memcache_process_delete_failed ++ (u_char *stream, size_t size, mc_workqueue_t *wq, size_t *consumed); ++static ngx_int_t ngx_memcache_process_get_ok ++ (u_char *stream, size_t size, mc_workqueue_t *wq, size_t *consumed); ++static ngx_int_t ngx_memcache_process_get_failed ++ (u_char *stream, size_t size, mc_workqueue_t *wq, size_t *consumed); ++static ngx_int_t ngx_memcache_process_incr_ok ++ (u_char *stream, size_t size, mc_workqueue_t *wq, size_t *consumed); ++static ngx_int_t ngx_memcache_process_incr_failed ++ (u_char *stream, size_t size, mc_workqueue_t *wq, size_t *consumed); ++static ngx_int_t ngx_memcache_process_decr_ok ++ (u_char *stream, size_t size, mc_workqueue_t *wq, size_t *consumed); ++static ngx_int_t ngx_memcache_process_decr_failed ++ (u_char *stream, size_t size, mc_workqueue_t *wq, size_t *consumed); ++static ngx_int_t ngx_memcache_process_error ++ (u_char *stream, size_t size, mc_workqueue_t *wq, size_t *consumed); ++static ngx_int_t ngx_memcache_process_client_error ++ (u_char *stream, size_t size, mc_workqueue_t *wq, size_t *consumed); ++static ngx_int_t ngx_memcache_process_server_error ++ (u_char *stream, size_t size, mc_workqueue_t *wq, size_t *consumed); ++static ngx_int_t ngx_memcache_process_error_line ++ (u_char *stream, size_t size, mc_workqueue_t *wq, size_t *consumed); ++static ngx_int_t ngx_memcache_process_any_response ++ (u_char *stream, size_t size, mc_workqueue_t *wq, size_t *consumed); ++ ++/* hashing functions to elect a memcached server for caching */ ++static ngx_uint_t ngx_memcache_hash (u_char *key, size_t len); ++static ngx_uint_t ngx_memcache_perl_hash (u_char *key, size_t len); ++static ngx_str_t ngx_sha256_hash (ngx_pool_t* p, u_char *key, size_t len); ++ ++/* generic event handler to read any memcache response */ ++static void ngx_memcache_any_read_handler (ngx_event_t *rev); ++static void ngx_memcache_dummy_write_handler (ngx_event_t *wev); ++static void ngx_memcache_reconnection_handler (ngx_event_t *ev); ++static inline void ngx_memcache_callback (mc_work_t * w); ++ ++/* Workqueue and connection maintenance functions */ ++static inline mc_workqueue_t *ngx_memcache_wq_front (mc_workqueue_t *head); ++static inline ngx_int_t ngx_memcache_wq_isempty (mc_workqueue_t *head); ++static void ngx_memcache_purge_connection_workqueue (mc_context_t *mcctx, mc_response_code_t res); ++static void ngx_memcache_reestablish_connection (mc_context_t *mcctx); ++static inline void ngx_memcache_close_connection (ngx_peer_connection_t *pc); ++static inline void ngx_memcache_prepare_reconnection (mc_context_t *mcctx); ++ ++/* main post function */ ++static void ngx_memcache_do_post(mc_work_t *w, ngx_str_t key, ngx_str_t value, ++ ngx_str_t ttl, ngx_pool_t *p, ngx_log_t *l); ++ ++/* other utility */ ++static u_char * ngx_memcache_hexstr(u_char* md, int len); ++static ngx_str_t ngx_memcache_create_pdu(ngx_pool_t *pool, mc_work_t *w, ++ ngx_str_t key, ngx_str_t value, ngx_str_t ttl, ngx_log_t * log); ++ ++static ngx_command_t ngx_memcache_commands[] = ++{ ++ { ngx_string("memcache"), ++ NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS, ++ ngx_memcache_block, ++ 0, ++ 0, ++ NULL }, ++ ++ { ngx_string("servers"), ++ NGX_DIRECT_CONF|NGX_MEMCACHE_CONF|NGX_CONF_1MORE, ++ ngx_memcache_servers, ++ 0, ++ 0, ++ NULL }, ++ ++ { ngx_string("timeout"), ++ NGX_DIRECT_CONF|NGX_MEMCACHE_CONF|NGX_CONF_TAKE1, ++ ngx_conf_set_msec_slot, ++ 0, ++ offsetof(ngx_memcache_conf_t, timeout), ++ NULL }, ++ ++ { ngx_string("reconnect"), ++ NGX_DIRECT_CONF|NGX_MEMCACHE_CONF|NGX_CONF_TAKE1, ++ ngx_conf_set_msec_slot, ++ 0, ++ offsetof(ngx_memcache_conf_t, reconnect), ++ NULL }, ++ ++ { ngx_string("ttl"), ++ NGX_DIRECT_CONF|NGX_MEMCACHE_CONF|NGX_CONF_TAKE1, ++ ngx_memcache_ttl, ++ 0, ++ offsetof(ngx_memcache_conf_t, ttl), ++ NULL }, ++ ++ ngx_null_command ++}; ++ ++static ngx_core_module_t ngx_memcache_module_ctx = ++{ ++ ngx_string("memcache"), ++ ngx_memcache_create_conf, ++ ngx_memcache_init_conf ++}; ++ ++ngx_module_t ngx_memcache_module = ++{ ++ NGX_MODULE_V1, ++ &ngx_memcache_module_ctx, /* module context */ ++ ngx_memcache_commands, /* module directives */ ++ NGX_CORE_MODULE, /* module type */ ++ NULL, /* init master */ ++ NULL, /* init module */ ++ ngx_memcache_init_process, /* init process */ ++ NULL, /* init thread */ ++ NULL, /* exit thread */ ++ NULL, /* exit process */ ++ NULL, /* exit master */ ++ NGX_MODULE_V1_PADDING ++}; ++ ++static void *ngx_memcache_create_conf(ngx_cycle_t *cycle) ++{ ++ ngx_memcache_conf_t *mcf; ++ ngx_pool_t *pool; ++ ngx_log_t *log; ++ ++ log = cycle->log; ++ pool = ngx_create_pool (8 * ngx_pagesize, cycle->log); ++ ++ mcf = ngx_pcalloc (pool, sizeof(ngx_memcache_conf_t)); ++ if (mcf == NULL) { ++ return NGX_CONF_ERROR; ++ } ++ ++ mcf->cpool = pool; ++ mcf->log = log; ++ ++ if(ngx_array_init (&mcf->servers, mcf->cpool, 4, sizeof(ngx_addr_t*)) ++ != NGX_OK) { ++ return NGX_CONF_ERROR; ++ } ++ ++ mcf->timeout = NGX_CONF_UNSET_MSEC; ++ mcf->reconnect = NGX_CONF_UNSET_MSEC; ++ mcf->ttl = NGX_CONF_UNSET_MSEC; ++ mcf->ttl_text.len = 0; ++ mcf->allow_unqualified = NGX_CONF_UNSET; ++ ++ ngx_log_error(NGX_LOG_DEBUG_CORE, cycle->log, 0, ++ "memcache - created configuration:%p", mcf); ++ return mcf; ++} ++ ++static char *ngx_memcache_init_conf(ngx_cycle_t *cycle, void *conf) ++{ ++ ngx_memcache_conf_t *mcf = conf; ++ ++ ngx_conf_init_msec_value(mcf->timeout, 5000); ++ ngx_conf_init_msec_value(mcf->reconnect, 60000); ++ ngx_conf_init_msec_value(mcf->ttl, 0); ++ if (mcf->ttl_text.len == 0) { ++ ngx_str_set(&mcf->ttl_text, "0"); ++ } ++ ngx_conf_init_value(mcf->allow_unqualified, 0); ++ ++ ngx_log_error(NGX_LOG_DEBUG_CORE,cycle->log, 0, ++ "memcache - initialized config defaults:%p", mcf); ++ return NGX_CONF_OK; ++} ++ ++static char* ngx_memcache_ttl(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) ++{ ++ char *res; ++ ngx_str_t *value, *ttl; ++ ngx_memcache_conf_t *mcf = (ngx_memcache_conf_t *)conf; ++ ++ res = ngx_conf_set_msec_slot(cf, cmd, conf); ++ ++ if (res != NGX_CONF_OK) { ++ return res; ++ } ++ ++ value = cf->args->elts; ++ ttl = &value[1]; ++ ++ /* trim the last 5 digit 'NNNms' and become the number of seconds ++ * for example, 3600000ms --> 3600 */ ++ if (mcf->ttl > 1000) { ++ mcf->ttl_text.data = ttl->data; ++ mcf->ttl_text.len = ttl->len - 5; ++ } else if (mcf->ttl > 0) { ++ /* 0 ~ 1000ms, make it 1 */ ++ ngx_str_set(&mcf->ttl_text, "1"); ++ } else { ++ /* ttl is 0, so keep the default ttl_text "0" */ ++ } ++ ++ return NGX_CONF_OK; ++} ++/* per-process initialization routine */ ++static ngx_int_t ngx_memcache_init_process(ngx_cycle_t *cycle) ++{ ++ ngx_memcache_conf_t *mcf; ++ ngx_log_t *log; ++ ngx_pool_t *pool; ++ ngx_addr_t *peer, ++ **peers; ++ ngx_peer_connection_t *peercxn; ++ mc_context_t *mcctx; ++ mc_workqueue_t *mcwq; ++ ngx_int_t rc; ++ ngx_uint_t npeers,i; ++ ngx_buf_t *buff; ++ ++ mcf = (ngx_memcache_conf_t*) ngx_get_conf(cycle->conf_ctx, ngx_memcache_module); ++ log = cycle->log; ++ // pool = ngx_create_pool(8*ngx_pagesize,log); ++ pool = mcf->cpool; ++ ++ npeers = mcf->servers.nelts; ++ peers = (ngx_addr_t **)mcf->servers.elts; ++ ++ rc = ngx_array_init(&mcf->contexts, pool, npeers > 0 ? npeers : 1, sizeof(mc_context_t)); ++ if (rc != NGX_OK) { ++ return rc; ++ } ++ ++ for ( i = 0; i < npeers; ++i) ++ { ++ peer = peers[i]; ++ peercxn = ngx_pcalloc(pool, sizeof(ngx_peer_connection_t)); ++ peercxn->sockaddr = peer->sockaddr; /* XXX peer->sockaddr is on cf->pool */ ++ peercxn->socklen = peer->socklen; ++ peercxn->name = &peer->name; /* XXX peer->name is on cf->pool */ ++ peercxn->get = ngx_event_get_peer; ++ peercxn->log = log; ++ peercxn->log_error = NGX_ERROR_ERR; ++ ++ rc = ngx_event_connect_peer(peercxn); ++ ++ if (rc == NGX_ERROR || rc == NGX_BUSY || rc == NGX_DECLINED) { ++ ngx_log_error (NGX_LOG_ERR, log, 0, ++ "cannot connect to memcached server %V (rc:%d)", ++ &peer->name, rc); ++ ++ if (peercxn->connection) { ++ ngx_close_connection (peercxn->connection); ++ } ++ continue; ++ } ++ ++ /* nginx sockets are non-blocking, so connect() returns EINPROGRESS */ ++ peercxn->connection->read->handler = ngx_memcache_any_read_handler; ++ peercxn->connection->write->handler = ngx_memcache_dummy_write_handler; ++ ++ mcctx = ngx_array_push (&mcf->contexts); ++ ngx_memzero (mcctx, sizeof (mc_context_t)); ++ buff = ngx_create_temp_buf (pool, ngx_pagesize); /* circular buffer */ ++ mcctx->readbuffer = buff; ++ ++ /* reconnection event */ ++ mcctx->reconnect_ev = ngx_palloc (pool, sizeof (ngx_event_t)); ++ ngx_memzero (mcctx->reconnect_ev, sizeof (ngx_event_t)); ++ mcctx->reconnect_ev->handler = ngx_memcache_reconnection_handler; ++ mcctx->reconnect_ev->log = log; ++ ++ /* Initialize the head of the work queue doubly-linked list */ ++ mcwq = &mcctx->wq_head; ++ mcwq->w.request_code = mcreq_noop; ++ mcwq->pool = pool; ++ mcwq->prev = mcwq; ++ mcwq->next = mcwq; ++ ++ peercxn->connection->data = mcctx; ++ peercxn->connection->log = log; ++ ++ mcctx->srvconn = peercxn; ++ mcctx->srvaddr = peer; ++ mcctx->status = mcchan_good; ++ mcctx->timeout = mcf->timeout; ++ mcctx->cxn_interval = mcf->reconnect; ++ setsockopt(peercxn->connection->fd, SOL_SOCKET, SO_SNDBUF, ++ (void *) &mc_sndbuf_len, sizeof (mc_sndbuf_len)); ++ } ++ ++ ngx_log_error(NGX_LOG_INFO, log, 0, ++ "memcache: %d/%d connections initialized", ++ mcf->contexts.nelts, mcf->servers.nelts); ++ ++ return NGX_OK; ++} ++ ++static char *ngx_memcache_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) ++{ ++ ngx_conf_t ocf; ++ char *rc; ++ ++ ocf = *cf; ++ ++ cf->ctx = cf->cycle->conf_ctx; ++ cf->module_type = NGX_CORE_MODULE; ++ cf->cmd_type = NGX_MEMCACHE_CONF; ++ ++ rc = ngx_conf_parse(cf, NULL); ++ ++ *cf = ocf; ++ ++ return rc; ++} ++ ++static char * ++ngx_memcache_servers (ngx_conf_t *cf, ngx_command_t *cmd, void *conf) ++{ ++ ngx_memcache_conf_t *mcf = conf; ++ ngx_addr_t **server; ++ ngx_uint_t i; ++ ngx_url_t u; ++ ++ for (i = 1; i < cf->args->nelts; ++i) ++ { ++ server = ngx_array_push(&mcf->servers); ++ if (server == NULL) { ++ return NGX_CONF_ERROR; ++ } ++ ++ *server = NULL; ++ ++ ngx_memzero(&u, sizeof(u)); ++ u.url = ((ngx_str_t *)cf->args->elts)[i]; ++ u.default_port = 11211; ++ u.uri_part = 1; ++ ++ /* note - since ngx_parse_url uses pools from cf, therefore all address ++ structures in *server will be allocated on the config pool instead ++ of the memcached pool ++ */ ++ if (ngx_parse_url(cf->pool, &u) != NGX_OK) { ++ if (u.err) { ++ ngx_log_error(NGX_LOG_ERR, cf->cycle->log, 0, ++ "%s in memcache:server %V", u.err, &u.url); ++ } ++ return NGX_CONF_ERROR; ++ } ++ ++ *server = u.addrs; ++ } ++ ++ return NGX_CONF_OK; ++} ++ ++/* Work queue manipulation functions */ ++static inline mc_workqueue_t *ngx_memcache_wq_front (mc_workqueue_t *head) ++{ ++ return head->next; ++} ++ ++mc_workqueue_t *ngx_memcache_wq_enqueue (mc_workqueue_t *head, mc_workqueue_t *wqe) ++{ ++ wqe->prev = head->prev; ++ wqe->next = head; ++ wqe->prev->next = wqe; ++ wqe->next->prev = wqe; ++ return wqe; ++} ++ ++mc_workqueue_t *ngx_memcache_wq_dequeue (mc_workqueue_t *head) ++{ ++ mc_workqueue_t *wqe; ++ ++ wqe = head->next; ++ ++ if (wqe != head) ++ { ++ wqe->prev->next = wqe->next; ++ wqe->next->prev = wqe->prev; ++ wqe->prev = NULL; ++ wqe->next = NULL; ++ } ++ ++ return wqe; ++} ++ ++static inline ngx_int_t ngx_memcache_wq_isempty (mc_workqueue_t *head) ++{ ++ return (ngx_memcache_wq_front(head) == head); ++} ++ ++/* Ignore a work entry in memcache work queue. This should be done if ++ * the ctx who initiated the memcache request is destroyed before ++ * memcache server returns. Otherwise, when response is coming, the ++ * invalid ctx may be callback and reused. ++ * ++ * Any work entry in the queue whose ctx is equal to the specified one ++ * will be ignored. ++ * ++ * */ ++void ngx_memcache_ignore_work_by_ctx (void * ctx) { ++ mc_workqueue_t * head, * entry; ++ ngx_memcache_conf_t * mcf; ++ mc_context_t * contexts; ++ ngx_uint_t i; ++ ++ mcf = (ngx_memcache_conf_t *) ++ ngx_get_conf(ngx_cycle->conf_ctx, ngx_memcache_module); ++ contexts = (mc_context_t *)mcf->contexts.elts; ++ for (i = 0; i < mcf->contexts.nelts; i++) { ++ mc_context_t * context = contexts + i; ++ head = &context->wq_head; ++ if (!ngx_memcache_wq_isempty(head)) { ++ entry = head->next; ++ while (entry->w.request_code != mcreq_noop) { ++ if ((void *) entry->w.ctx == (void *) ctx ) { ++ // mark it NULL and ignored later ++ entry->w.ctx = NULL; ++ } ++ entry = entry->next; ++ } ++ } ++ } ++} ++ ++/* Post a memcached request onto the workqueue of a memcached server ++ w work request describing the task ++ (also contains on_success/on_failure handlers) ++ k opaque key which will be used for calculating the server hash ++ value the data which should be sent to the memcached server ++ p the pool from which additional memory may be allocated as needed ++ l log object for debug/informational messages ++ */ ++void ngx_memcache_post ( ++ mc_work_t *w, ++ ngx_str_t key, ++ ngx_str_t value, ++ ngx_pool_t *p, ++ ngx_log_t *l ++ ) ++{ ++ ngx_str_t dummy_ttl = ngx_string("-1"); ++ ngx_memcache_post_with_ttl (w, key, value, dummy_ttl, p, l); ++} ++ ++void ngx_memcache_post_with_ttl ( ++ mc_work_t *w, ++ ngx_str_t key, ++ ngx_str_t value, ++ ngx_str_t ttl, ++ ngx_pool_t *p, ++ ngx_log_t *l) { ++ ngx_memcache_do_post(w, key, value, ttl, p, l); ++} ++ ++static void ++ngx_memcache_do_post ( ++ mc_work_t *w, ++ ngx_str_t key, ++ ngx_str_t value, ++ ngx_str_t ttl, ++ ngx_pool_t *p, ++ ngx_log_t *l ++ ) ++{ ++ ngx_uint_t h; ++ size_t t; ++ ssize_t n; ++ mc_context_t *mcctx; ++ mc_workqueue_t *r; ++ ngx_memcache_conf_t *mcf; ++ mc_context_t *contexts; ++ ngx_flag_t locked = 0; ++ ngx_flag_t reclaim = 0; ++ ngx_str_t pdu; ++ ++ mcf = (ngx_memcache_conf_t *)ngx_get_conf(ngx_cycle->conf_ctx, ngx_memcache_module); ++ contexts = (mc_context_t *)mcf->contexts.elts; ++ ++ ngx_log_debug1 (NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, "ngx_mc_log, org_key=[%s]", key.data); ++ h = ngx_memcache_hash (key.data, key.len); ++ ++ ngx_log_debug1 (NGX_LOG_DEBUG_CORE, l, 0, ++ "ngx_mc_log, posting memcache request to cache server #%d", h); ++ mcctx = contexts + h; ++ ++ if (h == MC_INVALID_HASH) { ++ ngx_log_error (NGX_LOG_NOTICE, l, 0, ++ "ngx_mc_log, no memcache server available, cannot post request"); ++ w->response_code = mcres_failure_unavailable; ++ w->on_failure(w); ++ return; ++ } ++ ++ if (p == NULL) { ++ p = ngx_create_pool(MC_REQ_POOL_SIZE, l); ++ if (p == NULL) { ++ ngx_log_error (NGX_LOG_NOTICE, l, 0, ++ "ngx_mc_log, failed to create pool, cannot post request"); ++ w->response_code = mcres_failure_unavailable; ++ w->on_failure(w); ++ return; ++ } ++ reclaim = 1; ++ } ++ ++ key = ngx_sha256_hash (p, key.data, key.len); ++ if (0 == key.len || NULL == key.data) { ++ ngx_log_error (NGX_LOG_NOTICE, l, 0, ++ "ngx_mc_log, failed to hash the key, cannot post request"); ++ w->response_code = mcres_failure_unavailable; ++ w->on_failure(w); ++ return; ++ } ++ ngx_log_debug1 (NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, "ngx_mc_log, hashed_key=[%s]", key.data); ++ ++ pdu = ngx_memcache_create_pdu(p, w, key, value, ttl, l); ++ if (pdu.data == NULL) { ++ ngx_log_error (NGX_LOG_NOTICE, l, 0, ++ "ngx_mc_log, failed to create pdu, cannot post request"); ++ w->response_code = mcres_failure_normal; ++ w->on_failure(w); ++ return; ++ } ++ ++ /* build up the request to enqueue on the workqueue ++ we build up the request earlier on, so that if ++ memory errors occur, we would not have posted a pdu ++ on the memcached channel that we cannot handle ++ */ ++ ++ r = ngx_pcalloc(p, sizeof(mc_workqueue_t)); ++ if (r == NULL) { ++ ngx_log_error (NGX_LOG_NOTICE, l, 0, ++ "ngx_mc_log, failed to allocate memory, cannot post request"); ++ w->response_code = mcres_failure_unavailable; ++ w->on_failure(w); ++ return; ++ } ++ ++ if (ngx_log_tid && mcctx->lock != ngx_log_tid) { ++ ngx_spinlock(&mcctx->lock, ngx_log_tid, 40); ++ locked = 1; ++ } ++ t = 0; ++ while (t < pdu.len) ++ { ++ n = ngx_send (mcctx->srvconn->connection, pdu.data + t, pdu.len - t); ++ if (n > 0) { ++ t += n; ++ if (mcctx->status == mcchan_reconnect) { ++ mcctx->status = mcchan_good; ++ } ++ } else { ++ if (locked) ++ ngx_unlock(&mcctx->lock); ++ ++ ngx_log_error (NGX_LOG_NOTICE, l, 0, ++ "ngx_mc_log, memcached channel %V orderly shutdown when posting request", ++ mcctx->srvconn->name); ++ ++ mcctx->status = mcchan_bad; ++ ngx_memcache_close_connection (mcctx->srvconn); ++ ngx_memcache_purge_connection_workqueue ++ (mcctx, mcres_failure_again); ++ ngx_memcache_prepare_reconnection(mcctx); ++ w->response_code = mcres_failure_again; ++ w->on_failure(w); ++ return; ++ } ++ } ++ ++ if (t == pdu.len) { ++ /* set the read timeout on the server channel *only* if there is no ++ outstanding timer set already (this is to opportunistically catch ++ any responses before the stipulated timeout ++ */ ++ ++ if (!mcctx->srvconn->connection->read->timer_set) { ++ ngx_add_timer (mcctx->srvconn->connection->read, mcctx->timeout); ++ } ++ ++ /* ngx_log_debug1 (NGX_LOG_DEBUG_ZIMBRA, ngx_cycle->log, 0, ++ "XXX post requst:%V", &pdu);*/ ++ } ++ ++ r->pool = p; ++ r->reclaim = reclaim; ++ r->w = *w; ++ r->w.response_code = mcres_unknown; ++ ++ ngx_memcache_wq_enqueue (&mcctx->wq_head, r); ++ ++ if (locked) { ++ ngx_unlock(&mcctx->lock); ++ } ++ ngx_log_debug2 (NGX_LOG_DEBUG_CORE, l, 0, ++ "ngx_mc_log, posted request(%p) on server #%d", r, h); ++ ++ return; ++} ++ ++/* memcache protocol handling routines */ ++ ++/* process successful add response */ ++static ngx_int_t ngx_memcache_process_add_ok ++ (u_char *stream, size_t size, mc_workqueue_t *wq, size_t *consumed) ++{ ++ size_t minimum_len; ++ ++ minimum_len = sizeof ("STORED") - 1 + ++ sizeof (CRLF) - 1; ++ ++ *consumed = 0; ++ ++ if (size < minimum_len) ++ { ++ return NGX_AGAIN; ++ } ++ ++ if (ngx_memcmp (stream, "STORED" CRLF, sizeof("STORED" CRLF) - 1)) ++ { ++ return ngx_memcache_process_error_line(stream, size, wq, consumed); ++ } ++ ++ *consumed = sizeof ("STORED" CRLF) - 1; ++ wq->w.response_code = mcres_success; ++ ++ return NGX_OK; ++} ++ ++/* process unsuccessful add response */ ++static ngx_int_t ngx_memcache_process_add_failed ++ (u_char *stream, size_t size, mc_workqueue_t *wq, size_t *consumed) ++{ ++ size_t minimum_len; ++ ++ minimum_len = sizeof ("NOT_STORED") - 1 + ++ sizeof (CRLF) - 1; ++ ++ *consumed = 0; ++ ++ if (size < minimum_len) ++ { ++ return NGX_AGAIN; ++ } ++ ++ if (ngx_memcmp (stream, "NOT_STORED" CRLF, sizeof("NOT_STORED" CRLF) - 1)) ++ { ++ return ngx_memcache_process_error_line(stream, size, wq, consumed); ++ } ++ ++ *consumed = sizeof ("NOT_STORED" CRLF) - 1; ++ wq->w.response_code = mcres_failure_normal; ++ ++ return NGX_OK; ++} ++ ++/* ++ * process the invalid add command. If the key in add command too long, ++ * memcached will return 2 error msgs for "add CRLF" ++ * and the "CRLF" respectively. It should be: ++ * CLIENT_ERROR bad command line format\r\nERROR\r\n ++ */ ++static ngx_int_t ngx_memcache_process_add_invalid ++ (u_char *stream, size_t size, mc_workqueue_t *wq, size_t *consumed) ++{ ++ size_t len; ++ len = sizeof ("CLIENT_ERROR bad command line format" CRLF) - 1 + ++ sizeof ("ERROR" CRLF) - 1; ++ ++ *consumed = 0; ++ ++ if (size < len) ++ { ++ return NGX_AGAIN; ++ } ++ ++ if (ngx_memcmp (stream, "CLIENT_ERROR bad command line format" CRLF ++ "ERROR" CRLF, ++ len)) ++ { ++ return ngx_memcache_process_error_line(stream, size, wq, consumed); ++ } ++ ++ *consumed = len; ++ wq->w.response_code = mcres_failure_input; ++ ++ ngx_log_error (NGX_LOG_NOTICE, ngx_cycle->log, 0, ++ "memcached return error message: " ++ "CLIENT_ERROR bad command line format ERROR"); ++ ++ return NGX_OK; ++} ++ ++/* process successful delete response */ ++static ngx_int_t ngx_memcache_process_delete_ok ++ (u_char *stream, size_t size, mc_workqueue_t *wq, size_t *consumed) ++{ ++ size_t minimum_len; ++ ++ minimum_len = sizeof ("DELETED") - 1 + ++ sizeof (CRLF) - 1; ++ ++ *consumed = 0; ++ ++ if (size < minimum_len) ++ { ++ return NGX_AGAIN; ++ } ++ ++ if (ngx_memcmp (stream, "DELETED" CRLF, sizeof("DELETED" CRLF) - 1)) ++ { ++ return ngx_memcache_process_error_line(stream, size, wq, consumed); ++ } ++ ++ *consumed = sizeof ("DELETED" CRLF) - 1; ++ wq->w.response_code = mcres_success; ++ ++ return NGX_OK; ++} ++ ++/* handle unsuccessful delete response */ ++static ngx_int_t ngx_memcache_process_delete_failed ++ (u_char *stream, size_t size, mc_workqueue_t *wq, size_t *consumed) ++{ ++ size_t minimum_len; ++ ++ minimum_len = sizeof ("NOT_FOUND") - 1 + ++ sizeof (CRLF) - 1; ++ ++ *consumed = 0; ++ ++ if (size < minimum_len) ++ { ++ return NGX_AGAIN; ++ } ++ ++ if (ngx_memcmp (stream, "NOT_FOUND" CRLF, sizeof("NOT_FOUND" CRLF) - 1)) ++ { ++ return ngx_memcache_process_error_line(stream,size,wq,consumed); ++ } ++ ++ *consumed = sizeof ("NOT_FOUND" CRLF) - 1; ++ wq->w.response_code = mcres_failure_normal; ++ ++ return NGX_OK; ++} ++ ++/* process successful get response */ ++static ngx_int_t ngx_memcache_process_get_ok ++ (u_char *stream, size_t size, mc_workqueue_t *wq, size_t *consumed) ++{ ++ /* A successful `get' response looks like: ++ ++ VALUE keyname flag size ++ ++ END ++ ++ A successful `get' response must be at least as long as: ++ ++ VALUE K F S ++ b ++ END ++ */ ++ ++ enum { ++ rr_value_read, ++ rr_key_read, ++ rr_flag_read, ++ rr_size_read, ++ rr_data_read, ++ rr_end_read ++ } state; ++ ++ size_t minimum_len; ++ u_char *p; ++ /* size_t tokenpos; */ ++ /* ngx_str_t key = ngx_string (""); */ ++ ngx_str_t value = ngx_string (""); ++ size_t value_size; ++ uint16_t flag; ++ ++ minimum_len = ++ sizeof ("VALUE ") - 1 + ++ sizeof ("k ") - 1 + ++ sizeof ("0 ") - 1 + ++ sizeof ("0 ") - 1 + ++ sizeof (CRLF) - 1 + ++ sizeof (CRLF) - 1 + ++ sizeof ("END") - 1 + ++ sizeof (CRLF) -1; ++ ++ p = stream; ++ *consumed = 0; ++ value_size = 0; ++ ++ /* If there is not enough space to even hold a bare minimum response, ++ then we cannot proceed */ ++ ++ if (size < minimum_len) ++ { ++ return NGX_AGAIN; ++ } ++ ++ if (ngx_memcmp (stream, "VALUE ", sizeof ("VALUE ") - 1)) ++ { ++ return ngx_memcache_process_error_line(stream,size,wq,consumed); ++ } ++ ++ p += (sizeof ("VALUE ") - 1); ++ *consumed += (sizeof ("VALUE ") - 1); ++ state = rr_value_read; ++ ++ for (;(p < stream + size) && (state != rr_end_read); ++p, ++*consumed) ++ { ++ switch (state) ++ { ++ case rr_value_read: ++ /* now we're expecting the key name */ ++ /* tokenpos = *consumed; */ ++ ++ while ((p < stream + size) && (*p != ' ')) ++ { ++ ++p; ++ ++*consumed; ++ } ++ ++ if (p == stream + size) ++ { ++ /* end of stream */ ++ --p; --*consumed; ++ } ++ else ++ { ++ /* The key is constructed */ ++ /* key.data = stream + tokenpos; */ ++ /* key.len = *consumed - tokenpos; */ ++ ++ state = rr_key_read; ++ } ++ ++ break; ++ ++ case rr_key_read: ++ /* now we're expecting a flag, which is a short integer ++ which corresponds to the `flags' argument to mc_add ++ To avoid looping more than is necessary, we'll play a ++ simple trick, which is to initialize the flag to 0, ++ and then keep *10 + the current digit encountered ++ */ ++ ++ flag = 0; ++ ++ while ((p < stream + size) && (*p != ' ')) ++ { ++ flag = (flag * 10) + *p; ++ ++p; ++ ++*consumed; ++ } ++ ++ if (p == stream + size) ++ { ++ --p; --*consumed; ++ } ++ else ++ { ++ /* The flag is constructed */ ++ ++ state = rr_flag_read; ++ } ++ ++ break; ++ ++ case rr_flag_read: ++ /* now we're expecting the size of the data ++ */ ++ value_size = 0; ++ ++ while ((p < stream + size) ++ && (*p != ' ') /* wasteful ? */ ++ && (*p != CR) ++ && (*p != LF) /* wasteful ? */ ++ ) ++ { ++ value_size = (value_size * 10) + (*p - '0'); ++ ++p; ++ ++*consumed; ++ } ++ ++ if (p == stream + size) ++ { ++ --p; --*consumed; ++ } ++ else ++ { ++ /* The size is constructed */ ++ state = rr_size_read; ++ } ++ ++ break; ++ ++ case rr_size_read: ++ /* now we're looking out for the data ++ the '\r' has already been consumed in the for loop ++ so we must look beyond the '\n', and then read ++ value_size bytes more from the stream ++ */ ++ ++ while ((p < stream + size) && (*p != LF)) ++ { ++ ++p; ++*consumed; ++ } ++ ++ if (p == stream + size) ++ { ++ --p; --*consumed; ++ } ++ else ++ { ++ /* now p is pointing at the line feed preceding the data */ ++ ++ ++p; ++*consumed; ++ ++ if (p == stream + size) ++ { ++ --p; --*consumed; ++ } ++ else ++ { ++ minimum_len = ++ value_size + ++ sizeof (CRLF) - 1 + ++ sizeof ("END") - 1 + ++ sizeof (CRLF) - 1; ++ ++ ++ if (size - *consumed < minimum_len) ++ { ++ /* request cannot be completed here */ ++ } ++ else ++ { ++ /* we have the value, starting at p */ ++ value.data = p; ++ value.len = value_size; ++ ++ /* now just advance in one shot till the end */ ++ ++ p+= value_size; *consumed += value_size; ++ ++ /* And now, just consume the following CR too ++ so that the END context lands bingo at END ++ */ ++ ++ state = rr_data_read; ++ ++ if (*p == CR) /* this is superfluous */ ++ { ++ ++p; ++*consumed; ++ } ++ } ++ } ++ } ++ ++ break; ++ ++ case rr_data_read: ++ /* we know that sufficient bytes are present, and that we ++ should be looking at "END" ++ */ ++ ++ if (ngx_memcmp (p, "END" CRLF, sizeof ("END" CRLF) - 1)) ++ { ++ /* not possible. try logging here */ ++ } ++ else ++ { ++ state = rr_end_read; ++ ++ p += (sizeof ("END") - 1); ++ *consumed += (sizeof ("END") - 1); ++ ++ /* again, we will consume the CR so that we break out at ++ once */ ++ ++ if (*p == CR) /* superfluous */ ++ { ++ ++p; ++ ++*consumed; ++ } ++ } ++ ++ break; ++ ++ default: ++ break; ++ } ++ } ++ ++ if (state != rr_end_read) ++ { ++ return NGX_AGAIN; /* This means there wasn't enough data */ ++ } ++ ++ /* we've finished processing the get response */ ++ ++ wq->w.payload.data = ngx_pstrdup (wq->pool, &value); ++ wq->w.payload.len = value.len; ++ wq->w.response_code = mcres_success; ++ ++ return NGX_OK; ++} ++ ++/* process unsuccessful get response */ ++static ngx_int_t ngx_memcache_process_get_failed ++ (u_char *stream, size_t size, mc_workqueue_t *wq, size_t *consumed) ++{ ++ size_t minimum_len; ++ ++ minimum_len = sizeof ("END") - 1 + ++ sizeof (CRLF) - 1; ++ ++ *consumed = 0; ++ ++ if (size < minimum_len) ++ { ++ return NGX_AGAIN; ++ } ++ ++ if (ngx_memcmp (stream, "END" CRLF, sizeof("END" CRLF) - 1)) ++ { ++ return ngx_memcache_process_error_line(stream,size,wq,consumed); ++ } ++ ++ *consumed = sizeof ("END" CRLF) - 1; ++ wq->w.response_code = mcres_failure_normal; ++ ++ return NGX_OK; ++} ++ ++static ngx_int_t ngx_memcache_process_incr_ok ++ (u_char *stream, size_t size, mc_workqueue_t *wq, size_t *consumed) ++{ ++ u_char *p; ++ ++ p = stream; ++ ++ while ((p < stream + size) && (*p != CR) && (*p != LF)) ++ { ++ if (!(*p >= '0' && *p <= '9')) { ++ return ngx_memcache_process_error_line(stream, size, wq, consumed); ++ } ++ ++ ++p; ++ ++*consumed; ++ } ++ ++ if (p == stream + size) { ++ return NGX_AGAIN; ++ } else { ++ if (size - *consumed >= 2) ++ { ++ if (*p == CR && *(p + 1) == LF) ++ { ++ *consumed += 2; ++ wq->w.payload.len = p - stream; ++ wq->w.payload.data = ngx_palloc(wq->pool, wq->w.payload.len); ++ ngx_memcpy (wq->w.payload.data,stream, wq->w.payload.len); ++ wq->w.response_code = mcres_success; ++ return NGX_OK; ++ } ++ else ++ { ++ return ngx_memcache_process_error_line(stream, size, wq, consumed); ++ } ++ } ++ else ++ { ++ return NGX_AGAIN; ++ } ++ } ++} ++ ++static ngx_int_t ngx_memcache_process_incr_failed ++ (u_char *stream, size_t size, mc_workqueue_t *wq, size_t *consumed) ++{ ++ size_t minimum_len; ++ ++ minimum_len = sizeof ("NOT_FOUND") - 1 + ++ sizeof (CRLF) - 1; ++ ++ *consumed = 0; ++ ++ if (size < minimum_len) ++ { ++ return NGX_AGAIN; ++ } ++ ++ if (ngx_memcmp (stream, "NOT_FOUND" CRLF, sizeof ("NOT_FOUND" CRLF) - 1)) ++ { ++ return ngx_memcache_process_error_line(stream, size, wq, consumed); ++ } ++ ++ *consumed = sizeof ("NOT_FOUND" CRLF) - 1; ++ wq->w.response_code = mcres_failure_normal; ++ ++ return NGX_OK; ++} ++ ++static ngx_int_t ngx_memcache_process_decr_ok ++ (u_char *stream, size_t size, mc_workqueue_t *wq, size_t *consumed) ++{ ++ u_char *p; ++ ++ p = stream; ++ ++ while ((p < stream+size) && (*p != CR) && (*p != LF)) ++ { ++ if (!(*p>='0' && *p<='9')) { ++ return ngx_memcache_process_error_line(stream, size, wq, consumed); ++ } ++ ++ ++p; ++ ++*consumed; ++ } ++ ++ if (p == stream + size) { ++ return NGX_AGAIN; ++ } else { ++ if (size - *consumed >= 2) ++ { ++ if (*p == CR && *(p+1) == LF) ++ { ++ *consumed += 2; ++ wq->w.payload.len = p-stream; ++ wq->w.payload.data = ngx_palloc(wq->pool,wq->w.payload.len); ++ ngx_memcpy (wq->w.payload.data,stream,wq->w.payload.len); ++ wq->w.response_code = mcres_success; ++ return NGX_OK; ++ } ++ else ++ { ++ return ngx_memcache_process_error_line(stream, size, wq, consumed); ++ } ++ } ++ else ++ { ++ return NGX_AGAIN; ++ } ++ } ++} ++ ++static ngx_int_t ngx_memcache_process_decr_failed ++ (u_char *stream, size_t size, mc_workqueue_t *wq, size_t *consumed) ++{ ++ size_t minimum_len; ++ ++ minimum_len = sizeof ("NOT_FOUND") - 1 + ++ sizeof (CRLF) - 1; ++ ++ *consumed = 0; ++ ++ if (size < minimum_len) ++ { ++ return NGX_AGAIN; ++ } ++ ++ if (ngx_memcmp (stream, "NOT_FOUND" CRLF, sizeof ("NOT_FOUND" CRLF) - 1)) ++ { ++ return ngx_memcache_process_error_line(stream, size, wq, consumed); ++ } ++ ++ *consumed = sizeof ("NOT_FOUND" CRLF) - 1; ++ wq->w.response_code = mcres_failure_normal; ++ ++ return NGX_OK; ++} ++ ++/* process "ERROR\r\n" */ ++static ngx_int_t ngx_memcache_process_error ++ (u_char *stream, size_t size, mc_workqueue_t *wq, size_t *consumed) ++{ ++ size_t minimum_len; ++ ++ minimum_len = sizeof ("ERROR") - 1 + ++ sizeof (CRLF) - 1; ++ ++ *consumed = 0; ++ ++ if (size < minimum_len) ++ { ++ return NGX_AGAIN; ++ } ++ ++ if (ngx_memcmp (stream, "ERROR" CRLF, sizeof ("ERROR" CRLF) - 1)) ++ { ++ return NGX_ERROR; ++ } ++ ++ *consumed = sizeof ("ERROR" CRLF) - 1; ++ ++ ngx_log_error (NGX_LOG_NOTICE, ngx_cycle->log, 0, ++ "memcached return error message: ERROR"); ++ ++ wq->w.response_code = mcres_failure_input; ++ return NGX_OK; ++} ++ ++static ngx_int_t ngx_memcache_process_client_error ++ (u_char *stream, size_t size, mc_workqueue_t *wq, size_t *consumed) ++{ ++ size_t minimum_len; ++ u_char *p; ++ minimum_len = sizeof ("CLIENT_ERROR") - 1 + ++ sizeof (CRLF) - 1; ++ ++ *consumed = 0; ++ ++ if (size < minimum_len) ++ { ++ return NGX_AGAIN; ++ } ++ ++ if (ngx_memcmp (stream, "CLIENT_ERROR", sizeof ("CLIENT_ERROR") - 1)) ++ { ++ return NGX_ERROR; ++ } ++ ++ p = stream + sizeof ("CLIENT_ERROR") - 1; ++ *consumed = sizeof ("CLIENT_ERROR") - 1; ++ while ((p < stream + size) && (*p != LF)) ++ { ++ ++p; ++*consumed; ++ } ++ ++ if (*p != LF || *(p - 1) != CR) ++ { ++ return NGX_AGAIN; ++ } ++ ++ if (p < stream + size) ++ { ++ ++p; ++ ++*consumed; ++ } ++ ++ ngx_str_t log_output; ++ log_output.data = stream; ++ log_output.len = *consumed - 2; //chomp the trailing CRLF ++ ++ ngx_log_error (NGX_LOG_NOTICE, ngx_cycle->log, 0, ++ "memcached return error message: %V", &log_output); ++ ++ wq->w.response_code = mcres_failure_input; ++ return NGX_OK; ++} ++ ++static ngx_int_t ngx_memcache_process_server_error ++ (u_char *stream, size_t size, mc_workqueue_t *wq, size_t *consumed) ++{ ++ size_t minimum_len; ++ u_char *p; ++ minimum_len = sizeof ("SERVER_ERROR") - 1 + ++ sizeof (CRLF) - 1; ++ ++ *consumed = 0; ++ ++ if (size < minimum_len) ++ { ++ return NGX_AGAIN; ++ } ++ ++ if (ngx_memcmp (stream, "SERVER_ERROR", sizeof ("SERVER_ERROR") - 1)) ++ { ++ return NGX_ERROR; ++ } ++ ++ p = stream + sizeof ("SERVER_ERROR") - 1; ++ *consumed = sizeof ("SERVER_ERROR") - 1; ++ while ((p < stream + size) && (*p != LF)) ++ { ++ ++p; ++*consumed; ++ } ++ ++ if (*p != LF || *(p - 1) != CR) ++ { ++ return NGX_AGAIN; ++ } ++ ++ if (p < stream + size) ++ { ++ ++p; ++ ++*consumed; ++ } ++ ++ ngx_str_t log_output; ++ log_output.data = stream; ++ log_output.len = *consumed - 2; //chomp the trailing CRLF ++ ++ ngx_log_error (NGX_LOG_NOTICE, ngx_cycle->log, 0, ++ "memcached return error message: %V", &log_output); ++ ++ wq->w.response_code = mcres_failure_again; ++ return NGX_OK; ++} ++ ++/* consume a line of error message or unexcepted response, ++ * including ERROR, SERVER_ERROR and CLIENT_ERROR, as well as ++ * other unrecognized ones. ++ * Returns: ++ * NGX_OK: handle correctly and continue the next qork queue entry ++ * NGX_ERROR: server is bad, has to close connection and purge work ++ * queue ++ * NGX_AGAIN: recv more bytes to complete handling */ ++static ngx_int_t ngx_memcache_process_error_line ++ (u_char *stream, size_t size, mc_workqueue_t *wq, size_t *consumed) ++{ ++ u_char *p; ++ ngx_int_t rc = NGX_ERROR; ++ ++ p = stream; ++ *consumed = 0; ++ ++ if (*p == 'E') { ++ /* memcached return 'ERROR CRLF' */ ++ rc = ngx_memcache_process_error (stream, size, wq, consumed); ++ } else if (*p == 'C' ) { ++ /* memcached return 'CLIENT_ERROR CRLF' */ ++ rc = ngx_memcache_process_client_error (stream, size, wq, consumed); ++ } else if (*p == 'S') { ++ /* memcached return 'SERVER_ERROR CRLF' ++ * if it's a valid SERVER_ERROR, we still have ++ * to return NGX_ERROR to close the connection to current ++ * channel and purge the queue. Therefore, all the buffered bytes ++ * has to be chomped here. ++ */ ++ rc = ngx_memcache_process_server_error (stream, size, wq, consumed); ++ if (rc == NGX_OK) { ++ /* chomp all the received raw bytes in buffer */ ++ *consumed = size; ++ ngx_str_t log_output; ++ log_output.data = stream; ++ log_output.len = *consumed; ++ ngx_log_error (NGX_LOG_ERR, ngx_cycle->log, 0, ++ "memcached returns SERVER_ERROR, " ++ "chomp all %d bytes in buffer: %V", ++ *consumed, &log_output); ++ return NGX_ERROR; ++ } ++ } ++ ++ if (rc == NGX_OK || rc == NGX_AGAIN) { ++ return rc; ++ } else { ++ /* the response is unrecognized, ++ * chomp all the received raw bytes in buffer */ ++ *consumed = size; ++ ++ ngx_str_t log_output; ++ log_output.data = stream; ++ log_output.len = size; ++ ngx_log_error (NGX_LOG_ERR, ngx_cycle->log, 0, ++ "memcached returns unrecognized response, " ++ "chomp all %d bytes in buffer: %V", ++ *consumed, &log_output); ++ return NGX_ERROR; ++ } ++} ++ ++/* process response to any memcached command ++ with the advent of more supported operations, we need more context in ++ order to process the response to a previously submitted command ++ that is because responses to some memcached commands are identical ++ ++ see docs/MEMCACHE-PROTOCOL for details ++ */ ++ngx_int_t ngx_memcache_process_any_response ++ (u_char *stream, size_t size, mc_workqueue_t *wq, size_t *consumed) ++{ ++ mcp_handler handler; ++ mc_request_code_t op; ++ ++ *consumed = 0; ++ if (size == 0) { return NGX_AGAIN; } ++ ++ op = wq->w.request_code; ++ ++ switch (op) ++ { ++ case mcreq_add: ++ switch (*stream) ++ { ++ case 'S': /* STORED */ ++ handler = ngx_memcache_process_add_ok; ++ break; ++ case 'N': /* NOT_STORED */ ++ handler = ngx_memcache_process_add_failed; ++ break; ++ case 'C': /* CLIENT_ERROR msgERROR */ ++ handler = ngx_memcache_process_add_invalid; ++ break; ++ default: ++ handler = ngx_memcache_process_error_line; ++ break; ++ } ++ break; ++ case mcreq_get: ++ switch (*stream) ++ { ++ case 'V': /* VALUE */ ++ handler = ngx_memcache_process_get_ok; ++ break; ++ case 'E': /* END */ ++ handler = ngx_memcache_process_get_failed; ++ break; ++ default: ++ handler = ngx_memcache_process_error_line; ++ break; ++ } ++ break; ++ case mcreq_delete: ++ switch (*stream) ++ { ++ case 'D': /* DELETED */ ++ handler = ngx_memcache_process_delete_ok; ++ break; ++ case 'N': /* NOT_FOUND */ ++ handler = ngx_memcache_process_delete_failed; ++ break; ++ default: ++ handler = ngx_memcache_process_error_line; ++ break; ++ } ++ break; ++ case mcreq_incr: ++ if (*stream == 'N') /* NOT_FOUND */ { ++ handler = ngx_memcache_process_incr_failed; ++ } else { ++ handler = ngx_memcache_process_incr_ok; ++ } ++ break; ++ case mcreq_decr: ++ if (*stream == 'N') /* NOT_FOUND */ { ++ handler = ngx_memcache_process_decr_failed; ++ } else { ++ handler = ngx_memcache_process_decr_ok; ++ } ++ break; ++ default: ++ handler = ngx_memcache_process_error_line; ++ break; ++ } ++ ++ return handler (stream, size, wq, consumed); ++} ++ ++static void ngx_memcache_dummy_write_handler (ngx_event_t *wev) ++{ ++ ngx_log_debug0(NGX_LOG_DEBUG_CORE, wev->log, 0, ++ "dummy memcache write-event handler"); ++} ++ ++/* Generic memcache response event handler (see docs/MEMCACHE-PROTOCOL) */ ++static void ngx_memcache_any_read_handler(ngx_event_t *rev) ++{ ++ ngx_connection_t *c; ++ mc_context_t *ctx; ++ size_t available, consumed; ++ ssize_t n; ++ ngx_buf_t *readbuffer; ++ /* volatile */ mc_workqueue_t *wq_head; ++ mc_workqueue_t *wq_entry; ++ ngx_int_t rc; ++ ngx_flag_t reclaim; ++ ++ c = rev->data; ++ ctx = c->data; ++ readbuffer = ctx->readbuffer; ++ wq_head = &ctx->wq_head; ++ ++ ngx_log_debug1 (NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, ++ "memcached read event:%V", ctx->srvconn->name); ++ ++ if (ctx->status == mcchan_bad) { ++ ngx_log_error (NGX_LOG_ERR, ngx_cycle->log, 0, ++ "ngx_memcache_any_read_handler should always be " ++ "callback when channel status is \"good\" or " ++ "\"reconnect\""); ++ } ++ ++ /* nginx buffer ++ ++ [[s]......[p]......[l].......[e]] ++ ++ s = start ++ p = pos ++ l = last ++ e = end ++ ++ */ ++ ++ /* TODO Why here is not locked ??? */ ++ available = readbuffer->end - readbuffer->last; ++ ++ if (available == 0) ++ { ++ /* no space in circular buffer to read the responses */ ++ ++ ngx_log_debug1 (NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, ++ "recycle circular buffer:%d bytes", ++ readbuffer->pos - readbuffer->start ++ ); ++ ++ memmove (readbuffer->start, readbuffer->pos, ++ readbuffer->last - readbuffer->pos); ++ ++ readbuffer->last -= (readbuffer->pos - readbuffer->start); ++ readbuffer->pos = readbuffer->start; ++ ++ available = readbuffer->end - readbuffer->last; ++ ++ if (available == 0) ++ { ++ ngx_log_error (NGX_LOG_CRIT, ngx_cycle->log, 0, ++ "cannot recycle circular buffer"); ++ ++ /* TODO how to recover ? */ ++ return; ++ } ++ ++ /* note: recursive call has been removed */ ++ } ++ ++ if (ngx_log_tid && ctx->lock == ngx_log_tid) { ++ ngx_log_error (NGX_LOG_CRIT, ngx_cycle->log, 0, ++ "memcached loop"); ++ return; ++ } else { ++ ngx_spinlock(&ctx->lock, ngx_log_tid, 40); ++ } ++ ++ if (rev->timedout) ++ { ++ /* read timed out */ ++ ngx_log_error (NGX_LOG_INFO, ngx_cycle->log, 0, ++ "memcached channel:%V timed out", ctx->srvconn->name); ++ ++ ctx->status = mcchan_bad; ++ ngx_memcache_close_connection (ctx->srvconn); ++ ngx_memcache_purge_connection_workqueue (ctx, mcres_failure_again); ++ ngx_memcache_prepare_reconnection (ctx); // schedule reconnection event ++ ++ ngx_unlock(&ctx->lock); ++ return; ++ } ++ ++ n = ngx_recv (c, readbuffer->last, available); ++ ++ if (rev->eof) ++ { ++ /* recv() zero bytes implies peer orderly shutdown (recv(2)) */ ++ ngx_log_error (NGX_LOG_NOTICE, ngx_cycle->log, 0, ++ "memcached channel:%V orderly shutdown", ctx->srvconn->name); ++ ++ /* channel was hitherto good, don't reconnect immediately, rather ++ wait for cxn_interval before reconnecting ++ */ ++ ctx->status = mcchan_bad; ++ ngx_memcache_close_connection (ctx->srvconn); ++ ngx_memcache_purge_connection_workqueue (ctx, mcres_failure_again); ++ ngx_log_debug2 (NGX_LOG_DEBUG_MAIL, ngx_cycle->log, 0, ++ "bad memcached channel:%V, reconnect:%d ms", ++ ctx->srvconn->name, ctx->cxn_interval); ++ ngx_memcache_prepare_reconnection(ctx); ++ ++ if (rev->timer_set) { ++ ngx_del_timer (rev); ++ } ++ ngx_unlock(&ctx->lock); ++ return; ++ ++ } ++ else if (n == NGX_AGAIN) ++ { ++ /* EAGAIN should have resulted in a timeout which has been ++ handled previously ++ */ ++ ngx_log_error (NGX_LOG_WARN, ngx_cycle->log, 0, ++ "ignoring *AGAIN on memcached channel %V", ctx->srvconn->name); ++ ++ ngx_unlock(&ctx->lock); ++ return; ++ ++ } ++ else if (n == NGX_ERROR) ++ { ++ /* After trying to reconnection, and then reach here, it indicates ++ * that the reconnection fails because target server is not available ++ */ ++ if (ctx->status == mcchan_reconnect) { ++ ctx->status = mcchan_bad; ++ ngx_log_error (NGX_LOG_ERR, ngx_cycle->log, 0, ++ "reconnect to memcached channel %V fails", ctx->srvconn->name); ++ ngx_memcache_close_connection (ctx->srvconn); ++ ngx_memcache_prepare_reconnection(ctx); ++ ngx_unlock(&ctx->lock); ++ return; ++ } ++ /* There was an error reading from this socket */ ++ ngx_log_debug1 (NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, ++ "memcached channel %V:error in recv, purging queue", ++ ctx->srvconn->name ++ ); ++ ctx->status = mcchan_bad; ++ ngx_memcache_close_connection (ctx->srvconn); ++ ngx_memcache_purge_connection_workqueue (ctx, mcres_failure_again); ++ ngx_log_debug2 (NGX_LOG_DEBUG_MAIL, ngx_cycle->log, 0, ++ "bad memcached channel:%V, reconnect:%d ms", ++ ctx->srvconn->name, ctx->cxn_interval); ++ ngx_memcache_prepare_reconnection(ctx); ++ if (rev->timer_set) { ++ ngx_del_timer (rev); ++ } ++ ngx_unlock(&ctx->lock); ++ return; ++ } ++ ++ readbuffer->last += n; ++ ++ if (rev->timer_set) { ++ ngx_del_timer (rev); ++ } ++ ++ if (ngx_memcache_wq_isempty (wq_head)) ++ { ++ ctx->readbuffer->pos = ctx->readbuffer->last; ++ ngx_unlock(&ctx->lock); ++ ngx_log_error (NGX_LOG_WARN, ngx_cycle->log, 0, ++ "memcached channel %V:discard %d bytes(bad data), reset buffer", ++ ctx->srvconn->name, n); ++ ++ return; ++ } ++ ++ while ((!ngx_memcache_wq_isempty (wq_head)) && ((readbuffer->last - readbuffer->pos) > 0)) ++ { ++ wq_entry = ngx_memcache_wq_front (wq_head); ++ consumed = 0; ++ ++ rc = ngx_memcache_process_any_response ( ++ readbuffer->pos, ++ readbuffer->last - readbuffer->pos, ++ wq_entry, ++ &consumed); ++ ++ ngx_log_debug2 (NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, ++ "memcache proto-handler consumed:%d,rc:%d", consumed, rc ++ ); ++ ++ if (rc == NGX_OK) ++ { ++ readbuffer->pos += consumed; ++ ++ /* correct response, so dequeue entry */ ++ wq_entry = ngx_memcache_wq_dequeue (wq_head); ++ ++ reclaim = wq_entry->reclaim; ++ ngx_memcache_callback (&wq_entry->w); ++ if (reclaim) { // free the memory if allocated from memcache request pool ++ ngx_destroy_pool(wq_entry->pool); ++ } ++ } ++ else if (rc == NGX_ERROR) ++ { ++ readbuffer->pos += consumed; ++ ++ /* wrong response, purge all the entries (including the current one) */ ++ ctx->status = mcchan_bad; ++ ngx_memcache_close_connection (ctx->srvconn); ++ ngx_memcache_purge_connection_workqueue ++ (ctx, mcres_failure_again); ++ ngx_memcache_prepare_reconnection(ctx); ++ } ++ else if (rc == NGX_AGAIN) ++ { ++ /* The response handler has indicated that there isn't sufficient ++ space to read the data, so we must move bytes over to the start ++ */ ++ ++ ngx_log_debug1 (NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, ++ "proto-handler got partial response, recycling [pos=%d]", ++ readbuffer->pos - readbuffer->start ++ ); ++ ++ memmove (readbuffer->start, readbuffer->pos, ++ readbuffer->last - readbuffer->pos); ++ ++ readbuffer->last -= (readbuffer->pos - readbuffer->start); ++ readbuffer->pos = readbuffer->start; ++ ++ ngx_unlock(&ctx->lock); ++ ngx_memcache_any_read_handler (rev); ++ ngx_spinlock(&ctx->lock, ngx_log_tid, 40); ++ ++ break; ++ } ++ } /* while */ ++ ++ if (!ngx_memcache_wq_isempty (wq_head)) { ++ ngx_add_timer (rev, ctx->timeout); ++ } ++ ++ ngx_unlock(&ctx->lock); ++} ++ ++/* purge all outstanding connections from memcached work-queue */ ++ ++static void ngx_memcache_purge_connection_workqueue (mc_context_t *mcctx, mc_response_code_t res) ++{ ++ mc_workqueue_t *head, *entry; ++ ngx_flag_t reclaim; ++ ngx_uint_t count; ++ ++ head = &mcctx->wq_head; ++ if (ngx_memcache_wq_isempty(head)) ++ return; ++ ++ /*the mcchannel status of current context has to be set as ++ "mcchan_bad" before this function's invoke because the user ++ in the upper layer may retry connection. If status is ++ mcchan_good, the retry may incur an endless recursion */ ++ if (mcctx->status == mcchan_good) ++ mcctx->status = mcchan_bad; ++ ++ ngx_log_debug1 (NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, ++ "memcached channel:%V, purging all entries", ++ mcctx->srvconn->name); ++ ++ count = 0; ++ while (!ngx_memcache_wq_isempty(head)) { ++ entry = ngx_memcache_wq_dequeue (head); ++ reclaim = entry->reclaim; ++ entry->w.response_code = res; ++ ngx_memcache_callback(&entry->w); ++ ++ if (reclaim) { ++ ngx_destroy_pool(entry->pool); ++ } ++ count++; ++ } ++ ++ ngx_log_error (NGX_LOG_INFO, ngx_cycle->log, 0, ++ "memcached channel:%V, purged all %d entries", ++ mcctx->srvconn->name, count); ++ ++ return; ++} ++ ++static inline void ngx_memcache_callback (mc_work_t * w) ++{ ++ if (w->ctx != NULL) { //call back only when not ignored ++ if (w->response_code == mcres_success) { ++ w->on_success (w); ++ } else { ++ w->on_failure (w); ++ } ++ } ++} ++ ++static inline void ngx_memcache_close_connection (ngx_peer_connection_t * pc) { ++ ngx_close_connection (pc->connection); ++ pc->connection = NULL; ++} ++ ++/* This is the routine for re-establishing a memcache connection that has ++ broken down for some reason. Usually, we *expect* this to happen in ++ cases when a memcache server crashes. ++ ++ There are interesting effects observed on linux how a connected TCP ++ socket, which is marked in non-blocking mode (as nginx does), behaves ++ when the peer end goes down. ++ ++ Firstly, when a connect() is invoked on a socket that's non-blocking, ++ the return code is EINPROGRESS even if the peer is up, or whether it's ++ down at the time (which is the reason why nginx's connection logic ++ in ngx_event_connect_peer explicitly checks for EINPROGRESS, and why we ++ explicitly ignore it) ++ ++ Now, there are two very distinct fates of the subsequent send() and recv() ++ calls on the socket, in case something goes(or went) wrong with the peer: ++ ++ (*) In the case when the peer was down at the time of connect(), then any ++ send() and recv() immediately fails with ECONNREFUSED. ++ ++ (*) In the case when the peer went down at some point after the connect(), ++ then the first send() after this time *appears* to succeed, whereas ++ subsequent send()s fail with EPIPE (broken pipe). recv(), however, as ++ usual, succeeds, but with 0 bytes read ++ ++ What this means for us, is that if a memcache connection goes sour because ++ of peer problems, then we can expect ngx_send()s to start failing with ++ EPIPE, whereas our ngx_recv(), which is indirectly invoked via the event ++ handling mechanism, will receive 0 bytes from ngx_recv() ++ ++ In both cases, we need to purge the corresponding work queues ASAP, because ++ these may contain pending cache-get requests. A pending cache-get request ++ corresponds to a email connection freshly initiated by a client, and which ++ is requesting for upstream server information in order to initiate the proxy. ++ This is a high priority request, because the proxy initiation is waiting for ++ the result of this operation. ++ ++ On the other hand, a pending cache-add request corresponds to a proxy session ++ already initiated (and possibly, already finished), which just needs to cache ++ the upstream info, which was previously retrieved from the http-auth servers. ++ In this situation, no client is really waiting for the add operation to ++ complete, but the memory still needs to be freed. Hence, it's lower priority. ++ ++ So, purging a work queue is an action that is highest priority, and must be ++ triggered whenever anything goes amiss with the ngx_send() or ngx_recv(). ++ ++ On the other hand, we don't need to be very aggressive in trying to re- ++ establish the connection to a broken peer, as long as we mark that memcache ++ connection as `bad', so that our memcache server hashing algorithm is smart ++ enough to ignore the bad connection. ++ ++ We can just maintain a counter, which will signify the number of times that ++ a connection could have been used, but was ignored because the server was ++ down. This counter will start at 0, and will be incremented whenever any ++ ngx_send() or ngx_recv() failed, or whenever the hashing algorithm ++ originally selected that channel, but selected another because the channel ++ was marked as `bad'. ++ ++ A channel is `bad' when the counter is greater than zero, and it is good if ++ the counter is 0. ++ ++ The counter is incremented by one (and the channel is marked bad) whenever ++ there is an error on ngx_send and/or ngx_recv, and it is also incremented by ++ one whenever the hashing algorithm elects the channel, but finds it bad. ++ ++ The counter is reset to zero (and the channel marked good), after we re- ++ establish the connection. The re-establishment is attempted when the counter ++ reaches the config-file-defined threshold. A threshold of 1 indicates ++ aggressive re-connection, whereas larger values signify more lethargic ++ attempts at re-connection ++ ++ (important note) ++ As an alternative to the channel ageing algorithm used above, we can use ++ a simple timeout to indicate how long nginx will do without a bad memcache ++ connection before attempting to reconnect. Therefore, all references to ++ memcache channel age are superseded by the `reconnect interval' ++ ++ */ ++static void ngx_memcache_reestablish_connection (mc_context_t *mcctx) ++{ ++ ngx_int_t rc; ++ ++ if (mcctx->srvconn->connection) { ++ ngx_memcache_close_connection (mcctx->srvconn); ++ } ++ ++ ngx_memzero (mcctx->srvconn, sizeof (ngx_peer_connection_t)); ++ ++ mcctx->srvconn->sockaddr = mcctx->srvaddr->sockaddr; ++ mcctx->srvconn->socklen = mcctx->srvaddr->socklen; ++ mcctx->srvconn->name = &mcctx->srvaddr->name; ++ mcctx->srvconn->get = ngx_event_get_peer; ++ mcctx->srvconn->log = ngx_cycle->log; ++ mcctx->srvconn->log_error = NGX_ERROR_ERR; ++ ++ rc = ngx_event_connect_peer (mcctx->srvconn); ++ ++ if (mcctx->srvconn->connection) ++ { ++ mcctx->srvconn->connection->read->handler = ngx_memcache_any_read_handler; ++ mcctx->srvconn->connection->write->handler = ngx_memcache_dummy_write_handler; ++ mcctx->srvconn->connection->data = mcctx; ++ mcctx->srvconn->connection->log = ngx_cycle->log; ++ } ++ ++ if (rc == NGX_ERROR || rc == NGX_BUSY || rc == NGX_DECLINED) { ++ ngx_log_error (NGX_LOG_WARN, ngx_cycle->log, 0, ++ "cannot re-establish connection to memcached channel %V", ++ mcctx->srvconn->name); ++ mcctx->status = mcchan_bad; ++ } else { ++ /* In this case, the connection may return NGX_AGAIN (-2). ++ * If Then the read event is comming and recv return NGX_ERROR (-1), ++ * the reconnection fails. Otherwise, the reconnection is successful. ++ */ ++ ngx_log_error (NGX_LOG_NOTICE, ngx_cycle->log, 0, ++ "reconnect to memcached channel %V (rc: %d)", ++ mcctx->srvconn->name, rc); ++ mcctx->status = mcchan_reconnect; ++ setsockopt(mcctx->srvconn->connection->fd, SOL_SOCKET, SO_SNDBUF, ++ (void *) &mc_sndbuf_len, sizeof (mc_sndbuf_len)); ++ } ++} ++ ++/* mc_hash ++ * ++ * hash an opaque key onto an available memcached channel number ++ * if the memcached channel is currently bad, then fail-over to the next ++ * server in declarative order, until all channels are exhausted ++ * ++ * return MC_INVALID_HASH on failure ++ */ ++static ngx_uint_t ngx_memcache_hash (u_char *key, size_t len) ++{ ++ ngx_uint_t h, r, i; ++ mc_context_t *mcctx; ++ ngx_memcache_conf_t *mcf; ++ ++ mcf = (ngx_memcache_conf_t*) ngx_get_conf(ngx_cycle->conf_ctx, ngx_memcache_module); ++ ++ if (mcf->contexts.nelts == 0) { ++ r = MC_INVALID_HASH; ++ } else { ++ h = ngx_memcache_perl_hash (key, len); ++ r = h % mcf->contexts.nelts; ++ i = r; ++ ++ do { ++ mcctx = ((mc_context_t *)mcf->contexts.elts) + i; ++ ++ if (mcctx->status == mcchan_bad) { ++ i = (i + 1) % mcf->contexts.nelts; ++ } ++ } while ((mcctx->status == mcchan_bad) && (i != r)); ++ ++ if (mcctx->status == mcchan_bad) ++ r = MC_INVALID_HASH; ++ else ++ r = i; ++ } ++ ++ return r; ++} ++ ++static ngx_uint_t ngx_memcache_perl_hash (u_char *key, size_t len) ++{ ++ size_t i; ++ ngx_uint_t h; ++ u_char *p; ++ ++ p = key; ++ i = len; ++ h = 0; ++ ++ while (i--) ++ { ++ h += *p++; ++ h += (h << 10); ++ h ^= (h >> 6); ++ } ++ ++ h += (h << 3); ++ h ^= (h >> 11); ++ h += (h << 15); ++ ++ return h; ++} ++ ++static ngx_str_t ngx_sha256_hash (ngx_pool_t* p, u_char* key, size_t len) ++{ ++ ngx_str_t hashed_key; ++ u_char * digest = ngx_pnalloc(p, SHA256_KEY_LENGTH); ++ SHA256(key, len, digest); ++ hashed_key.data = ngx_memcache_hexstr(digest, SHA256_KEY_LENGTH); ++ hashed_key.len = strlen((const char *)hashed_key.data); ++ ++ return hashed_key; ++} ++ ++static inline void ++ngx_memcache_prepare_reconnection (mc_context_t * mcctx) { ++ ngx_event_t * ev = mcctx->reconnect_ev; ++ ev->data = mcctx; ++ ++ if (!ev->timer_set) { ++ ngx_add_timer(ev, mcctx->cxn_interval); ++ ngx_log_error (NGX_LOG_NOTICE, ngx_cycle->log, 0, ++ "memcached channel:%V down, reconnect after:%d ms", ++ mcctx->srvconn->name, mcctx->cxn_interval); ++ } ++} ++ ++static void ++ngx_memcache_reconnection_handler (ngx_event_t * ev) { ++ mc_context_t * ctx = (mc_context_t *) ev->data; ++ ++ if (ctx->status == mcchan_good) return; ++ ++ ev->timedout = 0; ++ //ev->timer_set has been set to 0 before this handler are invoked. ++ ++ /* unconditionally re-connect bad channel if this event is fired */ ++ ngx_log_debug1 (NGX_LOG_DEBUG_MAIL, ngx_cycle->log, 0, ++ "reconnect to %V right now", ++ ctx->srvconn->name); ++ ++ ngx_memcache_reestablish_connection (ctx); ++ ngx_unlock(&ctx->lock); ++ ++ if (ctx->status == mcchan_bad) { //schedule next retry ++ ngx_memcache_prepare_reconnection (ctx); ++ } ++ ++ return; ++} ++ ++static u_char * ++ngx_memcache_hexstr(unsigned char *buf, int len) ++{ ++ const char *set = "0123456789abcdef"; ++ static u_char str[65], *tmp; ++ unsigned char *end; ++ if (len > 32) ++ len = 32; ++ end = buf + len; ++ tmp = &str[0]; ++ while (buf < end) ++ { ++ *tmp++ = set[ (*buf) >> 4 ]; ++ *tmp++ = set[ (*buf) & 0xF ]; ++ buf ++; ++ } ++ *tmp = '\0'; ++ return str; ++} ++ ++/* ++ * generate memcache post data unit (pdu) ++ * the value's usage depends on the request command: ++ * 1. for "get", "delete", value is useless. you can set it to null ++ * 2. for "add", value is the value to be added ++ * 3. for "incr" or "decr", value is the number to increase or decrease. You ++ * have to ensure it's an integer. ++ * ++ * If the ttl is set to "-1", the memcache conf's ttl will be used; otherwise ++ * use the specified one. ttl is only effective for the "add" command. ++ */ ++static ngx_str_t ngx_memcache_create_pdu(ngx_pool_t *pool, mc_work_t *w, ++ ngx_str_t key, ngx_str_t value, ngx_str_t ttl, ngx_log_t * log) { ++ ++ ngx_str_t pdu; ++ size_t l, ll, len; ++ u_char *p; ++ ngx_memcache_conf_t *mcf; ++ ++ len = 0, ll = 0; ++ ++ /* check key validity */ ++ if (key.len > NGX_MAX_KEY_LENGTH) { ++ ngx_str_t md5_key; ++ u_char * md5 = ngx_pnalloc(pool, MD5_DIGEST_LENGTH); ++ MD5(key.data, key.len, md5); ++ md5_key.data = ngx_memcache_hexstr(md5, MD5_DIGEST_LENGTH); ++ md5_key.len = strlen((const char *)md5_key.data); ++ ++ ngx_log_debug2 (NGX_LOG_DEBUG_ZIMBRA, log, 0, ++ "the memcache request key %V is too long, use " ++ "its MD5 digest string %V as the key", ++ &key, &md5_key); ++ ++ key = md5_key; ++ } ++ ++ switch (w->request_code) { ++ case mcreq_get: ++ l = sizeof("get ") - 1 + key.len + sizeof(CRLF) - 1; ++ break; ++ case mcreq_add: ++ ll = 0; ++ len = value.len; ++ do { ++ len /= 10; ++ ll++; ++ } while (len != 0); ++ ++ mcf = (ngx_memcache_conf_t*) ngx_get_conf(ngx_cycle->conf_ctx, ngx_memcache_module); ++ ++ if (ngx_strncmp(ttl.data, "-1", 2) == 0) { ++ ttl = mcf->ttl_text; ++ } ++ ++ l = sizeof("add ") - 1 + key.len + sizeof(" ") - 1 + ++ sizeof("0 ") - 1 + ttl.len + sizeof(" ") - 1 + ++ ll + sizeof(CRLF) - 1 + value.len + sizeof(CRLF) - 1; ++ break; ++ case mcreq_incr: ++ l = sizeof("incr ") - 1 + key.len + sizeof(" ") - 1 + value.len + sizeof(CRLF) - 1; ++ break; ++ case mcreq_decr: ++ l = sizeof("decr ") - 1 + key.len + sizeof(" ") - 1 + value.len + sizeof(CRLF) - 1; ++ break; ++ case mcreq_delete: ++ l = sizeof("delete ") - 1 + key.len + sizeof(CRLF) - 1; ++ break; ++ default: ++ ngx_log_error (NGX_LOG_ERR, log, 0, ++ "unkown command for the memcache key: %V", &key); ++ ngx_str_null(&pdu); ++ return pdu; ++ } ++ ++ pdu.data = ngx_palloc(pool, l); ++ ++ if (pdu.data == NULL) { ++ ngx_str_null(&pdu); ++ return pdu; ++ } ++ ++ p = pdu.data; ++ ++ switch (w->request_code) { ++ case mcreq_get: ++ ngx_sprintf(p, "get %V" CRLF, &key); ++ break; ++ case mcreq_add: ++ ngx_sprintf(p, "add %V 0 %V %d" CRLF "%V" CRLF, &key, ++ &ttl, value.len, &value); ++ break; ++ case mcreq_incr: ++ ngx_sprintf(p, "incr %V %V" CRLF, &key, &value); ++ break; ++ case mcreq_decr: ++ ngx_sprintf(p, "decr %V %V" CRLF, &key, &value); ++ break; ++ case mcreq_delete: ++ ngx_sprintf(p, "delete %V" CRLF, &key); ++ break; ++ default: ++ /* impossible to reach here */ ++ return pdu; ++ } ++ ++ pdu.len = l; ++ ++ ngx_log_debug1(NGX_LOG_DEBUG_ZIMBRA, log, 0, ++ "generate pdu %V", &pdu); ++ ++ return pdu; ++} +diff -urN nginx/src/core/ngx_memcache.h nginx/src/core/ngx_memcache.h +--- nginx/src/core/ngx_memcache.h 1970-01-01 05:30:00.000000000 +0530 ++++ nginx/src/core/ngx_memcache.h 2023-03-04 13:25:59.825361500 +0530 +@@ -0,0 +1,157 @@ ++/* ++ * ***** BEGIN LICENSE BLOCK ***** ++ * Zimbra Collaboration Suite Server ++ * Copyright (C) 2011 Zimbra Software, LLC. ++ * ++ * The contents of this file are subject to the Zimbra Public License ++ * Version 1.4 ("License"); you may not use this file except in ++ * compliance with the License. You may obtain a copy of the License at ++ * http://www.zimbra.com/license. ++ * ++ * Software distributed under the License is distributed on an "AS IS" ++ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. ++ * ***** END LICENSE BLOCK ***** ++ */ ++ ++#ifndef _NGX_MEMCACHE_H_INCLUDED_ ++#define _NGX_MEMCACHE_H_INCLUDED_ ++ ++#include ++#include ++#include ++#include ++ ++#define NGX_MEMCACHE_CONF 0x02000000 ++#define NGX_MAX_KEY_LENGTH 250 ++ ++typedef struct { ++ ngx_pool_t *cpool; /* main pool where self resides */ ++ ngx_log_t *log; ++ ngx_array_t contexts; /* mc_context_t[] */ ++ ngx_array_t servers; /* ngx_addr_t*[] */ ++ ngx_msec_t timeout; ++ ngx_msec_t reconnect; ++ ngx_msec_t ttl; ++ ngx_str_t ttl_text; /* in second, ttl / 1000 */ ++ ngx_flag_t allow_unqualified; ++} ngx_memcache_conf_t; ++ ++/* supported memcache request codes */ ++typedef enum { ++ mcreq_noop, ++ mcreq_get, ++ mcreq_add, ++ mcreq_delete, ++ mcreq_incr, ++ mcreq_decr ++} mc_request_code_t; ++ ++/* memcache response codes */ ++typedef enum { ++ mcres_unknown, ++ mcres_success, ++ mcres_failure_normal, /* memcached server response with failure info, ++ such as NOT_FOUND, NOT_STORED, ... */ ++ mcres_failure_input, /* failures triggered by the user input, such as ++ invalid key or command */ ++ mcres_failure_again, /* failures that might be recovered by retry, such as ++ one of many memcached servers is down; or the ++ message replied can't be parsed correctly */ ++ mcres_failure_unavailable /* failures of no memcache servers are available, ++ or other failures that make memcache service ++ unavailable */ ++} mc_response_code_t; ++ ++/* memcache channel status */ ++typedef enum { ++ mcchan_good, ++ mcchan_bad, ++ mcchan_reconnect /* temp status in reconnection */ ++} mc_channel_status_t; ++ ++/* additional data returned by a memcache operation */ ++typedef ngx_str_t mc_data_t; ++ ++struct mc_work_s; ++/* prototype for sucess/failure handler */ ++typedef void (*mc_chain_handler) (struct mc_work_s *w); ++ ++/* workqueue entry representing an outstanding memcache request ++ */ ++struct mc_work_s ++{ ++ mc_request_code_t request_code; /* op request code */ ++ mc_response_code_t response_code; /* op response status */ ++ void *ctx; /* op context */ ++ mc_data_t payload; /* op response payload */ ++ mc_chain_handler on_success; /* success handler */ ++ mc_chain_handler on_failure; /* failure handler */ ++}; ++ ++typedef struct mc_work_s mc_work_t; ++ ++ ++/* a queue of memcache entries representing all outstanding memcache ++ requests for a particular connection to a memcache server ++ */ ++ ++typedef struct mc_workqueue_s mc_workqueue_t; ++ ++struct mc_workqueue_s { ++ mc_work_t w; /* payload of the workqueue entry */ ++ ngx_pool_t *pool; /* pool in which this node resides */ ++ ngx_flag_t reclaim; /* reclaim the pool? (default:0) */ ++ mc_workqueue_t *prev; /* previous node in the queue */ ++ mc_workqueue_t *next; /* next node in the queue */ ++}; ++ ++/* a `memcache context' data structure that completely represents the ++ state of a particular connection to a memcached server ++ */ ++ ++typedef struct { ++ ngx_buf_t *readbuffer; /* circular buffer for recv() */ ++ mc_workqueue_t wq_head; /* head of outstanding requests */ ++ ngx_addr_t *srvaddr; /* address of memcached server */ ++ ngx_peer_connection_t *srvconn; /* active connection to server */ ++ mc_channel_status_t status; /* connection status */ ++ ngx_msec_t timeout; /* read/write timeout */ ++ ngx_msec_t cxn_interval; /* timeout for reconnection */ ++ ngx_event_t *reconnect_ev; /* event for reconnection */ ++ ngx_atomic_t lock; /* concurrent access lock */ ++} mc_context_t; ++ ++/* Functions to manipulate the memcache work queue */ ++mc_workqueue_t *ngx_memcache_wq_enqueue (mc_workqueue_t *head, mc_workqueue_t *wq); ++mc_workqueue_t *ngx_memcache_wq_dequeue (mc_workqueue_t *head); ++ ++/* Post a memcache operation request onto an available channel */ ++void ngx_memcache_post ( ++ mc_work_t *w, ++ ngx_str_t key, ++ ngx_str_t value, ++ ngx_pool_t *p, ++ ngx_log_t *l ++ ); ++ ++/* ++ * Post a memcache operation request onto an available channel ++ * custom ttl. (Only "add" is supported) ++ */ ++void ngx_memcache_post_with_ttl ( ++ mc_work_t *w, ++ ngx_str_t key, ++ ngx_str_t value, ++ ngx_str_t ttl, ++ ngx_pool_t *p, ++ ngx_log_t *l ++ ); ++ ++extern ngx_str_t NGX_EMPTY_STR; /* for dummy value */ ++ ++/* Ignore a work entry in the queue whose ctx is equal to ++ * the specified one. ++ */ ++void ngx_memcache_ignore_work_by_ctx (void * ctx); ++ ++#endif +diff -urN nginx/src/core/ngx_palloc.c nginx/src/core/ngx_palloc.c +--- nginx/src/core/ngx_palloc.c 2023-03-07 16:26:44.281516600 +0530 ++++ nginx/src/core/ngx_palloc.c 2023-09-14 18:47:11.690205600 +0530 +@@ -70,9 +70,10 @@ + } + + for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) { +- ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, pool->log, 0, +- "free: %p, unused: %uz", p, p->d.end - p->d.last); +- ++// Zimbra customizations start here (Jira Tickets: ) ++// ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, pool->log, 0, ++// "free: %p, unused: %uz", p, p->d.end - p->d.last); ++// Zimbra customizations end here + if (n == NULL) { + break; + } +diff -urN nginx/src/core/ngx_string.c nginx/src/core/ngx_string.c +--- nginx/src/core/ngx_string.c 2023-07-23 13:21:26.899184500 +0530 ++++ nginx/src/core/ngx_string.c 2023-09-14 18:47:11.725602200 +0530 +@@ -85,8 +85,26 @@ + + return dst; + } ++// Zimbra customizations start here (Jira Tickets: ) ++ngx_str_t * ++ngx_pstrcpy(ngx_pool_t *pool, ngx_str_t *src) { ++ ngx_str_t * dst; + ++ dst = ngx_pnalloc(pool, sizeof(ngx_str_t)); ++ if (dst == NULL) { ++ return NULL; ++ } + ++ dst->data = ngx_pnalloc(pool, src->len); ++ if (dst->data == NULL) { ++ return NULL; ++ } ++ ++ ngx_memcpy(dst->data, src->data, src->len); ++ dst->len = src->len; ++ return dst; ++} ++// Zimbra customizations end here + /* + * supported formats: + * %[0][width][x][X]O off_t +diff -urN nginx/src/core/ngx_string.h nginx/src/core/ngx_string.h +--- nginx/src/core/ngx_string.h 2023-07-23 13:21:26.903174100 +0530 ++++ nginx/src/core/ngx_string.h 2023-10-01 22:17:25.661131500 +0530 +@@ -150,6 +150,9 @@ + + u_char *ngx_cpystrn(u_char *dst, u_char *src, size_t n); + u_char *ngx_pstrdup(ngx_pool_t *pool, ngx_str_t *src); ++// Zimbra customizations start here (Jira Tickets: ) ++ngx_str_t *ngx_pstrcpy(ngx_pool_t *pool, ngx_str_t *src); ++// Zimbra customizations end here + u_char * ngx_cdecl ngx_sprintf(u_char *buf, const char *fmt, ...); + u_char * ngx_cdecl ngx_snprintf(u_char *buf, size_t max, const char *fmt, ...); + u_char * ngx_cdecl ngx_slprintf(u_char *buf, u_char *last, const char *fmt, +diff -urN nginx/src/core/ngx_zm_lookup.c nginx/src/core/ngx_zm_lookup.c +--- nginx/src/core/ngx_zm_lookup.c 1970-01-01 05:30:00.000000000 +0530 ++++ nginx/src/core/ngx_zm_lookup.c 2023-03-04 13:25:59.839357600 +0530 +@@ -0,0 +1,2282 @@ ++/* ++ * ***** BEGIN LICENSE BLOCK ***** ++ * Zimbra Collaboration Suite Server ++ * Copyright (C) 2011 Zimbra Software, LLC. ++ * ++ * The contents of this file are subject to the Zimbra Public License ++ * Version 1.4 ("License"); you may not use this file except in ++ * compliance with the License. You may obtain a copy of the License at ++ * http://www.zimbra.com/license. ++ * ++ * Software distributed under the License is distributed on an "AS IS" ++ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. ++ * ***** END LICENSE BLOCK ***** ++ */ ++ ++#include ++#include ++ ++static char * ngx_zm_lookup_handlers(ngx_conf_t *cf, ngx_command_t *cmd, ++ void *conf); ++ ++/* lookup from memcache */ ++static void ngx_zm_lookup_account_from_cache(ngx_zm_lookup_ctx_t *ctx); ++static void ngx_zm_lookup_account_from_cache_success_handler(mc_work_t *work); ++static void ngx_zm_lookup_account_from_cache_failure_handler(mc_work_t *work); ++static void ngx_zm_lookup_route_from_cache(ngx_zm_lookup_ctx_t *ctx); ++static void ngx_zm_lookup_route_from_cache_success_handler(mc_work_t *work); ++static void ngx_zm_lookup_route_from_cache_failure_handler(mc_work_t *work); ++static void ngx_zm_lookup_cache_alias(ngx_zm_lookup_ctx_t *ctx, ++ ngx_str_t alias, ngx_str_t account_name); ++static void ngx_zm_lookup_cache_route(ngx_zm_lookup_ctx_t *ctx, ++ ngx_str_t user, ngx_str_t route); ++static void ngx_zm_lookup_cache_dummy_handler(mc_work_t *work); ++static void ngx_zm_lookup_delete_cache_handler(mc_work_t *work); ++ ++/* memcache key creation */ ++static ngx_str_t ++ngx_zm_lookup_get_user_route_key(ngx_pool_t *pool, ngx_log_t *log, ++ ngx_str_t proto, ngx_str_t account_name, ngx_str_t client_ip); ++static ngx_str_t ++ngx_zm_lookup_get_id_route_key(ngx_pool_t *pool, ngx_log_t *log, ngx_str_t proto, ++ ngx_str_t id, ngx_http_zmauth_t type); ++ ++/* lookup from route lookup servlet */ ++static void ngx_zm_lookup_dummy_handler(ngx_event_t *ev); ++static void ngx_zm_lookup_connect_handler(ngx_event_t *ev); ++static void ngx_zm_lookup_connect(ngx_zm_lookup_ctx_t * ctx); ++static ngx_int_t ngx_zm_lookup_parse_response_headers(ngx_zm_lookup_ctx_t * ctx); ++static ngx_int_t ngx_zm_lookup_create_request(ngx_zm_lookup_ctx_t *ctx); ++static ngx_str_t ngx_zm_lookup_get_local_socket_addr_text (ngx_pool_t *pool, ++ ngx_socket_t s); ++static void ngx_zm_lookup_process_response(ngx_zm_lookup_ctx_t *ctx); ++static void ngx_zm_lookup_process_response_headers(ngx_zm_lookup_ctx_t *ctx); ++static void ngx_zm_lookup_send_request_handler(ngx_event_t *wev); ++static void ngx_zm_lookup_recv_response_handler(ngx_event_t *rev); ++static ngx_int_t ngx_zm_lookup_retrieve_route(ngx_pool_t * pool, ++ ngx_str_t * addr_text, ngx_str_t * port_text, ngx_addr_t * route); ++static void ngx_zm_lookup_close_connection(ngx_zm_lookup_ctx_t * ctx); ++ ++/* module configuration */ ++static void * ngx_zm_lookup_create_conf(ngx_cycle_t *cycle); ++static char * ngx_zm_lookup_init_conf(ngx_cycle_t *cycle, void *conf); ++static char *ngx_zm_lookup_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); ++ ++/* utility */ ++static const u_char * ngx_zm_strchr (ngx_str_t str, int chr); ++ ++static ngx_command_t ngx_zm_lookup_commands[] = ++{ ++ { ngx_string("zm_lookup"), ++ NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS, ++ ngx_zm_lookup_block, ++ 0, ++ 0, ++ NULL }, ++ ++ { ngx_string("zm_lookup_handlers"), ++ NGX_ZM_LOOKUP_CONF|NGX_DIRECT_CONF|NGX_CONF_1MORE, ++ ngx_zm_lookup_handlers, ++ 0, ++ 0, ++ NULL }, ++ ++ { ngx_string("zm_lookup_handler_retry_interval"), ++ NGX_ZM_LOOKUP_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1, ++ ngx_conf_set_msec_slot, ++ 0, ++ offsetof(ngx_zm_lookup_conf_t, retry_interval), ++ NULL }, ++ ++ { ngx_string("zm_lookup_timeout"), ++ NGX_ZM_LOOKUP_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1, ++ ngx_conf_set_msec_slot, ++ 0, ++ offsetof(ngx_zm_lookup_conf_t, timeout), ++ NULL }, ++ ++ { ngx_string("zm_lookup_buffer_size"), ++ NGX_ZM_LOOKUP_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1, ++ ngx_conf_set_size_slot, ++ 0, ++ offsetof(ngx_zm_lookup_conf_t, buffer_size), ++ NULL }, ++ ++ { ngx_string("zm_lookup_master_auth_username"), ++ NGX_ZM_LOOKUP_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1, ++ ngx_conf_set_str_slot, ++ 0, ++ offsetof(ngx_zm_lookup_conf_t, master_auth_username), ++ NULL }, ++ ++ { ngx_string("zm_lookup_master_auth_password"), ++ NGX_ZM_LOOKUP_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1, ++ ngx_conf_set_str_slot, ++ 0, ++ offsetof(ngx_zm_lookup_conf_t, master_auth_password), ++ NULL }, ++ ++ { ngx_string("zm_lookup_caching"), ++ NGX_ZM_LOOKUP_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1, ++ ngx_conf_set_flag_slot, ++ 0, ++ offsetof(ngx_zm_lookup_conf_t, caching), ++ NULL }, ++ ++ { ngx_string("zm_lookup_allow_unqualified"), ++ NGX_ZM_LOOKUP_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1, ++ ngx_conf_set_flag_slot, ++ 0, ++ offsetof(ngx_zm_lookup_conf_t, allow_unqualified), ++ NULL }, ++ ++ { ngx_string("zm_prefix_url"), ++ NGX_ZM_LOOKUP_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1, ++ ngx_conf_set_str_slot, ++ 0, ++ offsetof(ngx_zm_lookup_conf_t, url), ++ NULL }, ++ ++ ngx_null_command ++}; ++ ++static ngx_core_module_t ngx_zm_lookup_module_ctx = ++{ ++ ngx_string("ngx_zm_lookup"), ++ ngx_zm_lookup_create_conf, ++ ngx_zm_lookup_init_conf ++}; ++ ++/*static const u_char * LOGIN_FAILED = (u_char *)"login failed";*/ ++ ++static const ngx_str_t ZM_PROTO[] = { ++ ngx_string("unknown"), ++ ngx_string("http"), ++ ngx_string("httpssl"), ++ ngx_string("pop3"), ++ ngx_string("pop3ssl"), ++ ngx_string("imap"), ++ ngx_string("imapssl") ++}; ++ ++static const ngx_str_t ZM_AUTHMETH[] = { ++ ngx_string("username"), ++ ngx_string("gssapi"), ++ ngx_string("zimbraId"), ++ ngx_string("certauth") ++}; ++ ++static const ngx_str_t ERRMSG[] = { ++ ngx_string("success"), ++ ngx_string("mem alloc err"), ++ ngx_string("error occurs when writing lookup request to handler"), ++ ngx_string("error occurs when reading lookup response from handler"), ++ ngx_string("timeout occurs when writing lookup request to handler"), ++ ngx_string("timeout occurs when reading lookup response from handler"), ++ ngx_string("no valid lookup handlers"), ++ ngx_string("invalid route is returned"), ++ ngx_string("LOGIN failed"), ++ ngx_string("invalid response from lookup handler"), ++ ngx_string("client connection is closed"), ++ ngx_string("dummy") ++}; ++ ++/*There is no need to send real password for now */ ++static const ngx_str_t ++ngx_zm_lookup_password_placeholder = ngx_string("_password_"); ++ ++static const ngx_str_t ++ngx_zm_prefix_url = ngx_string("/"); ++ ++ngx_module_t ngx_zm_lookup_module = ++{ ++ NGX_MODULE_V1, ++ &ngx_zm_lookup_module_ctx, /* module context */ ++ ngx_zm_lookup_commands, /* module directives */ ++ NGX_CORE_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 char *ngx_zm_lookup_block(ngx_conf_t *cf, ngx_command_t *cmd, ++ void *conf) ++{ ++ ngx_conf_t ocf; ++ char *rc; ++ ++ ocf = *cf; ++ ++ cf->ctx = cf->cycle->conf_ctx; ++ cf->module_type = NGX_CORE_MODULE; ++ cf->cmd_type = NGX_ZM_LOOKUP_CONF; ++ ++ rc = ngx_conf_parse(cf, NULL); ++ ++ *cf = ocf; ++ ++ return rc; ++} ++ ++static void * ++ngx_zm_lookup_create_conf(ngx_cycle_t *cycle) ++{ ++ ngx_zm_lookup_conf_t *zlcf; ++ ngx_pool_t *pool; ++ ngx_log_t *log; ++ ++ log = cycle->log; ++ pool = ngx_create_pool (8 * ngx_pagesize, cycle->log); ++ ++ zlcf = ngx_pcalloc (pool, sizeof(ngx_zm_lookup_conf_t)); ++ if (zlcf == NULL) { ++ return NGX_CONF_ERROR; ++ } ++ ++ zlcf->pool = pool; ++ zlcf->log = log; ++ ++#if (NGX_SSL) ++ ++ ngx_pool_cleanup_t *cln; ++ zlcf->ssl = ngx_pcalloc(pool, sizeof(ngx_ssl_t)); ++ ++ if (zlcf->ssl == NULL) { ++ return NGX_CONF_ERROR; ++ } ++ ++ zlcf->ssl->log = log; ++ ++ // don't support SSLv2 anymore ++ if (ngx_ssl_create(zlcf->ssl, ~(NGX_SSL_SSLv2|NGX_SSL_SSLv3), NULL) ++ != NGX_OK) { ++ return NGX_CONF_ERROR; ++ } ++ ++ cln = ngx_pool_cleanup_add(zlcf->pool, 0); ++ if (cln == NULL) { ++ return NGX_CONF_ERROR; ++ } ++ ++ cln->handler = ngx_ssl_cleanup_ctx; ++ cln->data = zlcf->ssl; ++ ++#endif ++ ++ if(ngx_array_init (&zlcf->handlers, zlcf->pool, 4, ++ sizeof(ngx_zm_lookup_handler_t)) != NGX_OK) { ++ return NGX_CONF_ERROR; ++ } ++ ++ zlcf->retry_interval = NGX_CONF_UNSET; ++ zlcf->buffer_size = NGX_CONF_UNSET_SIZE; ++ zlcf->timeout = NGX_CONF_UNSET; ++ zlcf->caching = NGX_CONF_UNSET; ++ zlcf->allow_unqualified = NGX_CONF_UNSET; ++ ngx_str_null(&zlcf->master_auth_username); ++ ngx_str_null(&zlcf->master_auth_password); ++ ngx_str_null(&zlcf->url); ++ ngx_log_error(NGX_LOG_DEBUG_ZIMBRA, cycle->log, 0, ++ "zm lookup configuration created"); ++ return zlcf; ++} ++ ++static char * ++ngx_zm_lookup_init_conf(ngx_cycle_t *cycle, void *conf) ++{ ++ ngx_zm_lookup_conf_t *zlcf = conf; ++ ++ // set default value of timeout ++ if (zlcf->retry_interval == NGX_CONF_UNSET_UINT) { ++ zlcf->retry_interval = 60000; ++ } ++ ++ if (zlcf->caching == NGX_CONF_UNSET) { ++ zlcf->caching = 1; ++ } ++ ++ if (zlcf->allow_unqualified == NGX_CONF_UNSET) { ++ zlcf->allow_unqualified = 0; ++ } ++ ++ if (zlcf->buffer_size == NGX_CONF_UNSET_SIZE) { ++ zlcf->buffer_size = 1024; ++ } ++ ++ if (zlcf->timeout == NGX_CONF_UNSET_UINT) { ++ zlcf->timeout = 15000; ++ } ++ ++ if (zlcf->master_auth_username.data == NULL) { ++ zlcf->master_auth_username = ngx_zm_lookup_password_placeholder; ++ } ++ ++ if (zlcf->master_auth_password.data == NULL) { ++ zlcf->master_auth_password = ngx_zm_lookup_password_placeholder; ++ } ++ ++ if (zlcf->url.data == NULL) { ++ zlcf->url = ngx_zm_prefix_url; ++ } ++ ++ ngx_log_error(NGX_LOG_DEBUG_ZIMBRA,cycle->log, 0, ++ "zm lookup - initialized config defaults"); ++ return NGX_CONF_OK; ++} ++ ++static char * ++ngx_zm_lookup_handlers(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) ++{ ++ ngx_zm_lookup_conf_t *zlcf = conf; ++ ngx_zm_lookup_handler_t *handler; ++ ngx_url_t u; ++ ngx_uint_t i; ++ ngx_uint_t add; ++ ++ /* parse each url specified against directive "zm_lookup_handlers" */ ++ for(i = 1; i < cf->args->nelts; ++i) ++ { ++ ngx_memzero(&u, sizeof(ngx_url_t)); ++ u.url = ((ngx_str_t*)cf->args->elts)[i]; ++ u.default_port = 7072; ++ u.uri_part = 1; ++ add = 0; ++ ++ if (ngx_strncasecmp(u.url.data, (u_char *) "http://", 7) == 0) { ++ add = 7; ++ } else if (ngx_strncasecmp(u.url.data, (u_char *) "https://", 8) == 0) { ++#if (NGX_SSL) ++ add = 8; ++#else ++ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, ++ "https protocol requires SSL support"); ++ return NGX_CONF_ERROR; ++#endif ++ } else { ++ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid URL prefix"); ++ return NGX_CONF_ERROR; ++ } ++ u.url.len -= add; ++ u.url.data += add; ++ ++ if (ngx_parse_url(cf->pool, &u) != NGX_OK) { ++ if (u.err) { ++ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, ++ "%s in zm lookup handlers \"%V\"", u.err, &u.url); ++ } ++ return NGX_CONF_ERROR; ++ } ++ ++ handler = ngx_array_push(&zlcf->handlers); ++ if (handler == NULL) { ++ return NGX_CONF_ERROR; ++ } ++ ++ handler->peer = u.addrs; ++ handler->host = u.host; ++ handler->uri = u.uri; ++ handler->failure_time = 0; ++ if (add == 7) { ++ handler->ssl = 0; ++ } else if (add == 8) { ++ handler->ssl = 1; ++ } ++ ++ ngx_conf_log_error(NGX_LOG_INFO, cf, 0, ++ "add zimbra route lookup handler %V", &u.url); ++ } ++ ++ if (zlcf->handlers.nelts == 0) { ++ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, ++ "zero valid zmauth route handlers"); ++ return NGX_CONF_ERROR; ++ } ++ ++ return NGX_CONF_OK; ++} ++ ++static ngx_int_t ++ngx_zm_lookup_elect_handler(ngx_zm_lookup_ctx_t *ctx, ngx_zm_lookup_conf_t *zlcf) ++{ ++ ngx_zm_lookup_handler_t * handler; ++ time_t now = time(NULL); ++ ++ if (zlcf->handlers.nelts == 0) { ++ return NGX_ERROR; ++ } ++ /* so the loop below can start from zlcf->handler_index rather than ++ zlcf->handler_index + 1 ++ */ ++ ctx->handler_index = zlcf->handler_index - 1; ++ zlcf->handler_index = (zlcf->handler_index + 1) % zlcf->handlers.nelts; ++ do { ++ if (ctx->tries >= zlcf->handlers.nelts) { ++ ngx_log_error (NGX_LOG_ERR, ctx->log, 0, ++ "All nginx lookup handlers are unavailable"); ++ return NGX_ERROR; ++ ++ } else { ++ ctx->handler_index = (ctx->handler_index + 1) % zlcf->handlers.nelts; ++ ctx->tries ++; ++ ++ handler = ((ngx_zm_lookup_handler_t*)zlcf->handlers.elts) + ctx->handler_index; ++ if (handler->failure_time != 0) { ++ if((now < handler->failure_time || ++ (now - handler->failure_time) < (time_t)(zlcf->retry_interval / 1000))) { ++ continue; ++ } else { ++ handler->failure_time = 0; // mark it as available and try to connect it ++ } ++ } ++ ctx->handler = handler; ++ break; ++ } ++ } while (1); ++ ++ return NGX_OK; ++} ++ ++/* portal API */ ++void ++ngx_zm_lookup(ngx_zm_lookup_work_t * work) ++{ ++ ngx_zm_lookup_ctx_t *ctx; ++ ngx_zm_lookup_conf_t *zlcf; ++ ++ zlcf = (ngx_zm_lookup_conf_t *) ++ ngx_get_conf (ngx_cycle->conf_ctx, ngx_zm_lookup_module); ++ ++ ctx = ngx_pcalloc(work->pool, sizeof(ngx_zm_lookup_ctx_t)); ++ if (ctx == NULL) { ++ work->result = ZM_LOOKUP_MEM_ALLOC_ERROR; ++ work->err = ERRMSG[ZM_LOOKUP_MEM_ALLOC_ERROR]; ++ work->on_failure(work); ++ return; ++ } ++ ctx->pool = work->pool; ++ ctx->log = work->log; ++ ctx->work = work; ++ ctx->tries = 0; ++ ctx->handler = NULL; ++ ctx->state = 0; ++ work->ctx = ctx; ++ ++ if (ctx->work->auth_method == ZM_AUTHMETH_GSSAPI || ++ ctx->work->auth_method == ZM_AUTHMETH_CERTAUTH) { ++ /* These methods should never be cached */ ++ ngx_zm_lookup_connect(ctx); ++ return; ++ } ++ ++ if (zlcf->caching) { ++ ++ if (work->auth_method == ZM_AUTHMETH_ZIMBRAID) { ++ ngx_zm_lookup_route_from_cache(ctx); ++ return; ++ } ++ ++ if (work->alias_check_stat == ZM_ALIAS_NOT_CHECKED) { ++ ngx_zm_lookup_account_from_cache(ctx); ++ } else { ++ ngx_zm_lookup_route_from_cache(ctx); ++ } ++ ++ } else { ++ ngx_zm_lookup_connect(ctx); ++ } ++} ++ ++/* ++ * Consider current user name is an alias and lookup its ++ * corresponding account name, such 'john' --> 'john@test.com' ++ */ ++static void ++ngx_zm_lookup_account_from_cache (ngx_zm_lookup_ctx_t * ctx) ++{ ++ ngx_zm_lookup_work_t *work; ++ ngx_str_t key; ++ ngx_log_t *log; ++ mc_work_t mc; ++ ngx_pool_t *pool; ++ ++ log = ctx->log; ++ pool = ctx->pool; ++ work = ctx->work; ++ ++ if (work->alias_key.len > 0) { ++ key = work->alias_key; ++ } else { ++ if (IS_PROTO_WEB(work->protocol)) { ++ key = ngx_zm_lookup_get_http_alias_key ++ (pool, log, work->username, work->virtual_host); ++ } else { ++ key = ngx_zm_lookup_get_mail_alias_key ++ (pool, log, work->username, work->connection->addr_text); ++ } ++ ++ if (key.len == 0) { /* NOMEM */ ++ work->result = ZM_LOOKUP_MEM_ALLOC_ERROR; ++ work->err = ERRMSG[ZM_LOOKUP_MEM_ALLOC_ERROR]; ++ work->on_failure(work); ++ return; ++ } ++ ++ work->alias_key = key; ++ } ++ ++ mc.ctx = ctx; ++ mc.request_code = mcreq_get; ++ mc.response_code = mcres_unknown; ++ mc.on_success = ngx_zm_lookup_account_from_cache_success_handler; ++ mc.on_failure = ngx_zm_lookup_account_from_cache_failure_handler; ++ ++ ctx->wait_memcache = 1; ++ ngx_memcache_post(&mc, key, NGX_EMPTY_STR,/* pool */ NULL, log); ++} ++ ++static void ++ngx_zm_lookup_account_from_cache_success_handler (mc_work_t *mc) ++{ ++ ngx_zm_lookup_ctx_t *ctx; ++ ngx_str_t account_name; ++ ++ ctx = (ngx_zm_lookup_ctx_t *)mc->ctx; ++ ctx->wait_memcache = 0; ++ ++ account_name.data = ngx_pstrdup(ctx->pool, &mc->payload); ++ if (account_name.data != NULL) ++ { ++ account_name.len = mc->payload.len; ++ ctx->work->account_name = account_name; ++ ++ ngx_log_debug2 (NGX_LOG_DEBUG_ZIMBRA, ctx->log, 0, ++ "zm lookup: user:%V aliased to account name:%V", ++ &ctx->work->username, &account_name ++ ); ++ ++ ctx->work->alias_check_stat = ZM_ALIAS_FOUND; ++ ngx_zm_lookup_route_from_cache(ctx); ++ } ++} ++ ++static void ++ngx_zm_lookup_account_from_cache_failure_handler(mc_work_t *work) ++{ ++ ngx_zm_lookup_ctx_t *ctx; ++ ++ ctx = (ngx_zm_lookup_ctx_t *)work->ctx; ++ ctx->wait_memcache = 0; ++ ctx->work->alias_check_stat = ZM_ALIAS_NOT_FOUND; ++ /* If account name is not found, take username as the account name */ ++ ctx->work->account_name = ctx->work->username; ++ ++ ngx_zm_lookup_route_from_cache(ctx); ++} ++ ++/* lookup route by zimbra id/account name in memcache */ ++static void ++ngx_zm_lookup_route_from_cache (ngx_zm_lookup_ctx_t *ctx) ++{ ++ ngx_pool_t *pool; ++ ngx_log_t *log; ++ ngx_str_t key; ++ mc_work_t mc; ++ ngx_zm_lookup_work_t *work; ++ ngx_zm_lookup_conf_t *zlcf; ++ ngx_str_t username; ++ ++ zlcf = (ngx_zm_lookup_conf_t *) ++ ngx_get_conf(ngx_cycle->conf_ctx, ngx_zm_lookup_module); ++ ++ pool = ctx->pool; ++ log = ctx->log; ++ work = ctx->work; ++ ++ if (work->auth_method == ZM_AUTHMETH_ZIMBRAID) { ++ key = ngx_zm_lookup_get_id_route_key( ++ pool, log, ZM_PROTO[work->protocol], work->username, ++ work->type); ++ } else { ++ if (work->alias_check_stat == ZM_ALIAS_FOUND) { ++ username = work->account_name; ++ } else { ++ username = work->username; ++ } ++ ++ if (zlcf->allow_unqualified == 0 && !is_login_qualified(username)) { ++ key = ngx_zm_lookup_get_user_route_key(pool, log, ZM_PROTO[work->protocol], ++ username, work->connection->addr_text); ++ } else { ++ key = ngx_zm_lookup_get_user_route_key(pool, log, ZM_PROTO[work->protocol], ++ username, NGX_EMPTY_STR); ++ } ++ } ++ ++ if (key.len == 0) { /* NOMEM */ ++ work->result = ZM_LOOKUP_MEM_ALLOC_ERROR; ++ work->err = ERRMSG[ZM_LOOKUP_MEM_ALLOC_ERROR]; ++ work->on_failure(work); ++ return; ++ } ++ ++ ctx->work->route_key = key; ++ ++ mc.ctx = ctx; ++ mc.request_code = mcreq_get; ++ mc.response_code = mcres_unknown; ++ mc.on_success = ngx_zm_lookup_route_from_cache_success_handler; ++ mc.on_failure = ngx_zm_lookup_route_from_cache_failure_handler; ++ ++ ctx->wait_memcache = 1; ++ ngx_memcache_post(&mc, key, NGX_EMPTY_STR,/* pool */ NULL, log); ++} ++ ++static void ++ngx_zm_lookup_route_from_cache_success_handler (mc_work_t *mc) ++{ ++ ngx_zm_lookup_ctx_t *ctx; ++ ngx_str_t route_text; ++ ngx_url_t u; ++ ++ ctx = (ngx_zm_lookup_ctx_t *)mc->ctx; ++ ctx->wait_memcache = 0; ++ ++ route_text.data = ngx_pstrdup(ctx->pool, &mc->payload); ++ ++ if (route_text.data == NULL) { ++ ctx->work->result = ZM_LOOKUP_MEM_ALLOC_ERROR; ++ ctx->work->on_failure(ctx->work); ++ return; ++ } else { ++ route_text.len = mc->payload.len; ++ ++ ngx_log_debug2 (NGX_LOG_DEBUG_HTTP, ctx->log,0, ++ "zm lookup: fetch cached route:%V for user:%V", ++ &route_text, &ctx->work->username ++ ); ++ ++ ngx_memzero(&u, sizeof(u)); ++ u.url = route_text; ++ u.listen = 1; ++ ++ if (ngx_parse_url(ctx->pool, &u) != NGX_OK) { ++ ctx->work->result = ZM_LOOKUP_INVALID_ROUTE; ++ ctx->work->on_failure(ctx->work); ++ return; ++ } ++ ++ ctx->work->route = ngx_palloc(ctx->pool, sizeof(ngx_addr_t)); ++ if (ctx->work->route == NULL) { ++ ctx->work->result = ZM_LOOKUP_MEM_ALLOC_ERROR; ++ ctx->work->err = ERRMSG[ZM_LOOKUP_MEM_ALLOC_ERROR]; ++ ctx->work->on_failure(ctx->work); ++ return; ++ } ++ ctx->work->route->name = route_text; ++ ctx->work->route->socklen = u.socklen; ++ ctx->work->route->sockaddr = ngx_palloc(ctx->pool, u.socklen); ++ if(ctx->work->route->sockaddr == NULL) { ++ ctx->work->result = ZM_LOOKUP_MEM_ALLOC_ERROR; ++ ctx->work->err = ERRMSG[ZM_LOOKUP_MEM_ALLOC_ERROR]; ++ ctx->work->on_failure(ctx->work); ++ return; ++ } ++ ngx_memcpy(ctx->work->route->sockaddr, &u.sockaddr, u.socklen); ++ ctx->work->result = ZM_LOOKUP_SUCCESS; ++ ctx->work->on_success(ctx->work); ++ return; ++ } ++} ++ ++static void ++ngx_zm_lookup_route_from_cache_failure_handler(mc_work_t *mc) ++{ ++ ngx_zm_lookup_ctx_t *ctx; ++ ctx = (ngx_zm_lookup_ctx_t *) mc->ctx; ++ ctx->wait_memcache = 0; ++ ++ /* if alias-->account lookup succeeds, but route lookup fails, ++ * ignore the found account. Still use what user initial input ++ * do the route lookup. (bug 49283) ++ */ ++ if (ctx->work->alias_check_stat == ZM_ALIAS_FOUND) { ++ ctx->work->alias_check_stat = ZM_ALIAS_IGNORED; ++ } ++ ++ ngx_zm_lookup_connect (ctx); ++} ++ ++#if (NGX_SSL) ++ ++static void ++ngx_zm_lookup_ssl_handshake(ngx_connection_t *c) ++{ ++ ngx_zm_lookup_ctx_t *ctx = c->data; ++ ++ if (c->ssl->handshaked) { ++ c->read->handler = ngx_zm_lookup_dummy_handler; ++ c->write->handler = ngx_zm_lookup_connect_handler; ++ ngx_reusable_connection(c, 1); ++ c->write->handler(c->write); ++ ngx_log_debug1 (NGX_LOG_DEBUG_ZIMBRA, ctx->log, 0, ++ "zm lookup: ngx_zm_lookup_ssl_handshake set fd:%d", c->fd); ++ } else { ++ /* when handshake fails, we should close the session */ ++ ngx_zm_lookup_close_connection(ctx); ++ ngx_log_debug1 (NGX_LOG_DEBUG_ZIMBRA, ctx->log, 0, ++ "zm lookup: ngx_zm_lookup_ssl_handshake unset fd:%d", c->fd); ++ } ++} ++ ++static ngx_flag_t ++ngx_zm_lookup_ssl_init_connection(ngx_ssl_t* ssl, ngx_connection_t *c) ++{ ++ ngx_int_t rc; ++ ngx_int_t marker = 20; ++ ngx_zm_lookup_ctx_t *ctx = c->data; ++ ++ if (ngx_ssl_create_connection(ssl, c, ++ NGX_SSL_BUFFER|NGX_SSL_CLIENT) ++ != NGX_OK) ++ { ++ ngx_zm_lookup_close_connection(ctx); ++ return; ++ } ++ ++ c->log->action = "SSL handshaking to lookup handler"; ++ ++ do { ++ rc = ngx_ssl_handshake(c); ++ if(rc == NGX_AGAIN) ++ { ++ ngx_log_debug0 (NGX_LOG_DEBUG_ZIMBRA, c->log, 0, ++ "zm lookup: ngx_zm_lookup_ssl_init_connection ngx_ssl_handshake returned NGX_AGAIN"); ++ ngx_msleep(5); ++ } ++ else if (rc == NGX_ERROR) ++ { ++ ngx_log_debug0 (NGX_LOG_DEBUG_ZIMBRA, c->log, 0, ++ "zm lookup: ngx_zm_lookup_ssl_init_connection ssl event failed with NGX_ERROR"); ++ ngx_zm_lookup_ssl_handshake(c); ++ return ZM_LOOKUP_SSL_EVENT_FAILED; ++ } ++ }while (rc == NGX_AGAIN && --marker > 0); ++ ++ if( 0 == marker ) ++ { ++ ngx_log_debug0 (NGX_LOG_DEBUG_ZIMBRA, c->log, 0, ++ "zm lookup: ngx_zm_lookup_ssl_init_connection marker reached"); ++ ngx_zm_lookup_ssl_handshake(c); ++ return ZM_LOOKUP_SSL_EVENT_FAILED; ++ } ++ ++ ngx_log_debug0 (NGX_LOG_DEBUG_ZIMBRA, c->log, 0, ++ "zm lookup: ngx_zm_lookup_ssl_init_connection before call to ngx_zm_lookup_ssl_handshake"); ++ ngx_zm_lookup_ssl_handshake(c); ++ return ZM_LOOKUP_SSL_EVENT_SUCCESS; ++} ++ ++#endif ++ ++static void ++ngx_zm_lookup_connect (ngx_zm_lookup_ctx_t * ctx) ++{ ++ ngx_zm_lookup_conf_t *zlcf; ++ ngx_zm_lookup_handler_t *handler; ++ ngx_int_t rc; ++ ++ zlcf = (ngx_zm_lookup_conf_t *)ngx_get_conf(ngx_cycle->conf_ctx, ngx_zm_lookup_module); ++ ++ ngx_log_debug1 (NGX_LOG_DEBUG_ZIMBRA, ctx->log, 0, ++ "zm lookup: elected route handler #%d", ctx->handler_index); ++ if (ngx_zm_lookup_elect_handler(ctx, zlcf) != NGX_OK) { ++ ngx_log_error(NGX_LOG_ERR, ctx->log, 0, ++ "zm lookup: all lookup handlers exhausted"); ++ ctx->work->result = ZM_LOOKUP_NO_VALID_HANDLER; ++ ctx->work->err = ERRMSG[ZM_LOOKUP_NO_VALID_HANDLER]; ++ ctx->work->on_failure(ctx->work); ++ return; ++ } ++ ++ handler = ctx->handler; ++ ctx->peer.sockaddr = handler->peer->sockaddr; ++ ctx->peer.socklen = handler->peer->socklen; ++ ctx->peer.name = &handler->peer->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); ++ ++ if (rc == NGX_ERROR || rc == NGX_BUSY || rc == NGX_DECLINED) { ++ ngx_log_debug2(NGX_LOG_DEBUG_ZIMBRA, ctx->log, 0, ++ "zm lookup: connect to lookup handler failed, host:%V, uri:%V", ++ ctx->peer.name, &handler->uri); ++ ngx_zm_lookup_close_connection(ctx); ++ ++ /* try again */ ++ ngx_log_error(NGX_LOG_WARN, ctx->log, 0, "zm lookup: " ++ "connect lookup handle error, fail over to the next one"); ++ ngx_zm_lookup_connect(ctx); ++ return; ++ ++ } ++ ++ ctx->peer.connection->data = ctx; ++ ctx->peer.connection->pool = ctx->pool; ++ ctx->peer.connection->log = ctx->log; ++ ngx_add_timer(ctx->peer.connection->read, zlcf->timeout); ++ ngx_add_timer(ctx->peer.connection->write, zlcf->timeout); ++ ++#if (NGX_SSL) ++ ++ if (ctx->handler->ssl && ctx->peer.connection->ssl == NULL) { ++ if(ngx_zm_lookup_ssl_init_connection(zlcf->ssl, ctx->peer.connection) == ZM_LOOKUP_SSL_EVENT_FAILED) ++ { ++ ngx_log_error(NGX_LOG_WARN, ctx->log, 0, "zm lookup: ngx_zm_lookup_connect " ++ "connect lookup handle error for host:%V, uri:%V, fail over to the next one",ctx->peer.name, &handler->uri); ++ ngx_zm_lookup_connect(ctx); ++ } ++ return; ++ } ++ ++#endif ++ ++ ctx->peer.connection->read->handler = ngx_zm_lookup_dummy_handler; ++ ctx->peer.connection->write->handler = ngx_zm_lookup_connect_handler; ++} ++ ++static void ++ngx_zm_lookup_dummy_handler(ngx_event_t *ev) ++{ ++ ngx_log_debug0(NGX_LOG_DEBUG_ZIMBRA, ev->log, 0, ++ "ngx_zm_lookup_dummy_handler()"); ++} ++ ++static void ++ngx_zm_lookup_connect_handler(ngx_event_t *ev) ++{ ++ ngx_connection_t *c; ++ ngx_zm_lookup_ctx_t *ctx; ++ int sockerr; ++ socklen_t sockerr_len; ++ struct timeval tv; ++ ++ c = ev->data; ++ ctx = c->data; ++ ++ /* Add health checked as auth http? */ ++ ++ sockerr = 0; ++ sockerr_len = sizeof(sockerr); ++ getsockopt(c->fd, SOL_SOCKET, SO_ERROR, &sockerr, &sockerr_len); ++ ++ if(sockerr == EINPROGRESS) { ++ /* expect to be reinvoked */ ++ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, ++ "zm lookup: connect to route handler in progress"); ++ return; ++ } else if (sockerr != 0) { ++ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, ++ "zm lookup: connect to route handler error:%d, will re-elect", ++ sockerr); ++ ngx_gettimeofday(&tv); ++ ctx->handler->failure_time = tv.tv_sec; ++ ngx_close_connection(c); ++ ctx->peer.connection = NULL; ++ ++ /* try again */ ++ ngx_log_error(NGX_LOG_WARN, c->log, sockerr, "zm lookup: " ++ "connect lookup handle error, fail over to the next one"); ++ ngx_zm_lookup_connect(ctx); ++ return; ++ } else { ++ ++ if (ngx_zm_lookup_create_request(ctx) != NGX_OK) { ++ ctx->work->result = ZM_LOOKUP_MEM_ALLOC_ERROR; ++ ctx->work->err = ERRMSG[ZM_LOOKUP_MEM_ALLOC_ERROR]; ++ ctx->work->on_failure(ctx->work); ++ return; ++ } ++ ++ ngx_log_debug0(NGX_LOG_DEBUG_ZIMBRA, c->log, 0, ++ "zm lookup: begin zm lookup"); ++ ctx->peer.connection->write->handler = ngx_zm_lookup_send_request_handler; ++ ctx->peer.connection->read->handler = ngx_zm_lookup_recv_response_handler; ++ ctx->lookup_response_handler = ngx_zm_lookup_process_response; ++ ctx->peer.connection->write->handler(ctx->peer.connection->write); ++ return; ++ } ++} ++ ++static void ++ngx_zm_lookup_send_request_handler(ngx_event_t *wev) ++{ ++ ngx_connection_t *c; ++ ngx_zm_lookup_ctx_t *ctx; ++ ngx_zm_lookup_work_t *work; ++ ngx_zm_lookup_conf_t *zlcf; ++ ssize_t size, n; ++ ++ c = wev->data; ++ ctx = c->data; ++ work = ctx->work; ++ zlcf = (ngx_zm_lookup_conf_t *)ngx_get_conf(ngx_cycle->conf_ctx, ngx_zm_lookup_module); ++ ++ if (wev->timedout) { ++ ngx_log_error(NGX_LOG_ERR, wev->log, NGX_ETIMEDOUT, ++ "zm lookup: route handler timed out, failing over to the next one"); ++ ngx_zm_lookup_close_connection(ctx); ++ ++ /* try again */ ++ ngx_zm_lookup_connect(ctx); ++ return; ++ } ++ ++ size = ctx->lookup_req->last - ctx->lookup_req->pos; ++ ++ n = c->send(c, ctx->lookup_req->pos, size); ++ ++ if (n == NGX_ERROR) { ++ work->result = ZM_LOOKUP_WRITE_ERROR; ++ ngx_zm_lookup_close_connection(ctx); ++ work->on_failure(work); ++ return; ++ } ++ if (n > 0) { ++ ctx->lookup_req->pos += n; ++ if (n == size) { ++ wev->handler = ngx_zm_lookup_dummy_handler; ++ if (wev->timer_set) { ++ ngx_del_timer(wev); ++ } ++ if (ngx_handle_write_event(wev, 0) == NGX_ERROR) { ++ work->result = ZM_LOOKUP_WRITE_ERROR; ++ ngx_zm_lookup_close_connection(ctx); ++ work->on_failure(work); ++ return; ++ } ++ } ++ } ++ if (!wev->timer_set) { ++ ngx_add_timer(wev, zlcf->timeout); ++ } ++} ++ ++static ngx_int_t ++ngx_zm_lookup_create_request(ngx_zm_lookup_ctx_t *ctx) ++{ ++ ngx_buf_t *b; ++ size_t len; ++ ngx_pool_t *pool; ++ ngx_zm_lookup_conf_t *zlcf; ++ ngx_zm_lookup_work_t *work; ++ ngx_str_t proxy_ip, username; ++ zlcf = (ngx_zm_lookup_conf_t *)ngx_get_conf(ngx_cycle->conf_ctx, ngx_zm_lookup_module); ++ pool = ctx->pool; ++ work = ctx->work; ++ ++ if(work->alias_check_stat == ZM_ALIAS_FOUND) { ++ username = work->account_name; ++ } else { ++ username = work->username; ++ } ++ ++ proxy_ip = ngx_zm_lookup_get_local_socket_addr_text (pool, work->connection->fd); ++ ++ len = sizeof("GET ") - 1 + ctx->handler->uri.len + sizeof(" HTTP/1.0" CRLF) - 1 ++ + sizeof("Host: ") - 1 + ctx->handler->host.len + sizeof(CRLF) - 1 ++ + sizeof("Auth-Method: ") - 1 + ZM_AUTHMETH[work->auth_method].len + sizeof(CRLF) - 1 ++ + sizeof("Auth-User: ") - 1 + username.len + sizeof(CRLF) - 1 ++ + sizeof("Auth-Pass: ") - 1 + ngx_zm_lookup_password_placeholder.len + sizeof(CRLF) - 1 ++ + sizeof("Auth-Protocol: ") - 1 + ZM_PROTO[work->protocol].len + sizeof(CRLF) - 1 ++ + sizeof("Auth-Login-Attempt: ") - 1 + NGX_INT_T_LEN + sizeof(CRLF) - 1 ++ + sizeof ("X-Proxy-IP: ") - 1 + proxy_ip.len + sizeof(CRLF) - 1 ++ + sizeof ("Client-IP: ") - 1 + work->connection->addr_text.len + sizeof(CRLF) - 1; ++ ++ if (work->type == zmauth_admin_console) { ++ len += sizeof ("Auth-Zimbra-Admin: True" CRLF) - 1; ++ } else if (work->type == zmauth_zx) { ++ len += sizeof ("Auth-Zimbra-Zx: True" CRLF) - 1; ++ } ++ ++ if (IS_PROTO_WEB(work->protocol)) { ++ len += sizeof("X-Proxy-Host: ") - 1 + work->virtual_host.len + sizeof(CRLF) - 1; ++ } ++ ++ if (work->auth_method == ZM_AUTHMETH_CERTAUTH || ++ work->auth_method == ZM_AUTHMETH_GSSAPI) { ++ len += sizeof ("Auth-Admin-User: ") - 1 + zlcf->master_auth_username.len + sizeof(CRLF) - 1; ++ len += sizeof ("Auth-Admin-User: ") - 1 + zlcf->master_auth_password.len + sizeof(CRLF) - 1; ++ ++ if (work->auth_method == ZM_AUTHMETH_GSSAPI) { ++ len += sizeof ("Auth-ID: ") - 1 + work->auth_id.len + sizeof(CRLF) - 1; ++ } ++ } ++ ++ len += sizeof(CRLF) - 1; ++ ++ b = ngx_create_temp_buf(pool, len); ++ ++ if (b == NULL) { ++ return NGX_ERROR; ++ } ++ ++ b->last = ngx_sprintf(b->last, "GET %V HTTP/1.0" CRLF, &ctx->handler->uri); ++ b->last = ngx_sprintf(b->last, "Host: %V" CRLF, &ctx->handler->host); ++ b->last = ngx_sprintf(b->last, "Auth-Method: %V" CRLF, &ZM_AUTHMETH[work->auth_method]); ++ b->last = ngx_sprintf(b->last, "Auth-User: %V" CRLF, &username); ++ b->last = ngx_sprintf(b->last, "Auth-Pass: %V" CRLF, &ngx_zm_lookup_password_placeholder); ++ b->last = ngx_sprintf(b->last, "Auth-Protocol: %V" CRLF, &ZM_PROTO[work->protocol]); ++ if (IS_PROTO_WEB(work->protocol)) { ++ work->login_attempts = 0; /* for web, login attempts is always 0 */ ++ } ++ b->last = ngx_sprintf(b->last, "Auth-Login-Attempt: %d" CRLF, work->login_attempts); ++ b->last = ngx_sprintf(b->last, "X-Proxy-IP: %V" CRLF, &proxy_ip); ++ b->last = ngx_sprintf(b->last, "Client-IP: %V" CRLF, &work->connection->addr_text); ++ if (work->type == zmauth_admin_console) { ++ b->last = ngx_cpymem(b->last, "Auth-Zimbra-Admin: True" CRLF, sizeof("Auth-Zimbra-Admin: True" CRLF) - 1); ++ } else if (work->type == zmauth_zx) { ++ b->last = ngx_cpymem(b->last, "Auth-Zimbra-Zx: True" CRLF, sizeof("Auth-Zimbra-Zx: True" CRLF) - 1); ++ } ++ ++ if (IS_PROTO_WEB(work->protocol)) { ++ b->last = ngx_sprintf(b->last, "X-Proxy-Host: %V" CRLF, &work->virtual_host); ++ } ++ ++ if (work->auth_method == ZM_AUTHMETH_CERTAUTH|| ++ work->auth_method == ZM_AUTHMETH_GSSAPI) { ++ b->last = ngx_sprintf(b->last, "Auth-Admin-User: %V" CRLF, &zlcf->master_auth_username); ++ b->last = ngx_sprintf(b->last, "Auth-Admin-Pass: %V" CRLF, &zlcf->master_auth_password); ++ ++ if (work->auth_method == ZM_AUTHMETH_GSSAPI) { ++ b->last = ngx_sprintf(b->last, "Auth-ID: %V" CRLF, &work->auth_id); ++ } ++ } ++ ++ b->last = ngx_cpymem(b->last, CRLF, sizeof(CRLF) - 1); ++ ++ ctx->lookup_req = b; ++ ++#if (NGX_DEBUG) ++ ngx_str_t temp; ++ temp.data = b->pos; ++ temp.len = b->last - b->pos; ++ ++ ngx_log_debug1 (NGX_LOG_DEBUG_ZIMBRA, ctx->log, 0, ++ "send lookup request:\n%V", &temp); ++#endif ++ ++ return NGX_OK; ++} ++ ++static void ++ngx_zm_lookup_recv_response_handler(ngx_event_t *rev) ++{ ++ ngx_connection_t *c; ++ ngx_zm_lookup_ctx_t *ctx; ++ ngx_zm_lookup_work_t *work; ++ ngx_zm_lookup_conf_t *zlcf; ++ ssize_t n, size; ++ ++ c = rev->data; ++ ctx = c->data; ++ work = ctx->work; ++ zlcf = (ngx_zm_lookup_conf_t *)ngx_get_conf(ngx_cycle->conf_ctx, ngx_zm_lookup_module); ++ ++ if(rev->timedout) { ++ ngx_log_debug2(NGX_LOG_DEBUG_ZIMBRA, rev->log, 0, ++ "zm_route_lookup_module: lookup handler timed out, host:%V, uri:%V", ++ &ctx->handler->peer->name, &ctx->handler->uri); ++ ++ ngx_zm_lookup_close_connection(ctx); ++ work->result = ZM_LOOKUP_READ_TIMEOUT; ++ work->err = ERRMSG[ZM_LOOKUP_READ_TIMEOUT]; ++ work->on_failure(work); ++ return; ++ } ++ ++ if(ctx->lookup_resp == NULL) { ++ ctx->lookup_resp = ngx_create_temp_buf(ctx->pool, zlcf->buffer_size); ++ ++ if (ctx->lookup_resp == NULL) { ++ ngx_zm_lookup_close_connection(ctx); ++ work->ctx = NULL; ++ work->result = ZM_LOOKUP_MEM_ALLOC_ERROR; ++ work->err = ERRMSG[ZM_LOOKUP_MEM_ALLOC_ERROR]; ++ work->on_failure(work); ++ return; ++ } ++ } ++ ++ size = ctx->lookup_resp->end - ctx->lookup_resp->last; ++ n = c->recv(c, ctx->lookup_resp->pos, size); ++ ++ ngx_log_debug1(NGX_LOG_DEBUG_ZIMBRA, rev->log, 0, ++ "zm lookup: ngx_recv() returned %d bytes", n); ++ if(n > 0) { ++ ctx->lookup_resp->last += n; ++ ctx->lookup_response_handler (ctx); ++ return; ++ } ++ ++ if(n == NGX_AGAIN) { ++ ngx_log_debug0(NGX_LOG_DEBUG_ZIMBRA, rev->log, 0, ++ "zm lookup: ngx_recv() returned NGX_AGAIN"); ++ return; ++ } ++ ++ ngx_zm_lookup_close_connection(ctx); ++ work->ctx = NULL; ++ work->result = ZM_LOOKUP_READ_ERROR; ++ work->err = ERRMSG[ZM_LOOKUP_READ_ERROR]; ++ work->on_failure(work); ++} ++ ++static void ++ngx_zm_lookup_process_response(ngx_zm_lookup_ctx_t *ctx) ++{ ++ u_char *p, ch, *code_start; ++ ngx_int_t code; ++ ngx_flag_t error; ++ ngx_str_t errmsg; ++ ++ enum { ++ sw_start = 0, ++ sw_H, ++ sw_HT, ++ sw_HTT, ++ sw_HTTP, ++ sw_HTTPVER, ++ sw_code, ++ sw_skip, ++ sw_almost_done ++ } state; ++ ++ ngx_log_debug0(NGX_LOG_DEBUG_ZIMBRA, ctx->log, 0, ++ "zm lookup: process route discovery HTTP status"); ++ ++ state = ctx->state; ++ ++ error = 0; ++ ++ for (p = ctx->lookup_resp->pos; p < ctx->lookup_resp->last; p++) ++ { ++ ch = *p; ++ ++ switch (state) { ++ ++ /* "HTTP/" */ ++ case sw_start: ++ if (ch == 'H') { ++ state = sw_H; ++ break; ++ } ++ goto next; ++ ++ case sw_H: ++ if (ch == 'T') { ++ state = sw_HT; ++ break; ++ } ++ goto next; ++ ++ case sw_HT: ++ if (ch == 'T') { ++ state = sw_HTT; ++ break; ++ } ++ goto next; ++ ++ case sw_HTT: ++ if (ch == 'P') { ++ state = sw_HTTP; ++ break; ++ } ++ goto next; ++ ++ case sw_HTTP: ++ if (ch == '/') { ++ state = sw_HTTPVER; ++ break; ++ } ++ goto next; ++ ++ case sw_HTTPVER: ++ if (ch == ' ') { ++ state = sw_code; ++ code_start = p + 1; ++ break; ++ } ++ ++ if ((ch >= '0' && ch <= '9') || ch == '.') { ++ break; ++ } ++ goto next; ++ ++ case sw_code: ++ switch (ch) { ++ case ' ': ++ code = ngx_atoi(code_start, p - code_start); ++ if (code != 200) { ++ error = 1; ++ } ++ state = sw_skip; ++ break; ++ case CR: ++ state = sw_almost_done; ++ break; ++ case LF: ++ goto done; ++ } ++ break; ++ ++ /* any text until end of line */ ++ case sw_skip: ++ switch (ch) { ++ case CR: ++ state = sw_almost_done; ++ break; ++ case LF: ++ goto done; ++ } ++ break; ++ ++ /* end of status line */ ++ case sw_almost_done: ++ if (ch == LF) { ++ goto done; ++ } ++ ++ ngx_log_error(NGX_LOG_ERR, ctx->log, 0, ++ "zm lookup: lookup handler %V sent invalid response", ++ ctx->peer.name); ++ ngx_zm_lookup_close_connection(ctx); ++ ctx->work->result = ZM_LOOKUP_INVALID_RESPONSE; ++ ctx->work->on_failure(ctx->work); ++ return; ++ } ++ } ++ ++ ctx->lookup_resp->pos = p; ++ ctx->state = state; ++ ++ return; ++ ++next: ++ ++ p = ctx->lookup_resp->start - 1; ++ ++done: ++ ++ if (error) { ++ errmsg.data = code_start; ++ errmsg.len = p - code_start + 1; ++ /* trim the trailing CR LF */ ++ while (((*p) == CR || (*p) == LF) && p >= ctx->lookup_resp->start) { ++ errmsg.len--; ++ p--; ++ } ++ ++ ngx_log_error(NGX_LOG_ERR, ctx->log, 0, ++ "zm lookup: lookup handler %V sent error response: %V", ++ ctx->peer.name, &errmsg); ++ ngx_zm_lookup_close_connection(ctx); ++ ctx->work->result = ZM_LOOKUP_INVALID_RESPONSE; ++ ctx->work->on_failure(ctx->work); ++ return; ++ } ++ ++ ctx->lookup_resp->pos = p + 1; ++ ctx->state = 0; ++ ctx->lookup_response_handler = ngx_zm_lookup_process_response_headers; ++ ctx->lookup_response_handler (ctx); ++} ++ ++static ngx_int_t ++ngx_zm_lookup_parse_response_headers(ngx_zm_lookup_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->lookup_resp->pos; p < ctx->lookup_resp->last; p++) { ++ ch = *p; ++ ++ 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->lookup_resp->pos = p; ++ ctx->state = state; ++ ++ return NGX_AGAIN; ++ ++done: ++ ++ ctx->lookup_resp->pos = p + 1; ++ ctx->state = sw_start; ++ ++ return NGX_OK; ++ ++header_done: ++ ++ ctx->lookup_resp->pos = p + 1; ++ ctx->state = sw_start; ++ ++ return NGX_DONE; ++} ++ ++static void ++ngx_zm_lookup_process_response_headers(ngx_zm_lookup_ctx_t *ctx) ++{ ++ ngx_zm_lookup_conf_t *zlcf; ++ ++ size_t len; ++ ngx_int_t rc, n; ++ ngx_zm_lookup_work_t *work; ++ ngx_str_t addr; /* route ipaddr */ ++ ngx_str_t port; /* route port */ ++ ngx_flag_t isCacheAlias; /* whether to cache alias for the auth account */ ++ ++ zlcf = (ngx_zm_lookup_conf_t *)ngx_get_conf(ngx_cycle->conf_ctx, ngx_zm_lookup_module); ++ ++ work = ctx->work; ++ ++ ngx_log_debug0(NGX_LOG_DEBUG_ZIMBRA, ctx->log, 0, ++ "zm lookup: process route discovery HTTP headers"); ++ ++ for (;;) ++ { ++ rc = ngx_zm_lookup_parse_response_headers(ctx); ++ ++ if (rc == NGX_OK) ++ { ++ ++#if (NGX_DEBUG) ++ { ++ ngx_str_t key, value; ++ ++ key.len = ctx->header_name_end - ctx->header_name_start; ++ key.data = ctx->header_name_start; ++ value.len = ctx->header_end - ctx->header_start; ++ value.data = ctx->header_start; ++ ++ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ctx->log, 0, ++ "zm_ngx_lookup_module: zm lookup http header: \"%V: %V\"", ++ &key, &value); ++ } ++#endif ++ ++ len = ctx->header_name_end - ctx->header_name_start; ++ ++ if (len == sizeof("Auth-Status") - 1 ++ && ngx_strncasecmp(ctx->header_name_start, ++ (u_char *) "Auth-Status", ++ sizeof("Auth-Status") - 1) ++ == 0) ++ { ++ len = ctx->header_end - ctx->header_start; ++ ++ if (len == 2 ++ && ctx->header_start[0] == 'O' ++ && ctx->header_start[1] == 'K') ++ { ++ continue; ++ } ++ ++ if (len == 4 ++ && ctx->header_start[0] == 'W' ++ && ctx->header_start[1] == 'A' ++ && ctx->header_start[2] == 'I' ++ && ctx->header_start[3] == 'T') ++ { ++ /* NginxLookup never return "Auth-Status: WAIT" */ ++ continue; ++ } ++ ++ /* Accept error msg like "Auth-Status: login failed" */ ++ work->err.len = len; ++ work->err.data = ctx->header_start; ++ ++ continue; ++ } ++ ++ if (len == sizeof("Auth-Server") - 1 ++ && ngx_strncasecmp(ctx->header_name_start, ++ (u_char *) "Auth-Server", ++ sizeof("Auth-Server") - 1) ++ == 0) ++ { ++ addr.len = ctx->header_end - ctx->header_start; ++ addr.data = ctx->header_start; ++ ++ continue; ++ } ++ ++ if (len == sizeof("Auth-Port") - 1 ++ && ngx_strncasecmp(ctx->header_name_start, ++ (u_char *) "Auth-Port", ++ sizeof("Auth-Port") - 1) ++ == 0) ++ { ++ port.len = ctx->header_end - ctx->header_start; ++ port.data = ctx->header_start; ++ ++ continue; ++ } ++ ++ if (len == sizeof("Auth-Cache-Alias") - 1 ++ && ngx_strncasecmp(ctx->header_name_start, ++ (u_char *) "Auth-Cache-Alias", ++ sizeof("Auth-Cache-Alias") - 1) ++ == 0) ++ { ++ len = ctx->header_end - ctx->header_start; ++ ++ if (len == 4 ++ && ctx->header_start[0] == 'T' ++ && ctx->header_start[1] == 'R' ++ && ctx->header_start[2] == 'U' ++ && ctx->header_start[3] == 'E') ++ { ++ /* cache the alias if True*/ ++ isCacheAlias = 1; ++ } else { ++ isCacheAlias = 0; ++ } ++ continue; ++ } ++ if (len == sizeof("Auth-User") - 1 ++ && ngx_strncasecmp(ctx->header_name_start, ++ (u_char *) "Auth-User", ++ sizeof("Auth-User") - 1) ++ == 0) ++ { ++ work->account_name.len = ctx->header_end - ctx->header_start; ++ work->account_name.data = ctx->header_start; ++ ++ continue; ++ } ++ ++ if (len == sizeof("Auth-Pass") - 1 ++ && ngx_strncasecmp(ctx->header_name_start, ++ (u_char *) "Auth-Pass", ++ sizeof("Auth-Pass") - 1) ++ == 0) ++ { ++ /* client cert auth will return zm_auth_token in ++ Auth-Pass ++ */ ++ if (work->auth_method == ZM_AUTHMETH_GSSAPI || ++ work->auth_method == ZM_AUTHMETH_CERTAUTH) { ++ work->zm_auth_token.len = ctx->header_end - ctx->header_start; ++ work->zm_auth_token.data = ctx->header_start; ++ } ++ ++ continue; ++ } ++ ++ if (len == sizeof("Auth-ID") - 1 ++ && ngx_strncasecmp(ctx->header_name_start, ++ (u_char *) "Auth-ID", ++ sizeof("Auth-ID") - 1) ++ == 0) ++ { ++ /* just for GSSAPI */ ++ if (work->auth_method == ZM_AUTHMETH_GSSAPI) { ++ work->auth_id.len = ctx->header_end - ctx->header_start; ++ work->auth_id.data = ctx->header_start; ++ } ++ ++ continue; ++ } ++ ++ ++ if (len == sizeof("Auth-Wait") - 1 ++ && ngx_strncasecmp(ctx->header_name_start, ++ (u_char *) "Auth-Wait", ++ sizeof("Auth-Wait") - 1) ++ == 0) ++ { ++ n = ngx_atoi(ctx->header_start, ++ ctx->header_end - ctx->header_start); ++ ++ if (n != NGX_ERROR) { ++ work->wait_time = n; ++ } ++ ++ continue; ++ } ++ ++ ++ if (len == sizeof("Auth-Error-Code") - 1 ++ && ngx_strncasecmp(ctx->header_name_start, ++ (u_char *) "Auth-Error-Code", ++ sizeof("Auth-Error-Code") - 1) ++ == 0) ++ { ++ /* NginxLookup never return this header */ ++ } ++ ++ continue; ++ } ++ ++ if (rc == NGX_AGAIN ) { ++ return; ++ } ++ ++ /* close connection to the lookup handler */ ++ ngx_zm_lookup_close_connection(ctx); ++ work->ctx = NULL; /* avoid duplicate clean up */ ++ ++ if (rc == NGX_DONE){ ++ ngx_log_debug0(NGX_LOG_DEBUG_ZIMBRA, ctx->log, 0, ++ "zm lookup: done processing lookup headers"); ++ if (work->err.len) { ++ /* Login failed */ ++ ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "zm lookup: an error is " ++ "returned by zimbra lookup handler: %V", &work->err); ++ work->result = ZM_LOOKUP_LOGIN_FAILED; ++ work->on_failure(work); ++ return; ++ ++ } else { ++ if (IS_LOOKUP_ROUTE(work->auth_method)) { ++ ++ ngx_log_debug3(NGX_LOG_DEBUG_ZIMBRA, ctx->log, 0, ++ "zm lookup: lookup handler %V sent route %V:%V", ++ ctx->peer.name, &addr, &port); ++ if (addr.len == 0 || port.len == 0) { ++ work->result = ZM_LOOKUP_INVALID_ROUTE; ++ work->on_failure(work); ++ return; ++ } else { ++ /* retrieve route */ ++ work->route = ngx_palloc(ctx->pool, sizeof(ngx_addr_t)); ++ if (work->route == NULL) { /* NO MEM */ ++ work->result = ZM_LOOKUP_MEM_ALLOC_ERROR; ++ work->err = ERRMSG[ZM_LOOKUP_MEM_ALLOC_ERROR]; ++ work->on_failure(work); ++ return; ++ } ++ if (ngx_zm_lookup_retrieve_route (ctx->pool, ++ &addr, &port, work->route) == NGX_ERROR) { ++ ++ /* route retrival error */ ++ ngx_log_error(NGX_LOG_ERR, ctx->log, 0, ++ "Error occurs when retrieve route info from lookup handler, " ++ "addr=%V, port=%V", &addr, &port); ++ work->result = ZM_LOOKUP_INVALID_ROUTE; ++ work->err = ERRMSG[ZM_LOOKUP_INVALID_ROUTE]; ++ work->on_failure(work); ++ return; ++ } else { ++ ++ /* route retrival succeed */ ++ if (zlcf->caching && ++ ctx->work->auth_method != ZM_AUTHMETH_GSSAPI && ++ ctx->work->auth_method != ZM_AUTHMETH_CERTAUTH) { ++ /* add alias-->account && account-->route caching */ ++ if (ctx->work->alias_check_stat != ZM_ALIAS_FOUND && ++ ctx->work->alias_check_stat != ZM_ALIAS_IGNORED && isCacheAlias) { ++ /* only cache alias-->account when the account is unavailable from cache */ ++ /* cache alias-->account with default domain */ ++ ngx_zm_lookup_cache_alias(ctx, work->username, work->account_name); ++ } ++ ++ if (work->account_name.len > 0) { ++ ngx_zm_lookup_cache_route(ctx, work->account_name, work->route->name); ++ } else { ++ ngx_zm_lookup_cache_route(ctx, work->username, work->route->name); ++ } ++ } ++ work->result = ZM_LOOKUP_SUCCESS; ++ work->on_success(work); ++ return; ++ } ++ } ++ } else { ++ /* non-route lookup, such as certauth */ ++ work->result = ZM_LOOKUP_SUCCESS; ++ work->on_success(work); ++ return; ++ } ++ } ++ } ++ ++ /* rc == NGX_ERROR */ ++ ngx_log_error(NGX_LOG_ERR, ctx->log, 0, ++ "zm lookup: route handler %V sent invalid header in response", ++ ctx->peer.name); ++ ++ return; ++ } ++} ++ ++ ++static ngx_int_t ++ngx_zm_lookup_retrieve_route (ngx_pool_t * pool, ngx_str_t * addr_text, ++ ngx_str_t * port_text, ngx_addr_t * route) ++{ ++ ngx_int_t rc; ++ size_t i; ++ ngx_flag_t ipv6, domainName; ++ ngx_url_t u; ++ u_char c; ++ ++ ipv6 = 0, domainName = 0; ++ for (i = 0; i < addr_text->len; i++) { ++ c = addr_text->data[i]; ++ if (c == ':') { ++ /* an addr containing ":" may be IPv6 */ ++ domainName = 0; ++ ipv6 = 1; ++ break; ++ } else if (c >= 'A' && c != '.') { ++ domainName = 1; ++ // try to look for ":". if found it must be ipv6 ++ } ++ } ++ ++ ngx_memzero(&u, sizeof(ngx_url_t)); ++ u.uri_part = 0; ++ ++ if(ipv6) { ++ u.url.len = addr_text->len + port_text->len + 3; ++ u.url.data = ngx_palloc(pool, u.url.len); ++ ngx_sprintf(u.url.data, "[%V]:%V", addr_text, port_text); ++ } else { /* ipv4 or domain name */ ++ u.url.len = addr_text->len + port_text->len + 1; ++ u.url.data = ngx_palloc(pool, u.url.len); ++ ngx_sprintf(u.url.data, "%V:%V", addr_text, port_text); ++ } ++ ++ if(!domainName) { ++ u.listen = 1; // set this will only parse the IP but not resolve addr ++ } ++ ++ rc = ngx_parse_url(pool, &u); ++ ++ if(rc != NGX_OK) { ++ return rc; ++ } ++ ++ route->name = u.url; ++ route->socklen = u.socklen; ++ route->sockaddr = ngx_palloc(pool, route->socklen); ++ ngx_memcpy(route->sockaddr, &u.sockaddr, route->socklen); ++ ++ return NGX_OK; ++} ++ ++static void ++ngx_zm_lookup_close_connection(ngx_zm_lookup_ctx_t * ctx) { ++ if (ctx->peer.connection) { ++ ngx_log_debug2 (NGX_LOG_DEBUG_ZIMBRA, ctx->log, 0, ++ "close connection %p to nginx lookup handler %V", ++ ctx->peer.connection, ctx->peer.name); ++#if (NGX_SSL) ++ ++ if (ctx->handler->ssl && ctx->peer.connection->ssl) { ++ ctx->peer.connection->ssl->no_wait_shutdown = 1; ++ ngx_ssl_shutdown(ctx->peer.connection); ++ } ++ ++#endif ++ ngx_close_connection(ctx->peer.connection); ++ ctx->peer.connection = NULL; ++ } ++} ++ ++void ++ngx_zm_lookup_finalize(ngx_zm_lookup_work_t * work) ++{ ++ ngx_zm_lookup_ctx_t *ctx; ++ ++ if (work == NULL) { ++ return; ++ } ++ ++ ctx = work->ctx; ++ ++ if(ctx != NULL) { ++ if(ctx->wait_memcache) { ++ ngx_memcache_ignore_work_by_ctx(ctx); ++ work->ctx = NULL; ++ return; ++ } ++ ++ ngx_zm_lookup_close_connection(ctx); ++ work->ctx = NULL; ++ return; ++ } ++} ++ ++/* ++ * Giving a socket, return its local addr string representation IP. The ++ * string will be allocated on "pool". ++ */ ++ngx_str_t ++ngx_zm_lookup_get_local_socket_addr_text (ngx_pool_t *pool, ngx_socket_t s) ++{ ++ int family; ++ static ngx_str_t res; ++ struct sockaddr_in *sin; ++#if (NGX_HAVE_INET6) ++ struct sockaddr_in6 *sin6; ++#endif ++ u_char *p; ++ socklen_t len, strlen; ++ u_char sockaddr[NGX_SOCKADDRLEN]; ++ ++ len = NGX_SOCKADDRLEN; ++ ngx_memzero(sockaddr, len); ++ getsockname(s, (struct sockaddr*)sockaddr, &len); ++ ++ family = ((struct sockaddr *)sockaddr)->sa_family; ++ if (family == AF_INET) { ++ sin = (struct sockaddr_in *)sockaddr; ++ p = ngx_palloc(pool, NGX_INET_ADDRSTRLEN); ++ strlen = ngx_inet_ntop (family, &(sin->sin_addr.s_addr), p, ++ NGX_INET_ADDRSTRLEN); ++ ++#if (NGX_HAVE_INET6) ++ } else { ++ sin6 = (struct sockaddr_in6 *)sockaddr; ++ p = ngx_palloc(pool, NGX_INET6_ADDRSTRLEN); ++ strlen = ngx_inet_ntop (family, &(sin6->sin6_addr.s6_addr), ++ p, NGX_INET6_ADDRSTRLEN); ++#endif ++ ++ } ++ ++ res.data = p; ++ res.len = strlen; ++ ++ return res; ++} ++ ++/* make the cache "alias-->account" */ ++static void ++ngx_zm_lookup_cache_alias(ngx_zm_lookup_ctx_t *ctx, ngx_str_t alias, ++ ngx_str_t account_name) { ++ mc_work_t mc; ++ ngx_str_t key; ++ ngx_log_t *log; ++ ngx_zm_lookup_work_t *work; ++ ngx_pool_t *pool; ++ const u_char *p; ++ ++ log = ctx->log; ++ work = ctx->work; ++ pool = ctx->pool; ++ ++ if (alias.len == account_name.len && ++ ngx_memcmp(alias.data, account_name.data, alias.len) == 0 ++ ) { ++ /* bug 66469, try to take the part before '@' as alias */ ++ p = ngx_zm_strchr(alias, '@'); ++ if (p == NULL) { ++ return; ++ } ++ ++ alias.len = p - alias.data; ++ ++ if (work->alias_key.len > 0) { ++ ngx_str_null(&work->alias_key); /* reset it and force regeneration later */ ++ } ++ } ++ ++ if (work->alias_key.len > 0) { ++ key = work->alias_key; ++ ++ } else { ++ if (IS_PROTO_WEB(work->protocol)) { ++ key = ngx_zm_lookup_get_http_alias_key ++ (pool, log, alias, work->virtual_host); ++ } else { ++ key = ngx_zm_lookup_get_mail_alias_key ++ (pool, log, alias, work->connection->addr_text); ++ } ++ ++ if (key.len == 0) { /* NOMEM */ ++ work->result = ZM_LOOKUP_MEM_ALLOC_ERROR; ++ work->err = ERRMSG[ZM_LOOKUP_MEM_ALLOC_ERROR]; ++ work->on_failure(work); ++ return; ++ } ++ ++ work->alias_key = key; ++ } ++ ++ mc.ctx = ctx; ++ mc.request_code = mcreq_add; ++ mc.response_code = mcres_unknown; ++ mc.on_success = ngx_zm_lookup_cache_dummy_handler; ++ mc.on_failure = ngx_zm_lookup_cache_dummy_handler; ++ ++ //no need to set ctx->wait_memcache_response since w.ctx won't be ++ //destroyed before response comes ++ ngx_memcache_post(&mc, key, account_name, NULL, log); ++} ++ ++/* ++ * make the cache account-->route/id-->route ++ * user: account_name/zimbra id ++ */ ++static void ++ngx_zm_lookup_cache_route(ngx_zm_lookup_ctx_t *ctx, ngx_str_t user, ngx_str_t route) ++{ ++ mc_work_t mc; ++ ngx_log_t *log; ++ ngx_str_t key; ++ ngx_zm_lookup_work_t *work; ++ ngx_zm_lookup_conf_t *zlcf; ++ ++ zlcf = (ngx_zm_lookup_conf_t *) ++ ngx_get_conf(ngx_cycle->conf_ctx, ngx_zm_lookup_module); ++ ++ log = ctx->log; ++ work = ctx->work; ++ ++ if (work->auth_method == ZM_AUTHMETH_ZIMBRAID) { ++ key = ngx_zm_lookup_get_id_route_key( ++ ctx->pool, log, ++ ZM_PROTO[work->protocol], user, work->type); ++ } else { ++ if (zlcf->allow_unqualified == 0 && !is_login_qualified(user)) { ++ key = ngx_zm_lookup_get_user_route_key(ctx->pool, log, ++ ZM_PROTO[work->protocol], user, work->connection->addr_text); ++ } else { ++ key = ngx_zm_lookup_get_user_route_key(ctx->pool, log, ++ ZM_PROTO[work->protocol], user, NGX_EMPTY_STR); ++ } ++ } ++ ++ if (key.len == 0) { /* NOMEM */ ++ work->result = ZM_LOOKUP_MEM_ALLOC_ERROR; ++ work->err = ERRMSG[ZM_LOOKUP_MEM_ALLOC_ERROR]; ++ work->on_failure(work); ++ return; ++ } ++ ++ ctx->work->route_key = key; ++ ++ mc.ctx = ctx; ++ mc.request_code = mcreq_add; ++ mc.response_code = mcres_unknown; ++ mc.on_success = ngx_zm_lookup_cache_dummy_handler; ++ mc.on_failure = ngx_zm_lookup_cache_dummy_handler; ++ ++ //no need to set ctx->wait_memcache_response because nothing ++ //will be disposed ++ ngx_memcache_post(&mc, key, route, NULL, log); ++} ++ ++static void ++ngx_zm_lookup_cache_dummy_handler (mc_work_t *work) ++{ ++ /* do nothing */ ++} ++ ++static ngx_str_t ++ngx_zm_lookup_get_user_route_key(ngx_pool_t *pool, ngx_log_t *log, ++ ngx_str_t proto, ngx_str_t account_name, ngx_str_t client_ip) ++{ ++ ngx_str_t key; ++ size_t len; ++ u_char *p; ++ ++ len = sizeof("route:") - 1 + ++ sizeof("proto=") - 1 + ++ proto.len + ++ sizeof(";") - 1 + ++ sizeof("user=") - 1 + ++ account_name.len; ++ ++ if (client_ip.len > 0) { ++ len += sizeof("@") - 1 + client_ip.len; ++ } ++ ++ key.data = ngx_palloc(pool, len); ++ if (key.data == NULL) { ++ key.len = 0; ++ return key; ++ } ++ ++ p = key.data; ++ p = ngx_cpymem(p, "route:", sizeof("route:") - 1); ++ p = ngx_cpymem(p, "proto=", sizeof("proto=") - 1); ++ p = ngx_cpymem(p, proto.data, proto.len); ++ *p++ = ';'; ++ p = ngx_cpymem(p, "user=", sizeof("user=") - 1); ++ p = ngx_cpymem(p, account_name.data, account_name.len); ++ ++ if (client_ip.len > 0) { ++ *p++ = '@'; ++ p = ngx_cpymem(p, client_ip.data, client_ip.len); ++ } ++ ++ key.len = p - key.data; ++ ++ return key; ++} ++ ++ngx_str_t ++ngx_zm_lookup_get_mail_alias_key (ngx_pool_t *pool, ngx_log_t *log, ++ ngx_str_t alias, ngx_str_t ip) ++{ ++ ngx_str_t key; ++ size_t len; ++ u_char *p; ++ ++ len = sizeof("alias:") - 1 + ++ sizeof("user=") - 1 + ++ alias.len + ++ sizeof(";") - 1 + ++ sizeof("ip=") - 1 + ++ ip.len; ++ ++ key.data = ngx_palloc(pool, len); ++ if (key.data == NULL) { ++ key.len = 0; ++ return key; ++ } ++ ++ p = key.data; ++ p = ngx_cpymem(p, "alias:", sizeof("alias:") - 1); ++ p = ngx_cpymem(p, "user=", sizeof("user=") - 1); ++ p = ngx_cpymem(p, alias.data, alias.len); ++ *p++ = ';'; ++ p = ngx_cpymem(p,"ip=", sizeof("ip=") - 1); ++ p = ngx_cpymem(p, ip.data, ip.len); ++ ++ key.len = p - key.data; ++ ++ return key; ++} ++ ++ngx_str_t ++ngx_zm_lookup_get_http_alias_key (ngx_pool_t *pool, ngx_log_t *log, ++ ngx_str_t alias, ngx_str_t vhost) ++{ ++ ngx_str_t key; ++ size_t len; ++ u_char *p; ++ ++ len = sizeof("alias:") - 1 + ++ sizeof("user=") - 1 + ++ alias.len + ++ sizeof(";") - 1 + ++ sizeof("vhost=") - 1 + ++ vhost.len; ++ ++ key.data = ngx_palloc(pool, len); ++ if (key.data == NULL) { ++ key.len = 0; ++ return key; ++ } ++ ++ p = key.data; ++ p = ngx_cpymem(p, "alias:", sizeof("alias:") - 1); ++ p = ngx_cpymem(p, "user=", sizeof("user=") - 1); ++ p = ngx_cpymem(p, alias.data, alias.len); ++ *p++ = ';'; ++ p = ngx_cpymem(p, "vhost=", sizeof("vhost=") - 1); ++ p = ngx_cpymem(p, vhost.data, vhost.len); ++ ++ key.len = p - key.data; ++ ++ return key; ++} ++ ++static ngx_str_t ++ngx_zm_lookup_get_id_route_key(ngx_pool_t *pool, ngx_log_t *log, ++ ngx_str_t proto, ngx_str_t id, ngx_http_zmauth_t type) ++{ ++ ngx_str_t key; ++ size_t len; ++ u_char *p; ++ ++ len = sizeof("route:") - 1 + ++ sizeof("proto=") - 1 + ++ proto.len + ++ sizeof(";") - 1 + ++ sizeof("id=") - 1 + ++ id.len; ++ ++ if (type == zmauth_admin_console) { ++ len += sizeof("admin=1;") - 1; ++ } else if (type == zmauth_zx) { ++ len += sizeof("zx=1;") - 1; ++ } ++ ++ key.data = ngx_palloc(pool, len); ++ if (key.data == NULL) { ++ key.len = 0; ++ return key; ++ } ++ ++ p = key.data; ++ p = ngx_cpymem(p, "route:", sizeof("route:") - 1); ++ p = ngx_cpymem(p, "proto=", sizeof("proto=") - 1); ++ p = ngx_cpymem(p, proto.data, proto.len); ++ *p++ = ';'; ++ if (type == zmauth_admin_console) { ++ p = ngx_cpymem(p, "admin=1;", sizeof("admin=1;") - 1); ++ } else if (type == zmauth_zx) { ++ p = ngx_cpymem(p, "zx=1;", sizeof("zx=1;") - 1); ++ } ++ p = ngx_cpymem(p, "id=", sizeof("id=") - 1); ++ p = ngx_cpymem(p, id.data, id.len); ++ ++ key.len = p - key.data; ++ ++ return key; ++} ++ ++/* ++ * delete alias-->account & account-->route cache from memcache ++ * after auth failure ++ */ ++void ++ngx_zm_lookup_delete_cache(ngx_str_t alias_key, ngx_str_t route_key) ++{ ++ mc_work_t w; ++ ngx_log_t *log; ++ ngx_flag_t delete_alias_cache, delete_route_cache; ++ ++ delete_alias_cache = 0; ++ delete_route_cache = 0; ++ ++ if (alias_key.len > 0) { ++ delete_alias_cache = 1; ++ } ++ ++ if (route_key.len > 0) { ++ delete_route_cache = 1; ++ } ++ ++ if (delete_alias_cache == 0 && delete_route_cache == 0) { ++ return; ++ } ++ ++ log = ngx_cycle->log; ++ ++ w.request_code = mcreq_delete; ++ w.response_code = mcres_unknown; ++ w.on_success = ngx_zm_lookup_delete_cache_handler; ++ w.on_failure = ngx_zm_lookup_delete_cache_handler; ++ ++ if (delete_alias_cache) { ++ ngx_log_debug1 (NGX_LOG_DEBUG_ZIMBRA, log, 0, ++ "delete cached alias, key:%V", &alias_key); ++ ngx_memcache_post(&w, alias_key, NGX_EMPTY_STR, /* pool */ NULL, log); ++ } ++ ++ if (delete_route_cache) { ++ ngx_log_debug1 (NGX_LOG_DEBUG_ZIMBRA, log, 0, ++ "delete cached route, key:%V", &route_key); ++ ++ ngx_memcache_post (&w, route_key, NGX_EMPTY_STR, /* pool */ NULL, log); ++ } ++} ++ ++static void ++ngx_zm_lookup_delete_cache_handler (mc_work_t *work) ++{ ++ /* do nothing */ ++} ++ ++/* Utility function to check whether a login name is fully qualified ++ Return value is boolean (ngx_flag_t for portability) ++ */ ++ngx_flag_t ++is_login_qualified (ngx_str_t login) ++{ ++ /* we cannot use the crt strchr because login is not 0 terminated, ++ * neither we have no strnchr to use ++ */ ++ ++ size_t i, len; ++ len = login.len - 1; /* if the last symbol is @, it's not qualified */ ++ ++ for (i = 0; i < len; ++i) { ++ if (login.data[i] == '@') { ++ return 1; ++ } ++ } ++ ++ return 0; ++} ++ ++static const u_char * ++ngx_zm_strchr (ngx_str_t str, int chr) { ++ const u_char *end; ++ u_char *p; ++ u_char c; ++ ++ end = str.data + str.len; ++ p = str.data; ++ c = (u_char)chr; ++ do { ++ if (*p == c) { ++ return p; ++ } ++ } while (++p <= end); ++ ++ return NULL; ++} +diff -urN nginx/src/core/ngx_zm_lookup.h nginx/src/core/ngx_zm_lookup.h +--- nginx/src/core/ngx_zm_lookup.h 1970-01-01 05:30:00.000000000 +0530 ++++ nginx/src/core/ngx_zm_lookup.h 2023-03-04 13:25:59.843350100 +0530 +@@ -0,0 +1,207 @@ ++/* ++ * ***** BEGIN LICENSE BLOCK ***** ++ * Zimbra Collaboration Suite Server ++ * Copyright (C) 2011 Zimbra Software, LLC. ++ * ++ * The contents of this file are subject to the Zimbra Public License ++ * Version 1.4 ("License"); you may not use this file except in ++ * compliance with the License. You may obtain a copy of the License at ++ * http://www.zimbra.com/license. ++ * ++ * Software distributed under the License is distributed on an "AS IS" ++ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. ++ * ***** END LICENSE BLOCK ***** ++ */ ++ ++#ifndef _NGX_ZM_LOOKUP_H_INCLUDED_ ++#define _NGX_ZM_LOOKUP_H_INCLUDED_ ++ ++#include ++#include ++#include ++#include ++ ++#define NGX_ZM_LOOKUP_CONF 0x02000000 ++ ++typedef struct { ++ ngx_addr_t *peer; ++ ngx_str_t host; ++ ngx_str_t uri; ++ time_t failure_time; /* the time of last connection failure ++ to this handler peer, 0 means the ++ peer is now available */ ++ ngx_uint_t ssl; /* whether handler talks SSL or plain http */ ++} ngx_zm_lookup_handler_t; ++ ++typedef struct { ++ ngx_pool_t *pool; /* main pool where self resides */ ++ ngx_log_t *log; ++ ngx_array_t handlers; /* ngx_zm_lookup_handler_t*[] */ ++ ngx_uint_t handler_index; /* current index of round robin */ ++ ngx_msec_t retry_interval; /* time to retry to connect a handler after a failure (in ms) */ ++ ngx_msec_t timeout; /* timeout to fetch the result from nginx lookup handler (in ms) */ ++ ngx_str_t master_auth_username; ++ ngx_str_t master_auth_password; ++ ngx_str_t url; ++ ngx_flag_t caching; /* whether to add and check the alias/route in memcache */ ++ ngx_flag_t allow_unqualified; /* whether to append client ip to the "account-->route" caching key, ++ when the alias part is an unqualified name*/ ++ size_t buffer_size; ++ ngx_ssl_t *ssl; ++} ngx_zm_lookup_conf_t; ++ ++struct ngx_zm_lookup_work_s; ++ ++typedef void (*ngx_zm_lookup_callback)(struct ngx_zm_lookup_work_s *); ++ ++/* zmauth type */ ++typedef enum { ++ zmauth_web_client, ++ zmauth_admin_console, ++ zmauth_zx ++} ngx_http_zmauth_t; ++ ++ ++struct ngx_zm_lookup_work_s { ++ ngx_pool_t *pool; ++ ngx_log_t *log; ++ ++ /* input */ ++ ngx_str_t username; /* the original username given by user */ ++ ngx_str_t auth_id; /* GSSAPI auth id (principal) */ ++ ++ ngx_http_zmauth_t type; /* whether is web, admin or /zx/ */ ++ ++ ngx_uint_t protocol:3; /* protocol */ ++ ngx_uint_t auth_method:4; /* auth method */ ++ ngx_uint_t alias_check_stat:2; /* the alias-->account caching lookup status */ ++ ngx_uint_t login_attempts; /* only used for mail */ ++ ngx_str_t virtual_host; /* only used for web */ ++ ngx_connection_t * connection; /* client connection */ ++ ngx_str_t salt; /* only used for mail */ ++ ngx_str_t route_key; /* the key for "account-->route" cache */ ++ ++ /* output */ ++ ngx_addr_t *route; /* fetched route */ ++ ngx_str_t err; /* error message */ ++ time_t wait_time; /* wait time if login failed */ ++ ++ /* input & output */ ++ ngx_str_t zm_auth_token; /* for web route lookup, this will be input; ++ for client cert auth, this will be output ++ */ ++ ngx_str_t account_name; /* for mail route lookup, account name is ++ always returned ++ */ ++ ngx_str_t alias_key; /* the key for "alias-->account" cache */ ++ ++ ngx_zm_lookup_callback on_success; ++ ngx_zm_lookup_callback on_failure; ++ ++ ngx_int_t result; ++ ++ void *data; /* context such as http request or mail session */ ++ void *ctx; /* zm_lookup_ctx */ ++}; ++ ++typedef struct ngx_zm_lookup_work_s ngx_zm_lookup_work_t; ++ ++extern ngx_module_t ngx_zm_lookup_module; ++ ++struct ngx_zm_lookup_ctx_s; ++ ++typedef void (*ngx_zm_lookup_response_handler_t) (struct ngx_zm_lookup_ctx_s * ctx); ++ ++struct ngx_zm_lookup_ctx_s { ++ ngx_pool_t *pool; ++ ngx_log_t *log; ++ ++ /* for lookup handler elect */ ++ ngx_uint_t tries; ++ ngx_uint_t handler_index; ++ ++ ngx_zm_lookup_handler_t *handler; /*current handler to be used */ ++ ngx_peer_connection_t peer; ++ ++ /* for request send & response processing */ ++ ngx_buf_t *lookup_req; /* lookup request buffer */ ++ ngx_buf_t *lookup_resp; /* lookup response buffer */ ++ ngx_uint_t state; /* response parse state */ ++ u_char *header_name_start; ++ u_char *header_name_end; ++ u_char *header_start; ++ u_char *header_end; ++ ngx_zm_lookup_response_handler_t lookup_response_handler; ++ ++ ngx_zm_lookup_work_t *work; ++ ++ ngx_uint_t wait_memcache; /* whether memcache request is ++ posted but response doesn't come */ ++ ngx_event_t *wait_ev; ++}; ++ ++typedef struct ngx_zm_lookup_ctx_s ngx_zm_lookup_ctx_t; ++ ++/* lookup result */ ++#define ZM_LOOKUP_SUCCESS 0 ++#define ZM_LOOKUP_MEM_ALLOC_ERROR 1 ++#define ZM_LOOKUP_WRITE_ERROR 2 ++#define ZM_LOOKUP_READ_ERROR 3 ++#define ZM_LOOKUP_WRITE_TIMEOUT 4 ++#define ZM_LOOKUP_READ_TIMEOUT 5 ++#define ZM_LOOKUP_NO_VALID_HANDLER 6 ++#define ZM_LOOKUP_INVALID_ROUTE 7 ++#define ZM_LOOKUP_LOGIN_FAILED 8 ++#define ZM_LOOKUP_INVALID_RESPONSE 9 ++#define ZM_LOOKUP_CLIENT_CONNECTION_CLOSE 10 ++#define ZM_LOOKUP_OTHER_ERROR 50 ++#define ZM_LOOKUP_SSL_EVENT_SUCCESS 0 ++#define ZM_LOOKUP_SSL_EVENT_FAILED 1 ++ ++/* the protocols nginx lookup can serve for */ ++#define ZM_PROTO_UNKNOWN 0 ++#define ZM_PROTO_HTTP 1 ++#define ZM_PROTO_HTTPS 2 ++#define ZM_PROTO_POP3 3 ++#define ZM_PROTO_POP3S 4 ++#define ZM_PROTO_IMAP 5 ++#define ZM_PROTO_IMAPS 6 ++ ++#define IS_PROTO_MAIL(proto) (proto == ZM_PROTO_POP3 || \ ++ proto == ZM_PROTO_POP3S || \ ++ proto == ZM_PROTO_IMAP || \ ++ proto == ZM_RPOTO_IMAPS) ++ ++#define IS_PROTO_WEB(proto) (proto == ZM_PROTO_HTTP || \ ++ proto == ZM_PROTO_HTTPS) ++ ++/* alias-->account caching check state */ ++#define ZM_ALIAS_NOT_CHECKED 0 /* need to be checked but not done yet */ ++#define ZM_ALIAS_FOUND 1 /* has been checked and found */ ++#define ZM_ALIAS_NOT_FOUND 2 /* has been checked and not found */ ++#define ZM_ALIAS_IGNORED 3 /* has been checked and found, but ignored */ ++ ++/* the auth method supported */ ++#define ZM_AUTHMETH_USERNAME 0 /* get route by user name */ ++#define ZM_AUTHMETH_GSSAPI 1 /* get route and account id by kerberos v5 */ ++#define ZM_AUTHMETH_ZIMBRAID 2 /* get route by zimbra account id */ ++#define ZM_AUTHMETH_CERTAUTH 3 /* get account id by client certificate */ ++ ++#define IS_LOOKUP_ROUTE(auth_meth) (!(auth_meth == ZM_AUTHMETH_CERTAUTH)) ++ ++void ngx_zm_lookup(ngx_zm_lookup_work_t * work); ++void ngx_zm_lookup_delete_cache(ngx_str_t alias_key, ngx_str_t route_key); ++void ngx_zm_lookup_finalize(ngx_zm_lookup_work_t * work); ++ngx_flag_t ngx_zm_lookup_check_broken_connection(ngx_event_t *ev, ++ ngx_zm_lookup_work_t *work); ++ ++/* utility */ ++ngx_flag_t is_login_qualified (ngx_str_t login); ++ ++/* memcache key create */ ++ngx_str_t ngx_zm_lookup_get_http_alias_key(ngx_pool_t *pool, ++ ngx_log_t *log, ngx_str_t alias, ngx_str_t vhost); ++ngx_str_t ngx_zm_lookup_get_mail_alias_key(ngx_pool_t *pool, ++ ngx_log_t *log, ngx_str_t user, ngx_str_t ip); ++ ++#endif diff --git a/thirdparty/nginx/patches/nginx_src_event.patch b/thirdparty/nginx/patches/nginx_src_event.patch new file mode 100644 index 000000000..5ec093185 --- /dev/null +++ b/thirdparty/nginx/patches/nginx_src_event.patch @@ -0,0 +1,42 @@ +diff -urN nginx/src/event/ngx_event_openssl.c nginx/src/event/ngx_event_openssl.c +--- nginx/src/event/ngx_event_openssl.c 2023-10-03 16:49:31.242377500 +0530 ++++ nginx/src/event/ngx_event_openssl.c 2023-10-12 01:03:16.055868100 +0530 +@@ -3203,9 +3203,6 @@ + ngx_uint_t tries; + + rc = NGX_OK; +- +- ngx_ssl_ocsp_cleanup(c); +- + if (SSL_in_init(c->ssl->connection)) { + /* + * OpenSSL 1.0.2f complains if SSL_shutdown() is called during +@@ -3216,6 +3213,9 @@ + goto done; + } + ++// Zimbra customizations start here (Jira Tickets: ) ++ ngx_ssl_ocsp_cleanup(c); ++// Zimbra customizations end here + if (c->timedout || c->error || c->buffered) { + mode = SSL_RECEIVED_SHUTDOWN|SSL_SENT_SHUTDOWN; + SSL_set_quiet_shutdown(c->ssl->connection, 1); +diff -urN nginx/src/event/ngx_event_udp.h nginx/src/event/ngx_event_udp.h +--- nginx/src/event/ngx_event_udp.h 2023-10-03 16:49:31.264377900 +0530 ++++ nginx/src/event/ngx_event_udp.h 2023-09-19 12:57:46.301787700 +0530 +@@ -33,10 +33,11 @@ + #if (NGX_HAVE_IP_PKTINFO) + struct in_pktinfo pkt; + #endif +- +-#if (NGX_HAVE_INET6 && NGX_HAVE_IPV6_RECVPKTINFO) +- struct in6_pktinfo pkt6; +-#endif ++// AMB : Code is commented in order to move compilation forward and detect any errors that arise. ++// See https://trac.nginx.org/nginx/ticket/2312 ++//#if (NGX_HAVE_INET6 && NGX_HAVE_IPV6_RECVPKTINFO) ++// struct in6_pktinfo pkt6; ++//#endif + } ngx_addrinfo_t; + + size_t ngx_set_srcaddr_cmsg(struct cmsghdr *cmsg, diff --git a/thirdparty/nginx/patches/nginx_src_http.patch b/thirdparty/nginx/patches/nginx_src_http.patch new file mode 100644 index 000000000..675e1b47e --- /dev/null +++ b/thirdparty/nginx/patches/nginx_src_http.patch @@ -0,0 +1,1687 @@ +diff -urN nginx/src/http/ngx_http.h nginx/src/http/ngx_http.h +--- nginx/src/http/ngx_http.h 2023-08-18 17:02:49.306835700 +0530 ++++ nginx/src/http/ngx_http.h 2023-09-14 18:47:12.444732400 +0530 +@@ -32,6 +32,9 @@ + #include + #include + #include ++// Zimbra customizations start here (Jira Tickets: ) ++#include ++// Zimbra customizations end here + #include + #include + +diff -urN nginx/src/http/ngx_http_core_module.c nginx/src/http/ngx_http_core_module.c +--- nginx/src/http/ngx_http_core_module.c 2023-08-18 17:02:49.309830000 +0530 ++++ nginx/src/http/ngx_http_core_module.c 2023-10-02 01:02:04.912075500 +0530 +@@ -777,7 +777,14 @@ + NULL }, + + #endif +- ++// Zimbra customizations start here (Jira Tickets: ) ++ { ngx_string("exact_version_check"), ++ 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, exact_version_check), ++ NULL }, ++// Zimbra customizations end here + ngx_null_command + }; + +@@ -3630,7 +3637,9 @@ + clcf->open_file_cache_min_uses = NGX_CONF_UNSET_UINT; + clcf->open_file_cache_errors = NGX_CONF_UNSET; + clcf->open_file_cache_events = NGX_CONF_UNSET; +- ++// Zimbra customizations start here (Jira Tickets: ) ++ clcf->exact_version_check = NGX_CONF_UNSET; ++// Zimbra customizations end here + #if (NGX_HTTP_GZIP) + clcf->gzip_vary = NGX_CONF_UNSET; + clcf->gzip_http_version = NGX_CONF_UNSET_UINT; +diff -urN nginx/src/http/ngx_http_core_module.h nginx/src/http/ngx_http_core_module.h +--- nginx/src/http/ngx_http_core_module.h 2023-08-18 17:02:49.313817300 +0530 ++++ nginx/src/http/ngx_http_core_module.h 2023-10-02 01:02:51.567052200 +0530 +@@ -404,6 +404,9 @@ + ngx_flag_t chunked_transfer_encoding; /* chunked_transfer_encoding */ + ngx_flag_t etag; /* etag */ + ++// Zimbra customizations start here (Jira Tickets: ) ++ ngx_flag_t exact_version_check; /* added by zimbra for version check */ ++// Zimbra customizations end here + #if (NGX_HTTP_GZIP) + ngx_flag_t gzip_vary; /* gzip_vary */ + +diff -urN nginx/src/http/ngx_http_upstream.c nginx/src/http/ngx_http_upstream.c +--- nginx/src/http/ngx_http_upstream.c 2023-08-18 17:02:49.329775600 +0530 ++++ nginx/src/http/ngx_http_upstream.c 2023-10-02 01:18:32.519472600 +0530 +@@ -8,7 +8,9 @@ + #include + #include + #include +- ++// Zimbra customizations start here (Jira Tickets: ) ++#include ++// Zimbra customizations end here + + #if (NGX_HTTP_CACHE) + static ngx_int_t ngx_http_upstream_cache(ngx_http_request_t *r, +@@ -546,6 +548,9 @@ + { + ngx_str_t *host; + ngx_uint_t i; ++// Zimbra customizations start here (Jira Tickets: ) ++ ngx_int_t rc; ++// Zimbra customizations end here + ngx_resolver_ctx_t *ctx, temp; + ngx_http_cleanup_t *cln; + ngx_http_upstream_t *u; +@@ -794,8 +799,11 @@ + #if (NGX_HTTP_SSL) + u->ssl_name = uscf->host; + #endif +- +- if (uscf->peer.init(r, uscf) != NGX_OK) { ++// Zimbra customizations start here (Jira Tickets: ) ++ rc = uscf->peer.init(r, uscf); ++ if (rc != NGX_OK) { ++ if (rc == NGX_AGAIN) return; /* added by zimbra to support async peer init */ ++// Zimbra customizations start here (Jira Tickets: ) + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; +@@ -1552,7 +1560,9 @@ + } + + if (rc == NGX_DECLINED) { +- ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR); ++// Zimbra customizations start here (Jira Tickets: ) ++ ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_HTTP_403); ++// Zimbra customizations end here + return; + } + +@@ -1682,6 +1692,9 @@ + return; + } + ++// Zimbra customizations start here (Jira Tickets: ) ++ ngx_http_upstream_rr_peer_data_t *rrp = (ngx_http_upstream_rr_peer_data_t *)(u->peer.data); ++// Zimbra customizations end here + if (u->conf->ssl_server_name || u->conf->ssl_verify) { + if (ngx_http_upstream_ssl_name(r, u, c) != NGX_OK) { + ngx_http_upstream_finalize_request(r, u, +@@ -1701,8 +1714,9 @@ + return; + } + } +- +- if (u->conf->ssl_session_reuse) { ++// Zimbra customizations start here (Jira Tickets: ) ++ if (u->conf->ssl_session_reuse && rrp->current != NGX_INVALID_ARRAY_INDEX) { ++// Zimbra customizations end here + c->ssl->save_session = ngx_http_upstream_ssl_save_session; + + if (u->peer.set_session(&u->peer, u->peer.data) != NGX_OK) { +@@ -4302,7 +4316,9 @@ + { + ngx_msec_t timeout; + ngx_uint_t status, state; +- ++// Zimbra customizations start here (Jira Tickets: ) ++ ngx_zm_lookup_conf_t *zlcf; ++// Zimbra customizations end here + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http next upstream, %xi", ft_type); + +@@ -4386,11 +4402,19 @@ + { + ft_type |= NGX_HTTP_UPSTREAM_FT_NON_IDEMPOTENT; + } +- ++// Zimbra customizations start here (Jira Tickets: ) ++ zlcf = (ngx_zm_lookup_conf_t *) ++ ngx_get_conf (ngx_cycle->conf_ctx, ngx_zm_lookup_module); ++// Zimbra customizations end here + if (u->peer.tries == 0 + || ((u->conf->next_upstream & ft_type) != ft_type) + || (u->request_sent && r->request_body_no_buffering) +- || (timeout && ngx_current_msec - u->peer.start_time >= timeout)) ++// Zimbra customizations start here (Jira Tickets: ) ++ || (timeout && ngx_current_msec - u->peer.start_time >= timeout) ++ || (r->method == NGX_HTTP_POST && !((r->uri.len == 1 && ++ r->uri.data[r->uri.len - 1] == '/') || ++ (ngx_strncasecmp(r->uri.data, zlcf->url.data, zlcf->url.len) == 0)))) ++// Zimbra customizations end here + { + #if (NGX_HTTP_CACHE) + +@@ -6034,7 +6058,10 @@ + |NGX_HTTP_UPSTREAM_MAX_FAILS + |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT + |NGX_HTTP_UPSTREAM_DOWN +- |NGX_HTTP_UPSTREAM_BACKUP); ++ |NGX_HTTP_UPSTREAM_BACKUP ++// Zimbra customizations start here (Jira Tickets: ) ++ |NGX_HTTP_UPSTREAM_VERSION); ++// Zimbra customizations end here + if (uscf == NULL) { + return NGX_CONF_ERROR; + } +@@ -6120,6 +6147,9 @@ + return NGX_CONF_ERROR; + } + ++// Zimbra customizations start here (Jira Tickets: ) /* added by zimbra to support async upstream peer choose */ ++ uscf->connect = ngx_http_upstream_connect; ++// Zimbra customizations end here + return rv; + } + +@@ -6130,7 +6160,9 @@ + ngx_http_upstream_srv_conf_t *uscf = conf; + + time_t fail_timeout; +- ngx_str_t *value, s; ++// Zimbra customizations start here (Jira Tickets: ) ++ ngx_str_t *value, s, version; ++// Zimbra customizations end here + ngx_url_t u; + ngx_int_t weight, max_conns, max_fails; + ngx_uint_t i; +@@ -6149,7 +6181,10 @@ + max_conns = 0; + max_fails = 1; + fail_timeout = 10; +- ++// Zimbra customizations start here (Jira Tickets: ) ++ version.len = 0; ++ version.data = NULL; ++// Zimbra customizations end here + for (i = 2; i < cf->args->nelts; i++) { + + if (ngx_strncmp(value[i].data, "weight=", 7) == 0) { +@@ -6236,7 +6271,21 @@ + + continue; + } ++// Zimbra customizations start here (Jira Tickets: ) ++ if (ngx_strncmp(value[i].data, "version=", 8) == 0) { + ++ if (!(uscf->flags & NGX_HTTP_UPSTREAM_VERSION)) { ++ goto invalid; ++ } ++ ++ s.len = value[i].len - 8; ++ s.data = &value[i].data[8]; ++ ++ version = s; ++ ++ continue; ++ } ++// Zimbra customizations end here + goto invalid; + } + +@@ -6261,7 +6310,9 @@ + us->max_conns = max_conns; + us->max_fails = max_fails; + us->fail_timeout = fail_timeout; +- ++// Zimbra customizations start here (Jira Tickets: ) ++ us->version = version; ++// Zimbra customizations start here (Jira Tickets: ) + return NGX_CONF_OK; + + invalid: +diff -urN nginx/src/http/ngx_http_upstream.h nginx/src/http/ngx_http_upstream.h +--- nginx/src/http/ngx_http_upstream.h 2023-07-23 13:21:27.083727300 +0530 ++++ nginx/src/http/ngx_http_upstream.h 2023-09-14 18:47:12.557564900 +0530 +@@ -99,6 +99,9 @@ + ngx_uint_t max_conns; + ngx_uint_t max_fails; + time_t fail_timeout; ++// Zimbra customizations start here (Jira Tickets: ) ++ ngx_str_t version; ++// Zimbra customizations end here + ngx_msec_t slow_start; + ngx_uint_t down; + +@@ -115,6 +118,9 @@ + #define NGX_HTTP_UPSTREAM_FAIL_TIMEOUT 0x0008 + #define NGX_HTTP_UPSTREAM_DOWN 0x0010 + #define NGX_HTTP_UPSTREAM_BACKUP 0x0020 ++// Zimbra customizations start here (Jira Tickets: ) ++#define NGX_HTTP_UPSTREAM_VERSION 0x0040 ++// Zimbra customizations end here + #define NGX_HTTP_UPSTREAM_MAX_CONNS 0x0100 + + +@@ -131,6 +137,9 @@ + in_port_t port; + ngx_uint_t no_port; /* unsigned no_port:1 */ + ++// Zimbra customizations start here (Jira Tickets: ) // added by zimbra to support async upstream peer init ++ void (* connect)(ngx_http_request_t *r, ngx_http_upstream_t *u); ++// Zimbra customizations end here + #if (NGX_HTTP_UPSTREAM_ZONE) + ngx_shm_zone_t *shm_zone; + #endif +diff -urN nginx/src/http/ngx_http_upstream_fair.c nginx/src/http/ngx_http_upstream_fair.c +--- nginx/src/http/ngx_http_upstream_fair.c 1970-01-01 05:30:00.000000000 +0530 ++++ nginx/src/http/ngx_http_upstream_fair.c 2023-03-04 13:25:59.915137900 +0530 +@@ -0,0 +1,1295 @@ ++/* ++ * ***** BEGIN LICENSE BLOCK ***** ++ * Zimbra Collaboration Suite Server ++ * Copyright (C) 2011 Zimbra Software, LLC. ++ * ++ * The contents of this file are subject to the Zimbra Public License ++ * Version 1.4 ("License"); you may not use this file except in ++ * compliance with the License. You may obtain a copy of the License at ++ * http://www.zimbra.com/license. ++ * ++ * Software distributed under the License is distributed on an "AS IS" ++ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. ++ * ***** END LICENSE BLOCK ***** ++ */ ++ ++#include ++#include ++#include ++ ++#if 0 ++static char *ngx_http_upstream_fair(ngx_conf_t *cf, ngx_command_t *cmd, ++ void *conf); ++static ngx_chain_t *ngx_http_upstream_fair_report_status(ngx_http_request_t *r, ++ ngx_int_t *length); ++#endif ++ ++#if (NGX_HTTP_SSL) ++static ngx_int_t ngx_http_upstream_fair_set_session(ngx_peer_connection_t *pc, ++ void *data); ++static void ngx_http_upstream_fair_save_session(ngx_peer_connection_t *pc, ++ void *data); ++#endif ++ ++static ngx_http_module_t ngx_http_upstream_fair_module_ctx = { ++ NULL, /* preconfiguration */ ++ NULL, /* 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 */ ++ ++#if 0 ++ ngx_http_upstream_fair_report_status, ++#endif ++}; ++ ++ ++ngx_module_t ngx_http_upstream_fair_module = { ++ NGX_MODULE_V1, ++ &ngx_http_upstream_fair_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_uint_t ngx_http_upstream_fair_shm_size; ++static ngx_shm_zone_t * ngx_http_upstream_fair_shm_zone; ++static ngx_rbtree_t * ngx_http_upstream_fair_rbtree; ++static ngx_uint_t ngx_http_upstream_fair_generation; ++ ++ngx_uint_t *shm_size = &ngx_http_upstream_fair_shm_size; ++ ++static int ++ngx_http_upstream_fair_compare_rbtree_node(const ngx_rbtree_node_t *v_left, ++ const ngx_rbtree_node_t *v_right) ++{ ++ ngx_http_upstream_fair_shm_block_t *left, *right; ++ ++ left = (ngx_http_upstream_fair_shm_block_t *) v_left; ++ right = (ngx_http_upstream_fair_shm_block_t *) v_right; ++ ++ if (left->generation < right->generation) { ++ return -1; ++ } else if (left->generation > right->generation) { ++ return 1; ++ } else { /* left->generation == right->generation */ ++ if (left->peers < right->peers) { ++ return -1; ++ } else if (left->peers > right->peers) { ++ return 1; ++ } else { ++ return 0; ++ } ++ } ++} ++ ++/* ++ * generic functions start here ++ */ ++static void ++ngx_rbtree_generic_insert(ngx_rbtree_node_t *temp, ++ ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel, ++ int (*compare)(const ngx_rbtree_node_t *left, const ngx_rbtree_node_t *right)) ++{ ++ for ( ;; ) { ++ if (node->key < temp->key) { ++ ++ if (temp->left == sentinel) { ++ temp->left = node; ++ break; ++ } ++ ++ temp = temp->left; ++ ++ } else if (node->key > temp->key) { ++ ++ if (temp->right == sentinel) { ++ temp->right = node; ++ break; ++ } ++ ++ temp = temp->right; ++ ++ } else { /* node->key == temp->key */ ++ if (compare(node, temp) < 0) { ++ ++ if (temp->left == sentinel) { ++ temp->left = node; ++ break; ++ } ++ ++ temp = temp->left; ++ ++ } else { ++ ++ if (temp->right == sentinel) { ++ temp->right = node; ++ break; ++ } ++ ++ temp = temp->right; ++ } ++ } ++ } ++ ++ node->parent = temp; ++ node->left = sentinel; ++ node->right = sentinel; ++ ngx_rbt_red(node); ++} ++ ++#define NGX_BITVECTOR_ELT_SIZE (sizeof(uintptr_t) * 8) ++ ++static uintptr_t * ++ngx_bitvector_alloc(ngx_pool_t *pool, ngx_uint_t size, uintptr_t *small) ++{ ++ ngx_uint_t nelts = (size + NGX_BITVECTOR_ELT_SIZE - 1) / NGX_BITVECTOR_ELT_SIZE; ++ ++ if (small && nelts == 1) { ++ *small = 0; ++ return small; ++ } ++ ++ return ngx_pcalloc(pool, nelts * NGX_BITVECTOR_ELT_SIZE); ++} ++ ++static ngx_int_t ++ngx_bitvector_test(uintptr_t *bv, ngx_uint_t bit) ++{ ++ ngx_uint_t n, m; ++ ++ n = bit / NGX_BITVECTOR_ELT_SIZE; ++ m = 1 << (bit % NGX_BITVECTOR_ELT_SIZE); ++ ++ return bv[n] & m; ++} ++ ++static void ++ngx_bitvector_set(uintptr_t *bv, ngx_uint_t bit) ++{ ++ ngx_uint_t n, m; ++ ++ n = bit / NGX_BITVECTOR_ELT_SIZE; ++ m = 1 << (bit % NGX_BITVECTOR_ELT_SIZE); ++ ++ bv[n] |= m; ++} ++ ++/* ++ * generic functions end here ++ */ ++ ++static void ++ngx_http_upstream_fair_rbtree_insert(ngx_rbtree_node_t *temp, ++ ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) { ++ ++ ngx_rbtree_generic_insert(temp, node, sentinel, ++ ngx_http_upstream_fair_compare_rbtree_node); ++} ++ ++ ++static ngx_int_t ++ngx_http_upstream_fair_init_shm_zone(ngx_shm_zone_t *shm_zone, void *data) ++{ ++ ngx_slab_pool_t *shpool; ++ ngx_rbtree_t *tree; ++ ngx_rbtree_node_t *sentinel; ++ ++ if (data) { ++ shm_zone->data = data; ++ return NGX_OK; ++ } ++ ++ shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; ++ tree = ngx_slab_alloc(shpool, sizeof *tree); ++ if (tree == NULL) { ++ return NGX_ERROR; ++ } ++ ++ sentinel = ngx_slab_alloc(shpool, sizeof *sentinel); ++ if (sentinel == NULL) { ++ return NGX_ERROR; ++ } ++ ++ ngx_rbtree_sentinel_init(sentinel); ++ tree->root = sentinel; ++ tree->sentinel = sentinel; ++ tree->insert = ngx_http_upstream_fair_rbtree_insert; ++ shm_zone->data = tree; ++ ngx_http_upstream_fair_rbtree = tree; ++ ++ return NGX_OK; ++} ++ ++#if 0 ++static char * ++ngx_http_upstream_fair(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) ++{ ++ ngx_http_upstream_srv_conf_t *uscf; ++ ngx_uint_t i; ++ ngx_uint_t extra_peer_flags = 0; ++ ++ for (i = 1; i < cf->args->nelts; i++) { ++ ngx_str_t *value = cf->args->elts; ++ if (ngx_strcmp(value[i].data, "no_rr") == 0) { ++ extra_peer_flags |= NGX_HTTP_UPSTREAM_FAIR_NO_RR; ++ } else if (ngx_strcmp(value[i].data, "weight_mode=peak") == 0) { ++ if (extra_peer_flags & NGX_HTTP_UPSTREAM_FAIR_WEIGHT_MODE_MASK) { ++ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "weight_mode= options are mutually exclusive"); ++ return NGX_CONF_ERROR; ++ } ++ extra_peer_flags |= NGX_HTTP_UPSTREAM_FAIR_WEIGHT_MODE_PEAK; ++ } else if (ngx_strcmp(value[i].data, "weight_mode=idle") == 0) { ++ if (extra_peer_flags & NGX_HTTP_UPSTREAM_FAIR_WEIGHT_MODE_MASK) { ++ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "weight_mode= options are mutually exclusive"); ++ return NGX_CONF_ERROR; ++ } ++ extra_peer_flags |= NGX_HTTP_UPSTREAM_FAIR_WEIGHT_MODE_IDLE; ++ } else { ++ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "Invalid `fair' parameter `%V'", &value[i]); ++ return NGX_CONF_ERROR; ++ } ++ } ++ ++ uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module); ++ ++ uscf->peer.init_upstream = ngx_http_upstream_init_fair; ++ ++ uscf->flags = NGX_HTTP_UPSTREAM_CREATE ++ |NGX_HTTP_UPSTREAM_WEIGHT ++ |NGX_HTTP_UPSTREAM_MAX_FAILS ++ |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT ++ |NGX_HTTP_UPSTREAM_DOWN ++ |NGX_HTTP_UPSTREAM_VERSION ++ |extra_peer_flags; ++ ++ return NGX_CONF_OK; ++} ++#endif ++ ++static ngx_int_t ++ngx_http_upstream_cmp_servers(const void *one, const void *two) ++{ ++ const ngx_http_upstream_fair_peer_t *first, *second; ++ ++ first = one; ++ second = two; ++ ++ return (first->weight < second->weight); ++} ++ ++ ++/* TODO: Actually support backup servers */ ++static ngx_int_t ++ngx_http_upstream_init_fair_rr(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us) ++{ ++ ngx_url_t u; ++ ngx_uint_t i, j, n; ++ ngx_http_upstream_server_t *server; ++ ngx_http_upstream_fair_peers_t *peers, *backup; ++ ++ if (us->servers) { ++ server = us->servers->elts; ++ ++ n = 0; ++ ++ for (i = 0; i < us->servers->nelts; i++) { ++ if (server[i].backup) { ++ continue; ++ } ++ ++ n += server[i].naddrs; ++ } ++ ++ peers = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_fair_peers_t) ++ + sizeof(ngx_http_upstream_fair_peer_t) * (n - 1)); ++ if (peers == NULL) { ++ return NGX_ERROR; ++ } ++ ++ peers->number = n; ++ peers->name = &us->host; ++ ++ n = 0; ++ ++ for (i = 0; i < us->servers->nelts; i++) { ++ for (j = 0; j < server[i].naddrs; j++) { ++ if (server[i].backup) { ++ continue; ++ } ++ ++ peers->peer[n].sockaddr = server[i].addrs[j].sockaddr; ++ peers->peer[n].socklen = server[i].addrs[j].socklen; ++ peers->peer[n].name = server[i].addrs[j].name; ++ peers->peer[n].max_fails = server[i].max_fails; ++ peers->peer[n].fail_timeout = server[i].fail_timeout; ++ peers->peer[n].down = server[i].down; ++ peers->peer[n].weight = server[i].down ? 0 : server[i].weight; ++ peers->peer[n].version = server[i].version; ++ n++; ++ } ++ } ++ ++ us->peer.data = peers; ++ ++ ngx_sort(&peers->peer[0], (size_t) n, ++ sizeof(ngx_http_upstream_fair_peer_t), ++ ngx_http_upstream_cmp_servers); ++ ++ /* backup servers */ ++ ++ n = 0; ++ ++ for (i = 0; i < us->servers->nelts; i++) { ++ if (!server[i].backup) { ++ continue; ++ } ++ ++ n += server[i].naddrs; ++ } ++ ++ if (n == 0) { ++ return NGX_OK; ++ } ++ ++ backup = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_fair_peers_t) ++ + sizeof(ngx_http_upstream_fair_peer_t) * (n - 1)); ++ if (backup == NULL) { ++ return NGX_ERROR; ++ } ++ ++ backup->number = n; ++ backup->name = &us->host; ++ ++ n = 0; ++ ++ for (i = 0; i < us->servers->nelts; i++) { ++ for (j = 0; j < server[i].naddrs; j++) { ++ if (!server[i].backup) { ++ continue; ++ } ++ ++ backup->peer[n].sockaddr = server[i].addrs[j].sockaddr; ++ backup->peer[n].socklen = server[i].addrs[j].socklen; ++ backup->peer[n].name = server[i].addrs[j].name; ++ backup->peer[n].weight = server[i].weight; ++ backup->peer[n].max_fails = server[i].max_fails; ++ backup->peer[n].fail_timeout = server[i].fail_timeout; ++ backup->peer[n].down = server[i].down; ++ backup->peer[n].version = server[i].version; ++ n++; ++ } ++ } ++ ++ peers->next = backup; ++ ++ ngx_sort(&backup->peer[0], (size_t) n, ++ sizeof(ngx_http_upstream_fair_peer_t), ++ ngx_http_upstream_cmp_servers); ++ ++ return NGX_OK; ++ } ++ ++ ++ /* an upstream implicitly defined by proxy_pass, etc. */ ++ ++ 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); ++ return NGX_ERROR; ++ } ++ ++ ngx_memzero(&u, sizeof(ngx_url_t)); ++ ++ u.host = us->host; ++ u.port = (in_port_t) us->port; ++ ++ if (ngx_inet_resolve_host(cf->pool, &u) != NGX_OK) { ++ if (u.err) { ++ ngx_log_error(NGX_LOG_EMERG, cf->log, 0, ++ "%s in upstream \"%V\" in %s:%ui", ++ u.err, &us->host, us->file_name, us->line); ++ } ++ ++ return NGX_ERROR; ++ } ++ ++ n = u.naddrs; ++ ++ peers = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_fair_peers_t) ++ + sizeof(ngx_http_upstream_fair_peer_t) * (n - 1)); ++ if (peers == NULL) { ++ return NGX_ERROR; ++ } ++ ++ peers->number = n; ++ peers->name = &us->host; ++ ++ for (i = 0; i < u.naddrs; i++) { ++ peers->peer[i].sockaddr = u.addrs[i].sockaddr; ++ peers->peer[i].socklen = u.addrs[i].socklen; ++ peers->peer[i].name = u.addrs[i].name; ++ peers->peer[i].weight = 1; ++ peers->peer[i].max_fails = 1; ++ peers->peer[i].fail_timeout = 10; ++ peers->peer[n].version.len = 0; ++ peers->peer[n].version.data = NULL; ++ } ++ ++ us->peer.data = peers; ++ ++ /* implicitly defined upstream has no backup servers */ ++ ++ return NGX_OK; ++} ++ ++ngx_int_t ++ngx_http_upstream_init_fair(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us) ++{ ++ ngx_http_upstream_fair_peers_t *peers; ++ ngx_uint_t n; ++ ngx_str_t *shm_name; ++ ++ /* do the dirty work using rr module */ ++ if (ngx_http_upstream_init_fair_rr(cf, us) != NGX_OK) { ++ return NGX_ERROR; ++ } ++ ++ /* setup our wrapper around rr */ ++ peers = ngx_palloc(cf->pool, sizeof *peers); ++ if (peers == NULL) { ++ return NGX_ERROR; ++ } ++ peers = us->peer.data; ++ n = peers->number; ++ ++ shm_name = ngx_palloc(cf->pool, sizeof *shm_name); ++ shm_name->len = sizeof("upstream_fair") - 1; ++ shm_name->data = (unsigned char *) "upstream_fair"; ++ ++ if (ngx_http_upstream_fair_shm_size == 0) { ++ ngx_log_error(NGX_LOG_DEBUG_ZIMBRA, cf->log, 0, "The upstream_fair_shm_size is 0. The upstream_fair_shm_size value must be at least %udKiB", (8 * ngx_pagesize) >> 10); ++ ngx_http_upstream_fair_shm_size = 8 * ngx_pagesize; ++ } ++ ++ ngx_log_error(NGX_LOG_DEBUG_ZIMBRA, cf->log, 0, "The upstream_fair_shm_size value is %udKiB", ngx_http_upstream_fair_shm_size >> 10); ++ ++ ngx_http_upstream_fair_shm_zone = ngx_shared_memory_add( ++ cf, shm_name, ngx_http_upstream_fair_shm_size, &ngx_http_upstream_fair_module); ++ if (ngx_http_upstream_fair_shm_zone == NULL) { ++ return NGX_ERROR; ++ } ++ ngx_http_upstream_fair_shm_zone->init = ngx_http_upstream_fair_init_shm_zone; ++ ++ peers->shared = NULL; ++ peers->current = n - 1; ++ peers->no_rr = 1; ++ peers->weight_mode = WM_PEAK; ++ peers->size_err = 0; ++ ngx_http_upstream_fair_generation++; ++ ++ us->peer.init = ngx_http_upstream_init_fair_peer; ++ ++ return NGX_OK; ++} ++ ++ ++static void ++ngx_http_upstream_fair_update_nreq(ngx_http_upstream_fair_peer_data_t *fp, int delta, ngx_log_t *log) ++{ ++#if (NGX_DEBUG) ++ ngx_uint_t nreq; ++ ngx_uint_t total_nreq; ++ ++ nreq = (fp->peers->peer[fp->current].shared->nreq += delta); ++ total_nreq = (fp->peers->shared->total_nreq += delta); ++ ++ ngx_log_debug6(NGX_LOG_DEBUG_HTTP, log, 0, ++ "[upstream_fair] nreq for peer %ui @ %p/%p now %d, total %d, delta %d", ++ fp->current, fp->peers, fp->peers->peer[fp->current].shared, nreq, ++ total_nreq, delta); ++#endif ++} ++ ++/* ++ * SCHED_COUNTER_BITS is the portion of an ngx_uint_t which represents ++ * the req_delta part (number of requests serviced on _other_ ++ * backends). The rest (top bits) represents the number of currently ++ * processed requests. ++ * ++ * The value is not too critical because overflow is handled via ++ * saturation. With the default value of 20, scheduling is exact for ++ * fewer than 4k concurrent requests per backend (on 32-bit ++ * architectures) and fewer than 1M concurrent requests to all backends ++ * together. Beyond these limits, the algorithm essentially falls back ++ * to pure weighted round-robin. ++ * ++ * A higher score means less suitable. ++ * ++ * The `delta' parameter is bit-negated so that high values yield low ++ * scores and get chosen more often. ++ */ ++ ++#define SCHED_COUNTER_BITS 20 ++#define SCHED_NREQ_MAX ((~0UL) >> SCHED_COUNTER_BITS) ++#define SCHED_COUNTER_MAX ((1 << SCHED_COUNTER_BITS) - 1) ++#define SCHED_SCORE(nreq,delta) (((nreq) << SCHED_COUNTER_BITS) | (~(delta) & SCHED_COUNTER_MAX)) ++#define ngx_upstream_fair_min(a,b) (((a) < (b)) ? (a) : (b)) ++ ++static ngx_uint_t ++ngx_http_upstream_fair_sched_score(ngx_peer_connection_t *pc, ++ ngx_http_upstream_fair_peer_data_t *fp, ++ ngx_uint_t n) ++{ ++ ngx_http_upstream_fair_peer_t *peer = &fp->peers->peer[n]; ++ ngx_http_upstream_fair_shared_t *fs = peer->shared; ++ ngx_uint_t req_delta = fp->peers->shared->total_requests - fs->last_req_id; ++ ++ /* sanity check */ ++ if ((ngx_int_t)fs->nreq < 0) { ++ ngx_log_error(NGX_LOG_WARN, pc->log, 0, "[upstream_fair] upstream %ui has negative nreq (%i)", n, fs->nreq); ++ return SCHED_SCORE(0, req_delta); ++ } ++ ++ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, pc->log, 0, "[upstream_fair] peer %ui: nreq = %i, req_delta = %ui", n, fs->nreq, req_delta); ++ ++ return SCHED_SCORE( ++ ngx_upstream_fair_min(fs->nreq, SCHED_NREQ_MAX), ++ ngx_upstream_fair_min(req_delta, SCHED_COUNTER_MAX)); ++} ++ ++/* ++ * the core of load balancing logic ++ */ ++ ++static ngx_int_t ++ngx_http_upstream_fair_try_peer(ngx_peer_connection_t *pc, ++ ngx_http_upstream_fair_peer_data_t *fp, ++ ngx_uint_t peer_id) ++{ ++ ngx_http_upstream_fair_peer_t *peer; ++ ++ if (ngx_bitvector_test(fp->tried, peer_id)) ++ return NGX_BUSY; ++ ++ peer = &fp->peers->peer[peer_id]; ++ ++ if (!peer->down) { ++ if (peer->max_fails == 0 || peer->shared->fails < peer->max_fails) { ++ return NGX_OK; ++ } ++ ++ if (ngx_time() - peer->accessed > peer->fail_timeout) { ++ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, pc->log, 0, "[upstream_fair] resetting fail count for peer %d, time delta %d > %d", ++ peer_id, ngx_time() - peer->accessed, peer->fail_timeout); ++ peer->shared->fails = 0; ++ return NGX_OK; ++ } ++ } ++ ++ return NGX_BUSY; ++} ++ ++static ngx_uint_t ++ngx_http_upstream_choose_fair_peer_idle(ngx_peer_connection_t *pc, ++ ngx_http_upstream_fair_peer_data_t *fp) ++{ ++ ngx_uint_t i, n; ++ ngx_uint_t npeers = fp->peers->number; ++ ngx_uint_t weight_mode = fp->peers->weight_mode; ++ ngx_uint_t best_idx = NGX_PEER_INVALID; ++ ngx_uint_t best_nreq = ~0U; ++ ++ for (i = 0, n = fp->current; i < npeers; i++, n = (n + 1) % npeers) { ++ ngx_uint_t nreq = fp->peers->peer[n].shared->nreq; ++ ngx_uint_t weight = fp->peers->peer[n].weight; ++ ngx_http_upstream_fair_peer_t *peer; ++ peer = &fp->peers->peer[n]; ++ ++ if (fp->peers->peer[n].shared->fails > 0) ++ continue; ++ ++ if (nreq >= weight || (nreq > 0 && weight_mode != WM_IDLE)) { ++ continue; ++ } ++ ++ if (ngx_http_upstream_fair_try_peer(pc, fp, n) != NGX_OK) { ++ continue; ++ } ++ ++ if (ngx_http_upstream_fair_peer_version_allowed(peer, fp, pc->log) == 0) { ++ continue; ++ } ++ ++ /* not in WM_IDLE+no_rr mode: the first completely idle backend gets chosen */ ++ if (weight_mode != WM_IDLE || !fp->peers->no_rr) { ++ best_idx = n; ++ break; ++ } ++ ++ /* in WM_IDLE+no_rr mode we actually prefer slightly loaded backends ++ * to totally idle ones, under the assumption that they're spawned ++ * on demand and can handle up to 'weight' concurrent requests ++ */ ++ if (best_idx == NGX_PEER_INVALID || nreq) { ++ if (best_nreq <= nreq) { ++ continue; ++ } ++ best_idx = n; ++ best_nreq = nreq; ++ } ++ } ++ ++ return best_idx; ++} ++ ++static ngx_int_t ++ngx_http_upstream_choose_fair_peer_busy(ngx_peer_connection_t *pc, ++ ngx_http_upstream_fair_peer_data_t *fp) ++{ ++ ngx_uint_t i, n; ++ ngx_uint_t npeers = fp->peers->number; ++ ngx_uint_t weight_mode = fp->peers->weight_mode; ++ ngx_uint_t best_idx = NGX_PEER_INVALID; ++ ngx_uint_t sched_score; ++ ngx_uint_t best_sched_score = ~0UL; ++ ++ /* ++ * calculate sched scores for all the peers, choosing the lowest one ++ */ ++ for (i = 0, n = fp->current; i < npeers; i++, n = (n + 1) % npeers) { ++ ngx_http_upstream_fair_peer_t *peer; ++ ngx_uint_t nreq; ++ ngx_uint_t weight; ++ ++ peer = &fp->peers->peer[n]; ++ nreq = fp->peers->peer[n].shared->nreq; ++ ++ if (weight_mode == WM_PEAK && nreq >= peer->weight) { ++ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, pc->log, 0, "[upstream_fair] backend %d has nreq %ui >= weight %ui in WM_PEAK mode", n, nreq, peer->weight); ++ continue; ++ } ++ ++ if (ngx_http_upstream_fair_try_peer(pc, fp, n) != NGX_OK) { ++ if (!pc->tries) { ++ ngx_log_debug(NGX_LOG_DEBUG_HTTP, pc->log, 0, "[upstream_fair] all backends exhausted"); ++ return NGX_PEER_INVALID; ++ } ++ ++ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, "[upstream_fair] backend %d already tried", n); ++ continue; ++ } ++ ++ if (ngx_http_upstream_fair_peer_version_allowed(peer, fp, pc->log) == 0) { ++ continue; ++ } ++ ++ sched_score = ngx_http_upstream_fair_sched_score(pc, fp, n); ++ ++ if (weight_mode == WM_DEFAULT) { ++ /* ++ * take peer weight into account ++ */ ++ weight = peer->shared->current_weight; ++ if (peer->max_fails) { ++ ngx_uint_t mf = peer->max_fails; ++ weight = peer->shared->current_weight * (mf - peer->shared->fails) / mf; ++ } ++ if (weight > 0) { ++ sched_score /= weight; ++ } ++ ngx_log_debug8(NGX_LOG_DEBUG_HTTP, pc->log, 0, "[upstream_fair] bss = %ui, ss = %ui (n = %d, w = %d/%d, f = %d/%d, weight = %d)", ++ best_sched_score, sched_score, n, peer->shared->current_weight, peer->weight, peer->shared->fails, peer->max_fails, weight); ++ } ++ ++ if (sched_score <= best_sched_score) { ++ best_idx = n; ++ best_sched_score = sched_score; ++ } ++ } ++ ++ return best_idx; ++} ++ ++static ngx_int_t ++ngx_http_upstream_choose_fair_peer(ngx_peer_connection_t *pc, ++ ngx_http_upstream_fair_peer_data_t *fp, ngx_uint_t *peer_id) ++{ ++ ngx_uint_t npeers; ++ ngx_uint_t best_idx = NGX_PEER_INVALID; ++ ngx_uint_t weight_mode; ++ ++ npeers = fp->peers->number; ++ weight_mode = fp->peers->weight_mode; ++ ++ /* just a single backend */ ++ if (npeers == 1) { ++ *peer_id = 0; ++ return NGX_OK; ++ } ++ ++ /* any idle backends? */ ++ best_idx = ngx_http_upstream_choose_fair_peer_idle(pc, fp); ++ if (best_idx != NGX_PEER_INVALID) { ++ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, "[upstream_fair] peer %i is idle", best_idx); ++ goto chosen; ++ } ++ ++ /* no idle backends, choose the least loaded one */ ++ best_idx = ngx_http_upstream_choose_fair_peer_busy(pc, fp); ++ if (best_idx != NGX_PEER_INVALID) { ++ goto chosen; ++ } ++ ++ return NGX_BUSY; ++ ++chosen: ++ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, "[upstream_fair] chose peer %i", best_idx); ++ *peer_id = best_idx; ++ ngx_bitvector_set(fp->tried, best_idx); ++ ++ if (weight_mode == WM_DEFAULT) { ++ ngx_http_upstream_fair_peer_t *peer = &fp->peers->peer[best_idx]; ++ ++ if (peer->shared->current_weight-- == 0) { ++ peer->shared->current_weight = peer->weight; ++ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0, "[upstream_fair] peer %d expired weight, reset to %d", best_idx, peer->weight); ++ } ++ } ++ return NGX_OK; ++} ++ ++ngx_int_t ++ngx_http_upstream_get_fair_peer(ngx_peer_connection_t *pc, void *data) ++{ ++ ngx_int_t ret; ++ ngx_uint_t peer_id, i; ++ ngx_http_upstream_fair_peer_data_t *fp = data; ++ ngx_http_upstream_fair_peer_t *peer; ++ ngx_atomic_t *lock; ++ ++ peer_id = fp->current; ++ fp->current = (fp->current + 1) % fp->peers->number; ++ ++ lock = &fp->peers->shared->lock; ++ ngx_spinlock(lock, ngx_pid, 1024); ++ ret = ngx_http_upstream_choose_fair_peer(pc, fp, &peer_id); ++ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, pc->log, 0, "[upstream_fair] fp->current = %d, peer_id = %d, ret = %d", ++ fp->current, peer_id, ret); ++ ++ if (pc) ++ pc->tries--; ++ ++ if (ret == NGX_BUSY) { ++ for (i = 0; i < fp->peers->number; i++) { ++ fp->peers->peer[i].shared->fails = 0; ++ } ++ ++ pc->name = fp->peers->name; ++ fp->current = NGX_PEER_INVALID; ++ ngx_spinlock_unlock(lock); ++ return NGX_BUSY; ++ } ++ ++ /* assert(ret == NGX_OK); */ ++ peer = &fp->peers->peer[peer_id]; ++ fp->current = peer_id; ++ if (!fp->peers->no_rr) { ++ fp->peers->current = peer_id; ++ } ++ pc->sockaddr = peer->sockaddr; ++ pc->socklen = peer->socklen; ++ pc->name = &peer->name; ++ ++ peer->shared->last_req_id = fp->peers->shared->total_requests; ++ ngx_http_upstream_fair_update_nreq(fp, 1, pc->log); ++ peer->shared->total_req++; ++ ngx_spinlock_unlock(lock); ++ return ret; ++} ++ ++ ++void ++ngx_http_upstream_free_fair_peer(ngx_peer_connection_t *pc, void *data, ++ ngx_uint_t state) ++{ ++ ngx_http_upstream_fair_peer_data_t *fp = data; ++ ngx_http_upstream_fair_peer_t *peer; ++ ngx_atomic_t *lock; ++ ++ ngx_log_debug4(NGX_LOG_DEBUG_HTTP, pc->log, 0, "[upstream_fair] fp->current = %d, state = %ui, pc->tries = %d, pc->data = %p", ++ fp->current, state, pc->tries, pc->data); ++ ++ if (fp->current == NGX_PEER_INVALID) { ++ return; ++ } ++ ++ lock = &fp->peers->shared->lock; ++ ngx_spinlock(lock, ngx_pid, 1024); ++ if (!ngx_bitvector_test(fp->done, fp->current)) { ++ ngx_bitvector_set(fp->done, fp->current); ++ ngx_http_upstream_fair_update_nreq(fp, -1, pc->log); ++ } ++ ++ if (fp->peers->number == 1) { ++ pc->tries = 0; ++ } ++ ++ if (state & NGX_PEER_FAILED) { ++ peer = &fp->peers->peer[fp->current]; ++ ++ peer->shared->fails++; ++ peer->accessed = ngx_time(); ++ } ++ ngx_spinlock_unlock(lock); ++} ++ ++/* ++ * walk through the rbtree, removing old entries and looking for ++ * a matching one -- compared by (cycle, peers) pair ++ * ++ * no attempt at optimisation is made, for two reasons: ++ * - the tree will be quite small, anyway ++ * - being called once per worker startup per upstream block, ++ * this code isn't really the hot path ++ */ ++static ngx_http_upstream_fair_shm_block_t * ++ngx_http_upstream_fair_walk_shm( ++ ngx_slab_pool_t *shpool, ++ ngx_rbtree_node_t *node, ++ ngx_rbtree_node_t *sentinel, ++ ngx_http_upstream_fair_peers_t *peers) ++{ ++ ngx_http_upstream_fair_shm_block_t *uf_node; ++ ngx_http_upstream_fair_shm_block_t *found_node = NULL; ++ ngx_http_upstream_fair_shm_block_t *tmp_node; ++ ++ if (node == sentinel) { ++ return NULL; ++ } ++ ++ /* visit left node */ ++ if (node->left != sentinel) { ++ tmp_node = ngx_http_upstream_fair_walk_shm(shpool, node->left, ++ sentinel, peers); ++ if (tmp_node) { ++ found_node = tmp_node; ++ } ++ } ++ ++ /* visit right node */ ++ if (node->right != sentinel) { ++ tmp_node = ngx_http_upstream_fair_walk_shm(shpool, node->right, ++ sentinel, peers); ++ if (tmp_node) { ++ found_node = tmp_node; ++ } ++ } ++ ++ /* visit current node */ ++ uf_node = (ngx_http_upstream_fair_shm_block_t *) node; ++ if (uf_node->generation != ngx_http_upstream_fair_generation) { ++ ngx_spinlock(&uf_node->lock, ngx_pid, 1024); ++ if (uf_node->total_nreq == 0) { ++ /* don't bother unlocking */ ++ ngx_rbtree_delete(ngx_http_upstream_fair_rbtree, node); ++ ngx_slab_free_locked(shpool, node); ++ } ++ ngx_spinlock_unlock(&uf_node->lock); ++ } else if (uf_node->peers == (uintptr_t) peers) { ++ found_node = uf_node; ++ } ++ ++ return found_node; ++} ++ ++static ngx_int_t ++ngx_http_upstream_fair_shm_alloc(ngx_http_upstream_fair_peers_t *usfp, ngx_log_t *log) ++{ ++ ngx_slab_pool_t *shpool; ++ ngx_uint_t i; ++ ++ if (usfp->shared) { ++ return NGX_OK; ++ } ++ ++ shpool = (ngx_slab_pool_t *)ngx_http_upstream_fair_shm_zone->shm.addr; ++ ++ ngx_shmtx_lock(&shpool->mutex); ++ ++ usfp->shared = ngx_http_upstream_fair_walk_shm(shpool, ++ ngx_http_upstream_fair_rbtree->root, ++ ngx_http_upstream_fair_rbtree->sentinel, ++ usfp); ++ ++ if (usfp->shared) { ++ ngx_shmtx_unlock(&shpool->mutex); ++ return NGX_OK; ++ } ++ ++ usfp->shared = ngx_slab_alloc_locked(shpool, ++ sizeof(ngx_http_upstream_fair_shm_block_t) + ++ (usfp->number - 1) * sizeof(ngx_http_upstream_fair_shared_t)); ++ ++ if (!usfp->shared) { ++ ngx_shmtx_unlock(&shpool->mutex); ++ if (!usfp->size_err) { ++ ngx_log_error(NGX_LOG_EMERG, log, 0, ++ "upstream_fair_shm_size too small (current value is %udKiB)", ++ ngx_http_upstream_fair_shm_size >> 10); ++ usfp->size_err = 1; ++ } ++ return NGX_ERROR; ++ } ++ ++ usfp->shared->node.key = ngx_crc32_short((u_char *) &ngx_cycle, sizeof ngx_cycle) ^ ++ ngx_crc32_short((u_char *) &usfp, sizeof(usfp)); ++ ++ usfp->shared->generation = ngx_http_upstream_fair_generation; ++ usfp->shared->peers = (uintptr_t) usfp; ++ usfp->shared->total_nreq = 0; ++ usfp->shared->total_requests = 0; ++ ++ for (i = 0; i < usfp->number; i++) { ++ usfp->shared->stats[i].nreq = 0; ++ usfp->shared->stats[i].last_req_id = 0; ++ usfp->shared->stats[i].total_req = 0; ++ } ++ ++ ngx_rbtree_insert(ngx_http_upstream_fair_rbtree, &usfp->shared->node); ++ ++ ngx_shmtx_unlock(&shpool->mutex); ++ return NGX_OK; ++} ++ ++ngx_int_t ++ngx_http_upstream_init_fair_peer(ngx_http_request_t *r, ++ ngx_http_upstream_srv_conf_t *us) ++{ ++ ngx_http_upstream_fair_peer_data_t *fp; ++ ngx_http_upstream_fair_peers_t *usfp; ++ ngx_uint_t n; ++ ngx_http_core_loc_conf_t *clcf; ++ ++ fp = r->upstream->peer.data; ++ ++ if (fp == NULL) { ++ fp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_fair_peer_data_t)); ++ if (fp == NULL) { ++ return NGX_ERROR; ++ } ++ ++ r->upstream->peer.data = fp; ++ } ++ ++ usfp = us->peer.data; ++ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); ++ if (clcf) { ++ fp->exact_version_check = clcf->exact_version_check; ++ } ++ fp->version.data = (u_char*) ""; ++ fp->version.len = 0; ++ ++ fp->tried = ngx_bitvector_alloc(r->pool, usfp->number, &fp->data); ++ fp->done = ngx_bitvector_alloc(r->pool, usfp->number, &fp->data2); ++ ++ if (fp->tried == NULL || fp->done == NULL) { ++ return NGX_ERROR; ++ } ++ ++ /* set up shared memory area */ ++ ngx_http_upstream_fair_shm_alloc(usfp, r->connection->log); ++ ++ fp->current = usfp->current; ++ fp->peers = usfp; ++ usfp->shared->total_requests++; ++ ++ for (n = 0; n < usfp->number; n++) { ++ usfp->peer[n].shared = &usfp->shared->stats[n]; ++ } ++ ++ r->upstream->peer.get = ngx_http_upstream_get_fair_peer; ++ r->upstream->peer.free = ngx_http_upstream_free_fair_peer; ++ r->upstream->peer.tries = usfp->number; ++#if (NGX_HTTP_SSL) ++ r->upstream->peer.set_session = ++ ngx_http_upstream_fair_set_session; ++ r->upstream->peer.save_session = ++ ngx_http_upstream_fair_save_session; ++#endif ++ ++ return NGX_OK; ++} ++ ++/* check if this upstream peer is allowed (same exact version/same major-minor version based on exact_version_check flag) ++ * return 1 if allowed, 0 if not */ ++ngx_flag_t ++ngx_http_upstream_fair_peer_version_allowed(ngx_http_upstream_fair_peer_t *peer, void* data, ++ ngx_log_t* log) { ++ ngx_http_upstream_fair_peer_data_t* fp = data; ++ ngx_uint_t peer_version[2], auth_token_version[2]; ++ ngx_flag_t f = 1; ++ ngx_uint_t j = 0; ++ char *ptr = NULL; ++ ++ /* Version check enforcement when the auth_token contains a version */ ++ if (fp->version.len && peer->version.len == 0) { ++ // Peer does not have version set in nginx conf ++ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0, ++ "auth_token has a version but the peer doesn't, skipping peer"); ++ return 0; ++ } else if (fp->version.len && peer->version.len) { ++ // Peer has version set in nginx conf ++ if (fp->exact_version_check) { ++ // if the server version check is non-permissive (needs to be an exact match) ++ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0, ++ "exact_version_check is on"); ++ if (fp->version.len != peer->version.len || ngx_memcmp(fp->version.data, peer->version.data, fp->version.len)) { ++ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, ++ "skipping peer at a different version %V", &peer->version); ++ f = 0; ++ } ++ } else { ++ // if the server version check is permissive (eg. all 8.5.x will be treated same by nginx) ++ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0, ++ "exact_version_check is off"); ++ ++ ptr = strtok ((char *)fp->version.data,"."); ++ while (ptr != NULL && (j < 2)) { ++ auth_token_version[j] = atoi(ptr); ++ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, log, 0, ++ "auth_token_version[%d] = %d", j, auth_token_version[j]); ++ ptr = strtok (NULL, "."); ++ j++; ++ } ++ j = 0; ++ ptr = strtok ((char *)peer->version.data,"."); ++ while (ptr != NULL && (j < 2)) { ++ peer_version[j] = atoi(ptr); ++ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, log, 0, ++ "peer_version[%d] = %d", j, peer_version[j]); ++ ptr = strtok (NULL, "."); ++ j++; ++ } ++ // Compare if the major and minor revisions are same ++ for (j = 0; j < 2; j++) { ++ if (auth_token_version[j] != peer_version[j]) { ++ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, ++ "skipping peer at a different major/minor version %V", &peer->version); ++ f = 0; ++ } ++ } ++ } ++ } ++ ++ return f; ++} ++ ++#if (NGX_HTTP_SSL) ++static ngx_int_t ++ngx_http_upstream_fair_set_session(ngx_peer_connection_t *pc, void *data) ++{ ++ ngx_http_upstream_fair_peer_data_t *fp = data; ++ ++ ngx_int_t rc; ++ ngx_ssl_session_t *ssl_session; ++ ngx_http_upstream_fair_peer_t *peer; ++ ++ if (fp->current == NGX_PEER_INVALID) ++ return NGX_OK; ++ ++ peer = &fp->peers->peer[fp->current]; ++ ++ /* TODO: threads only mutex */ ++ /* ngx_lock_mutex(fp->peers->mutex); */ ++ ++ ssl_session = peer->ssl_session; ++ ++ rc = ngx_ssl_set_session(pc->connection, ssl_session); ++ ++ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,"set session: %p", ssl_session); ++ /* ngx_unlock_mutex(fp->peers->mutex); */ ++ ++ return rc; ++} ++ ++static void ++ngx_http_upstream_fair_save_session(ngx_peer_connection_t *pc, void *data) ++{ ++ ngx_http_upstream_fair_peer_data_t *fp = data; ++ ++ ngx_ssl_session_t *old_ssl_session, *ssl_session; ++ ngx_http_upstream_fair_peer_t *peer; ++ ++ if (fp->current == NGX_PEER_INVALID) ++ return; ++ ++ ssl_session = ngx_ssl_get_session(pc->connection); ++ ++ if (ssl_session == NULL) { ++ return; ++ } ++ ++ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,"save session: %p", ssl_session); ++ peer = &fp->peers->peer[fp->current]; ++ ++ /* TODO: threads only mutex */ ++ /* ngx_lock_mutex(fp->peers->mutex); */ ++ ++ old_ssl_session = peer->ssl_session; ++ peer->ssl_session = ssl_session; ++ ++ /* ngx_unlock_mutex(fp->peers->mutex); */ ++ ++ if (old_ssl_session) { ++ ++ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,"old session: %p", old_ssl_session); ++ /* TODO: may block */ ++ ++ ngx_ssl_free_session(old_ssl_session); ++ } ++} ++ ++#endif ++ ++#if 0 ++static void ++ngx_http_upstream_fair_walk_status(ngx_pool_t *pool, ngx_chain_t *cl, ngx_int_t *length, ++ ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) ++{ ++ ngx_http_upstream_fair_shm_block_t *s_node = (ngx_http_upstream_fair_shm_block_t *) node; ++ ngx_http_upstream_fair_peers_t *peers; ++ ngx_chain_t *new_cl; ++ ngx_buf_t *b; ++ ngx_uint_t size, i; ++ ++ if (node == sentinel) { ++ return; ++ } ++ ++ if (node->left != sentinel) { ++ ngx_http_upstream_fair_walk_status(pool, cl, length, node->left, sentinel); ++ } ++ ++ if (s_node->generation != ngx_http_upstream_fair_generation) { ++ size = 100; ++ peers = NULL; ++ } else { ++ /* this is rather ugly (casting an uintptr_t back into a pointer ++ * but as long as the generation is still the same (verified above), ++ * it should be still safe ++ */ ++ peers = (ngx_http_upstream_fair_peers_t *) s_node->peers; ++ if (!peers->shared) { ++ goto next; ++ } ++ ++ size = 200 + peers->number * 120; /* LOTS of slack */ ++ } ++ ++ b = ngx_create_temp_buf(pool, size); ++ if (!b) { ++ goto next; ++ } ++ ++ new_cl = ngx_alloc_chain_link(pool); ++ if (!new_cl) { ++ goto next; ++ } ++ ++ new_cl->buf = b; ++ new_cl->next = NULL; ++ ++ while (cl->next) { ++ cl = cl->next; ++ } ++ cl->next = new_cl; ++ ++ if (peers) { ++ b->last = ngx_sprintf(b->last, "upstream %V (%p): current peer %d/%d, total requests: %ui\n", peers->name, (void*) node, peers->current, peers->number, s_node->total_requests); ++ for (i = 0; i < peers->number; i++) { ++ ngx_http_upstream_fair_peer_t *peer = &peers->peer[i]; ++ ngx_http_upstream_fair_shared_t *sh = peer->shared; ++ b->last = ngx_sprintf(b->last, " peer %d: %V weight: %d/%d, fails: %d/%d, acc: %d, down: %d, nreq: %d, total_req: %ui, last_req: %ui\n", ++ i, &peer->name, sh->current_weight, peer->weight, sh->fails, peer->max_fails, peer->accessed, peer->down, ++ sh->nreq, sh->total_req, sh->last_req_id); ++ } ++ } else { ++ b->last = ngx_sprintf(b->last, "upstream %p: gen %ui != %ui, total_nreq = %ui", (void*) node, s_node->generation, ngx_http_upstream_fair_generation, s_node->total_nreq); ++ } ++ b->last = ngx_sprintf(b->last, "\n"); ++ b->last_buf = 1; ++ ++ *length += b->last - b->pos; ++ ++ if (cl->buf) { ++ cl->buf->last_buf = 0; ++ } ++ ++ cl = cl->next; ++next: ++ ++ if (node->right != sentinel) { ++ ngx_http_upstream_fair_walk_status(pool, cl, length, node->right, sentinel); ++ } ++} ++ ++static ngx_chain_t* ++ngx_http_upstream_fair_report_status(ngx_http_request_t *r, ngx_int_t *length) ++{ ++ ngx_buf_t *b; ++ ngx_chain_t *cl; ++ ngx_slab_pool_t *shpool; ++ ++ b = ngx_create_temp_buf(r->pool, sizeof("\nupstream_fair status report:\n")); ++ if (!b) { ++ return NULL; ++ } ++ ++ cl = ngx_alloc_chain_link(r->pool); ++ if (!cl) { ++ return NULL; ++ } ++ cl->next = NULL; ++ cl->buf = b; ++ ++ b->last = ngx_cpymem(b->last, "\nupstream_fair status report:\n", ++ sizeof("\nupstream_fair status report:\n") - 1); ++ ++ *length = b->last - b->pos; ++ ++ shpool = (ngx_slab_pool_t *)ngx_http_upstream_fair_shm_zone->shm.addr; ++ ++ ngx_shmtx_lock(&shpool->mutex); ++ ++ ngx_http_upstream_fair_walk_status(r->pool, cl, ++ length, ++ ngx_http_upstream_fair_rbtree->root, ++ ngx_http_upstream_fair_rbtree->sentinel); ++ ++ ngx_shmtx_unlock(&shpool->mutex); ++ ++ if (!cl->next || !cl->next->buf) { ++ /* no upstream_fair status to report */ ++ return NULL; ++ } ++ ++ return cl; ++} ++#endif ++ ++/* vim: set et ts=4 sw=4: */ +diff -urN nginx/src/http/ngx_http_upstream_fair.h nginx/src/http/ngx_http_upstream_fair.h +--- nginx/src/http/ngx_http_upstream_fair.h 1970-01-01 05:30:00.000000000 +0530 ++++ nginx/src/http/ngx_http_upstream_fair.h 2023-03-04 13:25:59.919145900 +0530 +@@ -0,0 +1,113 @@ ++/* ++ * ***** BEGIN LICENSE BLOCK ***** ++ * Zimbra Collaboration Suite Server ++ * Copyright (C) 2011 Zimbra Software, LLC. ++ * ++ * The contents of this file are subject to the Zimbra Public License ++ * Version 1.4 ("License"); you may not use this file except in ++ * compliance with the License. You may obtain a copy of the License at ++ * http://www.zimbra.com/license. ++ * ++ * Software distributed under the License is distributed on an "AS IS" ++ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. ++ * ***** END LICENSE BLOCK ***** ++ */ ++ ++#ifndef _NGX_HTTP_UPSTREAM_FAIR_H_INCLUDED_ ++#define _NGX_HTTP_UPSTREAM_FAIR_H_INCLUDED_ ++ ++ ++#include ++#include ++#include ++ ++typedef struct { ++ ngx_uint_t nreq; ++ ngx_uint_t total_req; ++ ngx_uint_t last_req_id; ++ ngx_uint_t fails; ++ ngx_uint_t current_weight; ++} ngx_http_upstream_fair_shared_t; ++ ++typedef struct ngx_http_upstream_fair_peers_s ngx_http_upstream_fair_peers_t; ++ ++typedef struct { ++ ngx_rbtree_node_t node; ++ ngx_uint_t generation; ++ uintptr_t peers; /* forms a unique cookie together with generation */ ++ ngx_uint_t total_nreq; ++ ngx_uint_t total_requests; ++ ngx_atomic_t lock; ++ ngx_http_upstream_fair_shared_t stats[1]; ++} ngx_http_upstream_fair_shm_block_t; ++ ++/* ngx_spinlock is defined without a matching unlock primitive */ ++#define ngx_spinlock_unlock(lock) (void) ngx_atomic_cmp_set(lock, ngx_pid, 0) ++ ++typedef struct { ++ ngx_http_upstream_fair_shared_t *shared; ++ struct sockaddr *sockaddr; ++ socklen_t socklen; ++ ngx_str_t name; ++ ++ ngx_uint_t weight; ++ ngx_uint_t max_fails; ++ time_t fail_timeout; ++ ++ time_t accessed; ++ ngx_uint_t down:1; ++ ngx_str_t version; /* upstream server version */ ++ ++#if (NGX_HTTP_SSL) ++ ngx_ssl_session_t *ssl_session; /* local to a process */ ++#endif ++ ++} ngx_http_upstream_fair_peer_t; ++ ++#define NGX_HTTP_UPSTREAM_FAIR_NO_RR (1<<26) ++#define NGX_HTTP_UPSTREAM_FAIR_WEIGHT_MODE_IDLE (1<<27) ++#define NGX_HTTP_UPSTREAM_FAIR_WEIGHT_MODE_PEAK (1<<28) ++#define NGX_HTTP_UPSTREAM_FAIR_WEIGHT_MODE_MASK ((1<<27) | (1<<28)) ++ ++enum { WM_DEFAULT = 0, WM_IDLE, WM_PEAK }; ++ ++struct ngx_http_upstream_fair_peers_s { ++ ngx_http_upstream_fair_shm_block_t *shared; ++ ngx_uint_t current; ++ ngx_uint_t size_err:1; ++ ngx_uint_t no_rr:1; ++ ngx_uint_t weight_mode:2; ++ ngx_uint_t number; ++ ngx_str_t *name; ++ ngx_http_upstream_fair_peers_t *next; /* for backup peers support, not really used yet */ ++ ngx_http_upstream_fair_peer_t peer[1]; ++}; ++ ++ ++#define NGX_PEER_INVALID (~0UL) ++ ++typedef struct { ++ ngx_http_upstream_fair_peers_t *peers; ++ ngx_uint_t current; ++ uintptr_t *tried; ++ uintptr_t *done; ++ uintptr_t data; ++ uintptr_t data2; ++ ngx_str_t version; ++ ngx_flag_t exact_version_check; ++} ngx_http_upstream_fair_peer_data_t; ++ ++extern ngx_uint_t *shm_size; ++ ++ngx_int_t ngx_http_upstream_init_fair(ngx_conf_t *cf, ++ ngx_http_upstream_srv_conf_t *us); ++ngx_int_t ngx_http_upstream_init_fair_peer(ngx_http_request_t *r, ++ ngx_http_upstream_srv_conf_t *us); ++ngx_int_t ngx_http_upstream_get_fair_peer(ngx_peer_connection_t *pc, ++ void *data); ++void ngx_http_upstream_free_fair_peer(ngx_peer_connection_t *pc, ++ void *data, ngx_uint_t state); ++ngx_flag_t ngx_http_upstream_fair_peer_version_allowed(ngx_http_upstream_fair_peer_t *peer, ++ void* data, ngx_log_t* log); ++ ++#endif /* _NGX_HTTP_UPSTREAM_FAIR_H_INCLUDED_ */ diff --git a/thirdparty/nginx/patches/nginx_src_mail_ngx_mail.patch b/thirdparty/nginx/patches/nginx_src_mail_ngx_mail.patch new file mode 100644 index 000000000..ccc2801ef --- /dev/null +++ b/thirdparty/nginx/patches/nginx_src_mail_ngx_mail.patch @@ -0,0 +1,260 @@ +--- nginx/src/mail/ngx_mail.h 2023-03-07 16:26:44.702481300 +0530 ++++ nginx/src/mail/ngx_mail.h 2023-10-02 14:46:14.229251500 +0530 +@@ -18,7 +18,7 @@ + #include + #endif + +- ++#include + + typedef struct { + void **main_conf; +@@ -117,6 +117,17 @@ + + ngx_uint_t max_errors; + ++ ngx_str_t master_auth_username; ++ ngx_str_t master_auth_password; ++ ++ ngx_str_t sasl_app_name; ++ ngx_str_t sasl_service_name; ++ ngx_flag_t sasl_host_from_ip; ++ ++ ngx_msec_t auth_wait_intvl; ++ ++ ngx_str_t default_realm; ++ + ngx_str_t server_name; + + u_char *file_name; +@@ -134,13 +145,16 @@ + + typedef enum { + ngx_pop3_start = 0, ++ ngx_pop3_xoip, + ngx_pop3_user, + ngx_pop3_passwd, + ngx_pop3_auth_login_username, + ngx_pop3_auth_login_password, + ngx_pop3_auth_plain, ++ ngx_pop3_auth_plain_response, + ngx_pop3_auth_cram_md5, +- ngx_pop3_auth_external ++ ngx_pop3_auth_external, ++ ngx_pop3_auth_gssapi + } ngx_pop3_state_e; + + +@@ -149,7 +163,10 @@ + ngx_imap_auth_login_username, + ngx_imap_auth_login_password, + ngx_imap_auth_plain, ++ ngx_imap_auth_plain_ir, ++ ngx_imap_auth_gssapi, + ngx_imap_auth_cram_md5, ++ ngx_imap_id, + ngx_imap_auth_external, + ngx_imap_login, + ngx_imap_user, +@@ -162,9 +179,11 @@ + ngx_smtp_auth_login_username, + ngx_smtp_auth_login_password, + ngx_smtp_auth_plain, ++ ngx_smtp_auth_gssapi, + ngx_smtp_auth_cram_md5, + ngx_smtp_auth_external, + ngx_smtp_helo, ++ ngx_smtp_noxclient, + ngx_smtp_helo_xclient, + ngx_smtp_helo_auth, + ngx_smtp_helo_from, +@@ -176,6 +195,12 @@ + ngx_smtp_to + } ngx_smtp_state_e; + ++/* sasl auth mechanisms */ ++typedef enum { ++ ngx_auth_unknown = 0, ++ ngx_auth_plain, ++ ngx_auth_gssapi, ++} ngx_auth_e; + + typedef struct { + ngx_peer_connection_t upstream; +@@ -183,6 +208,15 @@ + ngx_uint_t proxy_protocol; /* unsigned proxy_protocol:1; */ + } ngx_mail_proxy_ctx_t; + ++typedef void (*ngx_mail_cleanup_pt)(void *data); ++ ++typedef struct ngx_mail_cleanup_s ngx_mail_cleanup_t; ++ ++struct ngx_mail_cleanup_s { ++ ngx_mail_cleanup_pt handler; ++ void *data; ++ ngx_mail_cleanup_t *next; ++}; + + typedef struct { + uint32_t signature; /* "MAIL" */ +@@ -202,6 +236,8 @@ + + ngx_uint_t mail_state; + ++ ngx_str_t greetings[3]; ++ + unsigned ssl:1; + unsigned protocol:3; + unsigned blocked:1; +@@ -211,10 +247,26 @@ + unsigned no_sync_literal:1; + unsigned starttls:1; + unsigned esmtp:1; +- unsigned auth_method:3; ++ unsigned auth_method:4; + unsigned auth_wait:1; + +- ngx_str_t login; ++ unsigned sendquitmsg:1; ++ ++ unsigned vlogin:2; /* vlogin = 0 fqdn is not looked up; ++ vlogin = 1 fqdn has been looked up but not found; ++ vlogin = 2 fqdn has been looked up and assigned to "login" ++ */ ++ ++ ngx_str_t login; /* keep the original user input login */ ++ ++ ngx_str_t qlogin; /* initially equal to 'login', then hold account name ++ after successful alias cache fetch or route lookup ++ this value is finally used to login the upstream ++ mail server*/ ++ ngx_str_t zlogin; /* the hack suffix "/wm" or "/ni" or "/tb" */ ++ ngx_str_t id_name; /* the value of "name" field in IMAP ID */ ++ ngx_str_t id_version; /* the value of "version" field in IMAP ID */ ++ + ngx_str_t passwd; + + ngx_str_t salt; +@@ -243,11 +295,31 @@ + u_char *cmd_start; + u_char *arg_start; + ngx_uint_t literal_len; ++ ngx_uint_t eargs; /* expected #args for command */ ++ ++ /* SASL */ ++ ngx_flag_t usedauth; ++ ngx_flag_t qualifydauth; ++ ngx_str_t dusr; ++ ngx_str_t zusr; ++ ngx_str_t dpasswd; ++ ngx_auth_e authmech; ++ ngx_flag_t saslfr; ++ sasl_conn_t *saslconn; ++ ngx_str_t authid; /* SASL authenticating user */ ++ ++ /* memcache keys */ ++ ngx_str_t key_alias; ++ ngx_str_t key_route; ++ ++ /* clean up */ ++ ngx_mail_cleanup_t *cleanup; + } ngx_mail_session_t; + + + typedef struct { + ngx_str_t *client; ++ ngx_uint_t client_port; + ngx_mail_session_t *session; + } ngx_mail_log_ctx_t; + +@@ -275,9 +347,10 @@ + #define NGX_IMAP_NOOP 4 + #define NGX_IMAP_STARTTLS 5 + +-#define NGX_IMAP_NEXT 6 ++#define NGX_IMAP_ID 6 + + #define NGX_IMAP_AUTHENTICATE 7 ++#define NGX_IMAP_NEXT 8 + + + #define NGX_SMTP_HELO 1 +@@ -302,6 +375,11 @@ + #define NGX_MAIL_AUTH_CRAM_MD5 4 + #define NGX_MAIL_AUTH_EXTERNAL 5 + #define NGX_MAIL_AUTH_NONE 6 ++/* zimbra auth definitions */ ++#define NGX_MAIL_AUTH_PASSWD 7 ++#define NGX_MAIL_AUTH_PLAIN_IR 8 ++#define NGX_MAIL_AUTH_GSSAPI 9 ++#define NGX_MAIL_AUTH_GSSAPI_IR 10 + + + #define NGX_MAIL_AUTH_PLAIN_ENABLED 0x0002 +@@ -309,10 +387,19 @@ + #define NGX_MAIL_AUTH_APOP_ENABLED 0x0008 + #define NGX_MAIL_AUTH_CRAM_MD5_ENABLED 0x0010 + #define NGX_MAIL_AUTH_EXTERNAL_ENABLED 0x0020 +-#define NGX_MAIL_AUTH_NONE_ENABLED 0x0040 ++#define NGX_MAIL_AUTH_GSSAPI_ENABLED 0x0040 ++#define NGX_MAIL_AUTH_NONE_ENABLED 0x0080 + + + #define NGX_MAIL_PARSE_INVALID_COMMAND 20 ++#define NGX_MAIL_PARSE_INVALID_AUTH_MECH 30 ++#define NGX_MAIL_AUTH_ABORT 40 ++#define NGX_MAIL_AUTH_ARGUMENT 50 ++#define NGX_MAIL_AUTH_FAILED 60 ++#define NGX_MAIL_LOGIN_FAILED 70 ++ ++#define NGX_MAIL_MAX_LOGIN_LEN 256 ++#define NGX_MAIL_MAX_PASSWORD_LEN 1024 + + + typedef void (*ngx_mail_init_session_pt)(ngx_mail_session_t *s, +@@ -336,6 +423,7 @@ + ngx_str_t internal_server_error; + ngx_str_t cert_error; + ngx_str_t no_cert; ++ ngx_str_t quit_msg; + }; + + +@@ -397,13 +485,27 @@ + ngx_int_t ngx_mail_auth_cram_md5(ngx_mail_session_t *s, ngx_connection_t *c); + ngx_int_t ngx_mail_auth_external(ngx_mail_session_t *s, ngx_connection_t *c, + ngx_uint_t n); ++ngx_int_t ngx_mail_auth_gssapi(ngx_mail_session_t *s, ngx_connection_t *c, ngx_str_t * output); + ngx_int_t ngx_mail_auth_parse(ngx_mail_session_t *s, ngx_connection_t *c); + + void ngx_mail_send(ngx_event_t *wev); + ngx_int_t ngx_mail_read_command(ngx_mail_session_t *s, ngx_connection_t *c); ++void ngx_mail_set_imap_parse_state_start(ngx_mail_session_t * s); ++void ngx_mail_set_pop3_parse_state_start(ngx_mail_session_t * s); ++void ngx_mail_set_smtp_parse_state_start(ngx_mail_session_t * s); ++void ngx_mail_set_imap_parse_state_argument(ngx_mail_session_t * s); ++void ngx_mail_set_pop3_parse_state_argument(ngx_mail_session_t * s); ++void ngx_mail_set_smtp_parse_state_argument(ngx_mail_session_t * s); ++void ngx_mail_reset_parse_buffer(ngx_mail_session_t * s); + void ngx_mail_auth(ngx_mail_session_t *s, ngx_connection_t *c); ++void ngx_mail_do_auth(ngx_mail_session_t *s, ngx_connection_t *c); /* Zimbra mail auth portal */ + void ngx_mail_close_connection(ngx_connection_t *c); + void ngx_mail_session_internal_server_error(ngx_mail_session_t *s); ++void ngx_mail_end_session(ngx_mail_session_t *s); ++ngx_str_t ngx_mail_session_getquitmsg(ngx_mail_session_t *s); ++ngx_str_t ngx_mail_session_geterrmsg(ngx_mail_session_t *s); ++ngx_str_t ngx_mail_get_socket_local_addr_str (ngx_pool_t *pool, ngx_socket_t s); ++ngx_int_t ngx_mail_decode_auth_plain(ngx_mail_session_t *s, ngx_str_t *encoded); + u_char *ngx_mail_log_error(ngx_log_t *log, u_char *buf, size_t len); + + +@@ -416,6 +518,9 @@ + ngx_int_t ngx_mail_realip_handler(ngx_mail_session_t *s); + /**/ + ++ngx_mail_cleanup_t * ngx_mail_cleanup_add(ngx_mail_session_t * s, size_t size); ++ ++ngx_flag_t ngx_mail_get_proxy_ssl(ngx_mail_session_t *s); + + extern ngx_uint_t ngx_mail_max_module; + extern ngx_module_t ngx_mail_core_module; diff --git a/thirdparty/nginx/patches/nginx_src_mail_ngx_mail_auth_http_module.patch b/thirdparty/nginx/patches/nginx_src_mail_ngx_mail_auth_http_module.patch new file mode 100644 index 000000000..7919aea3f --- /dev/null +++ b/thirdparty/nginx/patches/nginx_src_mail_ngx_mail_auth_http_module.patch @@ -0,0 +1,18 @@ +--- nginx/src/mail/ngx_mail_auth_http_module.c 2023-03-07 16:26:44.704430400 +0530 ++++ nginx/src/mail/ngx_mail_auth_http_module.c 2023-09-14 18:47:12.605101100 +0530 +@@ -1536,6 +1536,7 @@ + conf->host_header = prev->host_header; + conf->uri = prev->uri; + ++ /* zimbra uses zmauth, so no auth_http + if (conf->peer == NULL) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no \"auth_http\" is defined for server in %s:%ui", +@@ -1543,6 +1544,7 @@ + + return NGX_CONF_ERROR; + } ++ */ + } + + ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 60000); diff --git a/thirdparty/nginx/patches/nginx_src_mail_ngx_mail_core_module.patch b/thirdparty/nginx/patches/nginx_src_mail_ngx_mail_core_module.patch new file mode 100644 index 000000000..3370c3de5 --- /dev/null +++ b/thirdparty/nginx/patches/nginx_src_mail_ngx_mail_core_module.patch @@ -0,0 +1,160 @@ +--- nginx/src/mail/ngx_mail_core_module.c 2023-07-23 13:21:27.107626300 +0530 ++++ nginx/src/mail/ngx_mail_core_module.c 2023-09-14 18:47:12.612942300 +0530 +@@ -92,6 +92,41 @@ + offsetof(ngx_mail_core_srv_conf_t, max_errors), + NULL }, + ++ { ngx_string("sasl_app_name"), ++ NGX_MAIL_MAIN_CONF|NGX_CONF_TAKE1, ++ ngx_conf_set_str_slot, ++ NGX_MAIL_SRV_CONF_OFFSET, ++ offsetof(ngx_mail_core_srv_conf_t, sasl_app_name), ++ NULL }, ++ ++ { ngx_string("sasl_service_name"), ++ NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, ++ ngx_conf_set_str_slot, ++ NGX_MAIL_SRV_CONF_OFFSET, ++ offsetof(ngx_mail_core_srv_conf_t, sasl_service_name), ++ NULL }, ++ ++ { ngx_string("zm_auth_wait"), ++ NGX_MAIL_MAIN_CONF|NGX_CONF_TAKE1, ++ ngx_conf_set_msec_slot, ++ NGX_MAIL_SRV_CONF_OFFSET, ++ offsetof(ngx_mail_core_srv_conf_t, auth_wait_intvl), ++ NULL }, ++ ++ { ngx_string("default_realm"), ++ NGX_MAIL_MAIN_CONF|NGX_CONF_TAKE1, ++ ngx_conf_set_str_slot, ++ NGX_MAIL_SRV_CONF_OFFSET, ++ offsetof(ngx_mail_core_srv_conf_t, default_realm), ++ NULL }, ++ ++ { ngx_string("sasl_host_from_ip"), ++ NGX_MAIL_MAIN_CONF|NGX_CONF_TAKE1, ++ ngx_conf_set_flag_slot, ++ NGX_MAIL_SRV_CONF_OFFSET, ++ offsetof(ngx_mail_core_srv_conf_t, sasl_host_from_ip), ++ NULL }, ++ + ngx_null_command + }; + +@@ -167,12 +202,15 @@ + * cscf->error_log = NULL; + */ + ++ cscf->protocol = NGX_CONF_UNSET_PTR; + cscf->timeout = NGX_CONF_UNSET_MSEC; + cscf->resolver_timeout = NGX_CONF_UNSET_MSEC; + + cscf->max_errors = NGX_CONF_UNSET_UINT; + + cscf->resolver = NGX_CONF_UNSET_PTR; ++ cscf->auth_wait_intvl = NGX_CONF_UNSET_MSEC; ++ cscf->sasl_host_from_ip = NGX_CONF_UNSET; + + cscf->file_name = cf->conf_file->file.name.data; + cscf->line = cf->conf_file->line; +@@ -192,6 +230,27 @@ + 30000); + + ngx_conf_merge_uint_value(conf->max_errors, prev->max_errors, 5); ++ ngx_conf_merge_str_value( ++ conf->sasl_app_name, prev->sasl_app_name, "nginx"); ++ ++ ngx_conf_merge_str_value( ++ conf->sasl_service_name, prev->sasl_service_name, ""); ++/* TO BE HANDLED */ ++ if (conf->sasl_service_name.len == 0) { ++ if (conf->protocol->type == NGX_MAIL_IMAP_PROTOCOL) { ++ conf->sasl_service_name.data = (u_char *)"imap"; ++ conf->sasl_service_name.len = sizeof("imap") - 1; ++ } else if (conf->protocol->type == NGX_MAIL_POP3_PROTOCOL) { ++ conf->sasl_service_name.data = (u_char *)"pop"; ++ conf->sasl_service_name.len = sizeof("pop") - 1; ++ } else if (conf->protocol->type == NGX_MAIL_SMTP_PROTOCOL) { ++ conf->sasl_service_name.data = (u_char *)"smtp"; ++ conf->sasl_service_name.len = sizeof("smtp") - 1; ++ } else { ++ conf->sasl_service_name.data = (u_char *)"unknown"; ++ conf->sasl_service_name.len = sizeof("unknown") - 1; ++ } ++ } + + ngx_conf_merge_str_value(conf->server_name, prev->server_name, ""); + +@@ -214,6 +273,17 @@ + } + } + ++ /* ++ * master_auth_username and master_auth_password is already set in ngx_zm_lookup module ++ * ++ * ngx_conf_merge_str_value(conf->master_auth_username, prev->master_auth_username, ""); ++ * ngx_conf_merge_str_value(conf->master_auth_password, prev->master_auth_password, ""); ++ */ ++ ngx_conf_merge_msec_value (conf->auth_wait_intvl, prev->auth_wait_intvl, 10000); ++ ++ ngx_conf_merge_str_value (conf->default_realm, prev->default_realm,""); ++ ngx_conf_merge_value (conf->sasl_host_from_ip, prev->sasl_host_from_ip, 0); ++ + ngx_conf_merge_ptr_value(conf->resolver, prev->resolver, NULL); + + return NGX_CONF_OK; +@@ -597,7 +667,7 @@ + nls->wildcard = ngx_inet_wildcard(nls->sockaddr); + + als = cmcf->listen.elts; +- ++ /* Zimbra ports can be same + for (i = 0; i < cmcf->listen.nelts - 1; i++) { + + if (ngx_cmp_sockaddr(als[i].sockaddr, als[i].socklen, +@@ -612,7 +682,7 @@ + &nls->addr_text); + return NGX_CONF_ERROR; + } +- ++ */ + next: + continue; + } +@@ -714,3 +784,35 @@ + + return NGX_CONF_OK; + } ++ ++ ++ngx_mail_cleanup_t * ++ngx_mail_cleanup_add(ngx_mail_session_t *s, size_t size) ++{ ++ ngx_mail_cleanup_t *cln; ++ ++ cln = ngx_palloc(s->connection->pool, sizeof(ngx_mail_cleanup_t)); ++ if (cln == NULL) { ++ return NULL; ++ } ++ ++ if (size) { ++ cln->data = ngx_palloc(s->connection->pool, size); ++ if (cln->data == NULL) { ++ return NULL; ++ } ++ ++ } else { ++ cln->data = NULL; ++ } ++ ++ cln->handler = NULL; ++ cln->next = s->cleanup; ++ ++ s->cleanup = cln; ++ ++ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, s->connection->log, 0, ++ "mail cleanup add: %p", cln); ++ ++ return cln; ++} diff --git a/thirdparty/nginx/patches/nginx_src_mail_ngx_mail_handler.patch b/thirdparty/nginx/patches/nginx_src_mail_ngx_mail_handler.patch new file mode 100644 index 000000000..205b01ae8 --- /dev/null +++ b/thirdparty/nginx/patches/nginx_src_mail_ngx_mail_handler.patch @@ -0,0 +1,1490 @@ +--- nginx/src/mail/ngx_mail_handler.c 2023-08-18 17:02:49.358696000 +0530 ++++ nginx/src/mail/ngx_mail_handler.c 2023-10-02 15:28:39.963273000 +0530 +@@ -10,10 +10,20 @@ + #include + #include + ++#include ++#include ++#include ++#include ++#include ++#include + + static void ngx_mail_proxy_protocol_handler(ngx_event_t *rev); + static void ngx_mail_init_session_handler(ngx_event_t *rev); + static void ngx_mail_init_session(ngx_connection_t *c); ++static void ngx_mail_choke_session(throttle_callback_t *cb); ++static void ngx_mail_allow_session(throttle_callback_t *cb); ++static void ngx_mail_allow_userauth(throttle_callback_t *cb); ++static void ngx_mail_choke_userauth(throttle_callback_t *cb); + + #if (NGX_MAIL_SSL) + static void ngx_mail_ssl_init_connection(ngx_ssl_t *ssl, ngx_connection_t *c); +@@ -22,12 +32,25 @@ + ngx_connection_t *c); + #endif + ++static int ngx_mail_create_sasl_context(ngx_connection_t *s); ++static void ngx_mail_dispose_sasl_context(ngx_mail_session_t *s); ++static int ngx_mail_initialize_sasl(ngx_connection_t *c); ++static int ngx_mail_sasl_startstep(ngx_connection_t *c, const char *mech, ++ ngx_str_t *response, ngx_str_t *challenge); ++static int ngx_mail_sasl_log(void *context, int level, const char * message); ++static int ngx_mail_sasl_pauthorize(sasl_conn_t *conn, void *context, ++ const char *authz, unsigned authzlen, const char *authc, unsigned authclen, ++ const char *realm, unsigned rlen, struct propctx *propctx); ++ ++static ngx_str_t krb5_cooked_password = ngx_string("KKK"); ++ ++static ngx_flag_t sasl_initialized = 0; + + void + ngx_mail_init_connection(ngx_connection_t *c) + { +- size_t len; + ngx_uint_t i; ++ ngx_uint_t remote_port=0; + ngx_event_t *rev; + ngx_mail_port_t *port; + struct sockaddr *sa; +@@ -37,7 +60,7 @@ + ngx_mail_session_t *s; + ngx_mail_addr_conf_t *addr_conf; + ngx_mail_core_srv_conf_t *cscf; +- u_char text[NGX_SOCKADDR_STRLEN]; ++ //u_char text[NGX_SOCKADDR_STRLEN]; + #if (NGX_HAVE_INET6) + struct sockaddr_in6 *sin6; + ngx_mail_in6_addr_t *addr6; +@@ -145,10 +168,28 @@ + + ngx_set_connection_log(c, cscf->error_log); + +- len = ngx_sock_ntop(c->sockaddr, c->socklen, text, NGX_SOCKADDR_STRLEN, 1); ++ //len = ngx_sock_ntop(c->sockaddr, c->socklen, text, NGX_SOCKADDR_STRLEN, 1); + +- ngx_log_error(NGX_LOG_INFO, c->log, 0, "*%uA client %*s connected to %V", +- c->number, len, text, s->addr_text); ++ switch (c->sockaddr->sa_family) { ++#if (NGX_HAVE_INET6) ++ case AF_INET6: ++ sin6 = (struct sockaddr_in6 *) c->sockaddr; ++ remote_port = ntohs(sin6->sin6_port); ++ break; ++#endif ++ default: ++ sin = (struct sockaddr_in *) c->sockaddr; ++ remote_port = ntohs(sin->sin_port); ++ break; ++ } ++ ++ if (remote_port && remote_port < 65536) { ++ ngx_log_error(NGX_LOG_INFO, c->log, 0, "*%ui client %V:%ui connected to %V", ++ c->number, &c->addr_text, remote_port, s->addr_text); ++ } else { ++ ngx_log_error(NGX_LOG_INFO, c->log, 0, "*%ui client %V connected to %V", ++ c->number, &c->addr_text, s->addr_text); ++ } + + ctx = ngx_palloc(c->pool, sizeof(ngx_mail_log_ctx_t)); + if (ctx == NULL) { +@@ -157,6 +198,7 @@ + } + + ctx->client = &c->addr_text; ++ ctx->client_port = remote_port; + ctx->session = s; + + c->log->connection = c->number; +@@ -379,7 +421,7 @@ + ngx_mail_init_session(c); + return; + } +- ++ + ngx_mail_close_connection(c); + } + +@@ -456,26 +498,260 @@ + { + ngx_mail_session_t *s; + ngx_mail_core_srv_conf_t *cscf; ++ ngx_mail_throttle_srv_conf_t *tscf; ++ throttle_callback_t *cb; ++ ngx_uint_t login_ip_max; + + s = c->data; + + c->log->action = "sending client greeting line"; + + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); ++ tscf = ngx_mail_get_module_srv_conf(s, ngx_mail_throttle_module); + + s->protocol = cscf->protocol->type; + ++ if(s->ctx != NULL) { ++ return; ++ } ++ + s->ctx = ngx_pcalloc(c->pool, sizeof(void *) * ngx_mail_max_module); + if (s->ctx == NULL) { + ngx_mail_session_internal_server_error(s); + return; + } + ++ s->cleanup = NULL; ++ ++ /* throttle */ ++ cb = ngx_pcalloc(c->pool, sizeof(throttle_callback_t)); ++ if(cb == NULL) { ++ ngx_mail_session_internal_server_error(s); ++ return; ++ } ++ ++ ngx_memset(cb, 0, sizeof(throttle_callback_t)); ++ cb->session = s; ++ cb->connection = c; ++ cb->log = ngx_cycle->log; ++ cb->pool = c->pool; ++ cb->on_allow = ngx_mail_allow_session; ++ cb->on_deny = ngx_mail_choke_session; ++ ++ login_ip_max = ngx_mail_throttle_ip_max_for_protocol(tscf, s->protocol); ++ if (login_ip_max == 0) { ++ cb->on_allow(cb); //unlimited, direct allow session ++ } else { ++ ngx_mail_throttle_whitelist_ip(c->addr_text, cb); ++ } ++} ++ ++static void ++ngx_mail_choke_session(throttle_callback_t *cb) ++{ ++ ngx_connection_t *c; ++ ngx_mail_session_t *s; ++ ngx_mail_throttle_srv_conf_t *tscf; ++ ngx_str_t bye, msg; ++ u_char *p; ++ ++ c = (ngx_connection_t *)cb->connection; ++ s = c->data; ++ tscf = ngx_mail_get_module_srv_conf(s, ngx_mail_throttle_module); ++ msg = tscf->mail_login_ip_rejectmsg; ++ ++ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log,0, ++ "ip throttle:%V choking mail session", &c->addr_text); ++ ++ if(s->protocol == NGX_MAIL_IMAP_PROTOCOL) { ++ bye.data = ++ ngx_palloc(c->pool, ++ sizeof("* BYE ")- 1 + ++ msg.len + ++ sizeof(CRLF) - 1 ++ ); ++ if (bye.data == NULL) { ++ ngx_str_set(&bye, "* BYE" CRLF); ++ } else { ++ p = bye.data; ++ p = ngx_cpymem(p, "* BYE ", sizeof("* BYE ") - 1); ++ p = ngx_cpymem(p, msg.data, msg.len); ++ *p++ = CR; ++ *p++ = LF; ++ bye.len = p - bye.data; ++ } ++ } else if(s->protocol == NGX_MAIL_POP3_PROTOCOL) { ++ bye.data = ngx_palloc(c->pool, ++ sizeof("-ERR ") - 1 + ++ msg.len + ++ sizeof(CRLF) - 1); ++ if (bye.data == NULL) { ++ ngx_str_set(&bye, "-ERR" CRLF); ++ } else { ++ p = bye.data; ++ p = ngx_cpymem(p,"-ERR ",sizeof("-ERR ") - 1); ++ p = ngx_cpymem(p, msg.data, msg.len); ++ *p++ = CR; ++ *p++ = LF; ++ bye.len = p - bye.data; ++ } ++ } else { ++ /* TODO SMTP is not (yet) relevant for zimbra, but how do we reject it ? */ ++ ngx_str_set(&bye, ""); ++ } ++ ++ s->out = bye; ++ s->quit = 1; ++ ++ ngx_mail_send(c->write); ++ ++ return; ++} ++ ++static void ++ngx_mail_allow_session(throttle_callback_t *cb) ++{ ++ ngx_connection_t *c; ++ ngx_mail_session_t *s; ++ ngx_mail_core_srv_conf_t *cscf; ++ ++ c = (ngx_connection_t*)cb->connection; ++ s = c->data; ++ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); ++ + c->write->handler = ngx_mail_send; + + cscf->protocol->init_session(s, c); + } + ++static void ++ngx_mail_choke_userauth(throttle_callback_t *cb) ++{ ++ ngx_connection_t *c; ++ ngx_mail_session_t *s; ++ ngx_mail_throttle_srv_conf_t *tscf; ++ ngx_str_t bye, msg, umsg; ++ size_t l; ++ u_char *p; ++ ++ c = (ngx_connection_t *)cb->connection; ++ s = c->data; ++ tscf = ngx_mail_get_module_srv_conf(s, ngx_mail_throttle_module); ++ msg = tscf->mail_login_user_rejectmsg; ++ ++ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, ++ "user throttle:%V choking mail session", &s->login); ++ ++ if(s->protocol == NGX_MAIL_IMAP_PROTOCOL) ++ { ++ bye.data = ngx_palloc(c->pool, sizeof("* BYE ") - 1 + ++ msg.len + sizeof(CRLF) - 1); ++ if (bye.data == NULL) { ++ ngx_str_set(&bye, "* BYE" CRLF); ++ } else { ++ p = bye.data; ++ p = ngx_cpymem(p, "* BYE ", sizeof("* BYE ") - 1); ++ p = ngx_cpymem(p, msg.data, msg.len); ++ *p++ = CR; ++ *p++ = LF; ++ bye.len = p - bye.data; ++ } ++ s->out = bye; ++ s->quit = 0; /* don't quit just yet */ ++ ngx_mail_send(c->write); ++ ++ /* for IMAP, we also want to send back a tagged NO response */ ++ l = s->tag.len + 1 /*for space*/ + ++ sizeof("NO ") - 1 + ++ sizeof(" failed") - 1 + /* ?? "failed" or "rejected" ?? */ ++ sizeof(CRLF) - 1; ++ ++ if (s->command == NGX_IMAP_LOGIN) { ++ l += (sizeof("LOGIN ") - 1); ++ } else if (s->command == NGX_IMAP_AUTHENTICATE) { ++ l += (sizeof("AUTHENTICATE ") - 1); ++ } ++ ++ umsg.data = ngx_palloc(c->pool,l); ++ ++ if (umsg.data == NULL) { ++ ngx_str_set(&umsg, ""); ++ } else { ++ p = umsg.data; ++ p = ngx_cpymem(p, s->tag.data, s->tag.len); ++ *p++=' '; ++ p = ngx_cpymem(p, "NO ", sizeof("NO ") - 1); ++ if (s->command == NGX_IMAP_LOGIN) { ++ p = ngx_cpymem(p, "LOGIN ", sizeof("LOGIN ") - 1); ++ } else if (s->command == NGX_IMAP_AUTHENTICATE) { ++ p = ngx_cpymem(p, "AUTHENTICATE ", sizeof("AUTHENTICATE ") - 1); ++ } ++ p = ngx_cpymem(p, "failed", sizeof("failed") - 1); ++ *p++ = CR; ++ *p++ = LF; ++ umsg.len = p - umsg.data; ++ } ++ ++ s->out = umsg; ++ s->quit = 1; ++ ngx_mail_send(c->write); ++ ++ return; ++ } ++ else if(s->protocol == NGX_MAIL_POP3_PROTOCOL) ++ { ++ bye.data = ++ ngx_palloc(c->pool, ++ sizeof("-ERR ")-1+msg.len+sizeof(CRLF)-1); ++ if (bye.data == NULL) { ++ bye.data = (u_char*)("-ERR" CRLF); ++ bye.len = sizeof("-ERR" CRLF)-1; ++ } else { ++ p = bye.data; ++ p = ngx_cpymem(p,"-ERR ",sizeof("-ERR ")-1); ++ p = ngx_cpymem(p,msg.data,msg.len); ++ *p++ = CR; ++ *p++ = LF; ++ bye.len = p-bye.data; ++ } ++ s->out = bye; ++ s->quit = 1; ++ ngx_mail_send(c->write); ++ return; ++ } ++ else ++ { ++ /* TODO SMTP is not (yet) relevant for zimbra, but how do we reject it ? */ ++ ngx_str_set(&bye, ""); ++ s->out = bye; ++ s->quit = 1; ++ ngx_mail_send(c->write); ++ return; ++ } ++} ++ ++static void ++ngx_mail_allow_userauth(throttle_callback_t *cb) ++{ ++ ngx_connection_t *c; ++ ngx_mail_session_t *s; ++ ++ c = (ngx_connection_t *)cb->connection; ++ s = c->data; ++ ++ /* remainder code is the erstwhile ngx_mail_do_auth(s);*/ ++ s->args.nelts = 0; ++ s->buffer->pos = s->buffer->start; ++ s->buffer->last = s->buffer->start; ++ s->state = 0; ++ ++ if (s->connection->read->timer_set) { ++ ngx_del_timer(s->connection->read); ++ } ++ ++ s->login_attempt++; ++ ngx_mail_zmauth_init(s); ++} + + ngx_int_t + ngx_mail_salt(ngx_mail_session_t *s, ngx_connection_t *c, +@@ -520,14 +796,42 @@ + #endif + + ++/* Decode an SASL PLAIN challenge (RFC 4616) ++ If AUTHZ is empty: ++ set s->usedauth = 0, ++ set s->login = AUTHC ++ If AUTHZ is present: ++ set s->usedauth = 1 ++ set s->dusr = AUTHC ++ set s->login = AUTHZ ++ */ + ngx_int_t + ngx_mail_auth_plain(ngx_mail_session_t *s, ngx_connection_t *c, ngx_uint_t n) + { + u_char *p, *last; +- ngx_str_t *arg, plain; ++ ngx_str_t *arg, plain, temp; + + arg = s->args.elts; + ++#if (NGX_MAIL_SSL) ++ if(ngx_mail_starttls_only(s, c)) { ++ return NGX_MAIL_PARSE_INVALID_COMMAND; ++ } ++#endif ++ ++ /* check if the auth exchange is being aborted */ ++ if (s->args.nelts > 0 && ++ arg[n].len == 1 && ++ arg[n].data[0] == '*' ++ ) ++ { ++ ngx_log_debug0 (NGX_LOG_DEBUG_MAIL, c->log, 0, ++ "auth:abort SASL PLAIN"); ++ ++ ngx_mail_dispose_sasl_context(s); ++ return NGX_MAIL_AUTH_ABORT; ++ } ++ + #if (NGX_DEBUG_MAIL_PASSWD) + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, + "mail auth plain: \"%V\"", &arg[n]); +@@ -544,6 +848,8 @@ + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + ++ s->auth_method = NGX_MAIL_AUTH_PLAIN; ++ + p = plain.data; + last = p + plain.len; + +@@ -555,6 +861,9 @@ + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + ++ s->dusr.data = plain.data; ++ s->dusr.len = p - plain.data - 1; ++ + s->login.data = p; + + while (p < last && *p) { p++; } +@@ -570,11 +879,29 @@ + s->passwd.len = last - p; + s->passwd.data = p; + ++ if (s->login.len > NGX_MAIL_MAX_LOGIN_LEN || ++ s->dusr.len > NGX_MAIL_MAX_LOGIN_LEN || ++ s->passwd.len > NGX_MAIL_MAX_PASSWORD_LEN) { ++ return NGX_MAIL_AUTH_FAILED; ++ } ++ + #if (NGX_DEBUG_MAIL_PASSWD) + ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0, + "mail auth plain: \"%V\" \"%V\"", &s->login, &s->passwd); + #endif + ++ if (s->dusr.len == 0) { ++ /* s->dusr = s->login; */ ++ s->usedauth = 0; ++ } else { ++ s->usedauth = 1; ++ temp = s->dusr; ++ s->dusr = s->login; ++ s->login = temp; ++ } ++ ++ s->dpasswd = s->passwd; ++ + return NGX_DONE; + } + +@@ -590,6 +917,22 @@ + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, + "mail auth login username: \"%V\"", &arg[n]); + ++ /* check if the auth exchange is being aborted */ ++ if (s->args.nelts > 0 && ++ arg[n].len == 1 && ++ arg[n].data[0] == '*' ++ ) ++ { ++ ngx_log_debug0 (NGX_LOG_DEBUG_MAIL, c->log, 0, ++ "auth:abort SASL LOGIN"); ++ ++ ngx_mail_dispose_sasl_context(s); ++ return NGX_MAIL_AUTH_ABORT; ++ } ++ ++ if (ngx_base64_decoded_length(arg->len) > NGX_MAIL_MAX_LOGIN_LEN) { ++ return NGX_MAIL_AUTH_FAILED; ++ } + s->login.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[n].len)); + if (s->login.data == NULL) { + return NGX_ERROR; +@@ -604,7 +947,7 @@ + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, + "mail auth login username: \"%V\"", &s->login); + +- return NGX_OK; ++ return NGX_MAIL_AUTH_ARGUMENT; + } + + +@@ -619,7 +962,21 @@ + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, + "mail auth login password: \"%V\"", &arg[0]); + #endif ++ /* check if the auth exchange is being aborted */ ++ if (s->args.nelts > 0 && ++ arg[0].len == 1 && ++ arg[0].data[0] == '*' ++ ) { ++ ngx_log_debug0 (NGX_LOG_DEBUG_MAIL, c->log, 0, ++ "auth:abort SASL LOGIN"); ++ ++ ngx_mail_dispose_sasl_context(s); ++ return NGX_MAIL_AUTH_ABORT; ++ } + ++ if(ngx_base64_decoded_length(arg[0].len) > NGX_MAIL_MAX_PASSWORD_LEN) { ++ return NGX_MAIL_AUTH_FAILED; ++ } + s->passwd.data = ngx_pnalloc(c->pool, + ngx_base64_decoded_length(arg[0].len)); + if (s->passwd.data == NULL) { +@@ -637,6 +994,9 @@ + "mail auth login password: \"%V\"", &s->passwd); + #endif + ++ s->auth_method = NGX_MAIL_AUTH_LOGIN; ++ s->usedauth = 0; ++ + return NGX_DONE; + } + +@@ -714,12 +1074,640 @@ + "mail auth cram-md5: \"%V\" \"%V\"", &s->login, &s->passwd); + + s->auth_method = NGX_MAIL_AUTH_CRAM_MD5; ++ s->usedauth = 0; + + return NGX_DONE; + } + + + ngx_int_t ++ngx_mail_auth_gssapi(ngx_mail_session_t *s, ngx_connection_t *c, ngx_str_t * output) ++{ ++ ngx_str_t *args, *arg; ++ ngx_uint_t narg; ++ ngx_mail_core_srv_conf_t *cscf; ++ int saslrc; ++ ++ args = s->args.elts; ++ narg = s->args.nelts; ++ arg = args + narg - 1; ++ ++ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); ++ ++ /* check if the auth exchange is being aborted */ ++ if (narg > 0 && ++ arg->len == 1 && ++ arg->data[0] == '*' ++ ) ++ { ++ ngx_log_debug0 (NGX_LOG_DEBUG_MAIL,c->log,0, ++ "auth:abort SASL GSSAPI"); ++ ++ ngx_mail_dispose_sasl_context(s); ++ return NGX_MAIL_AUTH_ABORT; ++ } ++ ++ /* Initialize SASL once per process */ ++ saslrc = ngx_mail_initialize_sasl (c); ++ ++ if (saslrc != SASL_OK) { ++ return NGX_ERROR; ++ } ++ ++ /* create one sasl authentication object per connection */ ++ saslrc = ngx_mail_create_sasl_context (c); ++ ++ if (saslrc != SASL_OK) { ++ return NGX_ERROR; ++ } ++ ++ saslrc = ngx_mail_sasl_startstep(c,"gssapi", arg, output); ++ ++ if (saslrc == SASL_CONTINUE) ++ { ++ return NGX_MAIL_AUTH_ARGUMENT; ++ } ++ else if (saslrc == SASL_OK) ++ { ++ s->dusr = cscf->master_auth_username; ++ s->dpasswd = cscf->master_auth_password; ++ s->auth_method = NGX_MAIL_AUTH_GSSAPI; ++ s->passwd = krb5_cooked_password; ++ s->usedauth = 1; ++ return NGX_DONE; ++ } ++ else ++ { ++ return NGX_ERROR; ++ } ++} ++ ++/* Perform a once-per-process initialization of the sasl library */ ++static int ++ngx_mail_initialize_sasl (ngx_connection_t *c) ++{ ++ ngx_mail_session_t *s; ++ ngx_mail_core_srv_conf_t *cscf; ++ int rc = SASL_OK; ++ char *app; ++ ++ if (!sasl_initialized) ++ { ++ s = c->data; ++ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); ++ ++ app = ngx_palloc(c->pool, cscf->sasl_app_name.len + 1); ++ ++ if (app == NULL) { return SASL_FAIL; } ++ ++ ngx_memcpy (app, cscf->sasl_app_name.data, cscf->sasl_app_name.len); ++ ngx_memcpy (app + cscf->sasl_app_name.len, "\x0", 1); ++ ++ ngx_log_debug1 (NGX_LOG_DEBUG_MAIL, c->log, 0, ++ "Initializing SASL library, app:%s", app); ++ ++ rc = sasl_server_init (NULL, app); ++ ++ if (rc != SASL_OK) ++ { ++ ngx_log_error (NGX_LOG_ERR, c->log, 0, ++ "Cannot initialize SASL library: err:%d, %s", ++ rc, sasl_errstring(rc,NULL,NULL) ++ ); ++ } ++ else ++ { ++ ngx_log_debug0 (NGX_LOG_DEBUG_MAIL, c->log, 0, ++ "Initialized SASL library"); ++ sasl_initialized = 1; ++ } ++ } ++ ++ return rc; ++} ++ ++static int ++ngx_mail_sasl_pauthorize (sasl_conn_t *conn, void *context, const char *authz, ++ unsigned authzlen, const char *authc, unsigned authclen, const char *realm, ++ unsigned rlen, struct propctx *propctx) ++{ ++ /* This function is called when we need to indicate whether the authz/authc ++ relationship should be allowed or not i.e can authc access authz's mailbox ++ since that decision must be made in the lookup servlet (which will happen later), ++ we need to defer that decision to the route lookup phase, and simply indicate our consent here ++ */ ++ ++ ngx_connection_t *c = context; ++ ngx_str_t nauthz = ngx_string(""), ++ nauthc = ngx_string(""), ++ nrealm = ngx_string(""); ++ ++ (void)c; ++ if (authz != NULL && authzlen > 0) { ++ nauthz.data = (u_char *)authz; ++ nauthz.len = authzlen; ++ } ++ if (authc != NULL && authclen > 0) { ++ nauthc.data = (u_char *)authc; ++ nauthc.len = authclen; ++ } ++ if (realm != NULL && rlen > 0) { ++ nrealm.data = (u_char *)realm; ++ nrealm.len = rlen; ++ } ++ ++ ngx_log_debug3(NGX_LOG_DEBUG_MAIL,c->log,0, ++ "sasl: indicating proxy policy ok, authz:%V,authc:%V,realm:%V", ++ &nauthz,&nauthc,&nrealm ++ ); ++ ++ return SASL_OK; ++} ++ ++static int ++ngx_mail_sasl_log (void *context, int level, const char *message) ++{ ++ ngx_connection_t *c = context; ++ ++ (void)c; ++ ngx_log_debug1 (NGX_LOG_DEBUG_MAIL, c->log, 0, ++ "%s", message == NULL ? "null" : message); ++ ++ return SASL_OK; ++} ++ ++ ++/* create a new SASL server authentication object (once per connection) */ ++static int ++ngx_mail_create_sasl_context (ngx_connection_t *c) ++{ ++ ngx_mail_session_t *s; ++ ngx_mail_core_srv_conf_t *cscf; ++ char *service; ++ int rc = SASL_OK; ++ sasl_security_properties_t rsec; ++ sasl_callback_t *callbacks; ++ ngx_uint_t i; ++ const char *fqdn = NULL; ++ struct hostent *host; ++ struct sockaddr_in sa; ++ socklen_t salen; ++ u_char *octets; ++ ++ s = c->data; ++ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); ++ ++ if (s->saslconn == NULL) ++ { ++ service = ngx_palloc (c->pool, cscf->sasl_service_name.len + 1); ++ if (service == NULL) { ++ return SASL_FAIL; ++ } ++ ++ callbacks = ngx_palloc(c->pool,sizeof(sasl_callback_t) * 8); ++ if (callbacks == NULL) { ++ ngx_log_error (NGX_LOG_ERR, c->log, 0, ++ "cannot alloc memory for SASL callbacks" ++ ); ++ return SASL_NOMEM; ++ } ++ ++ i =0 ; ++ ++ callbacks[i].id = SASL_CB_LOG; ++ callbacks[i].proc = (sasl_callback_ft)&ngx_mail_sasl_log; ++ callbacks[i].context = c; ++ ++i; ++ ++ callbacks[i].id = SASL_CB_PROXY_POLICY; ++ callbacks[i].proc = (sasl_callback_ft)&ngx_mail_sasl_pauthorize; ++ callbacks[i].context = c; ++ ++i; ++ ++ callbacks[i].id = SASL_CB_LIST_END; ++ callbacks[i].proc = NULL; ++ callbacks[i].context = NULL; ++ ++i; ++ ++ ngx_memcpy (service, cscf->sasl_service_name.data, ++ cscf->sasl_service_name.len); ++ service[cscf->sasl_service_name.len] = 0; ++ ++ /* The second argument to sasl_server_new is the FQDN of the server ++ If the srvprinc_from_ip configuration parameter is true, then ++ */ ++ ++ if (cscf->sasl_host_from_ip) ++ { ++ ngx_log_error (NGX_LOG_WARN, c->log, 0, ++ "will use IP address to resolve service principal"); ++ ++ salen = sizeof(sa); ++ if ( ++ getsockname(s->connection->fd, (struct sockaddr*)&sa, &salen) ++ == 0 ++ ) ++ { ++ if (sa.sin_family != AF_INET || salen != sizeof(sa)) ++ { ++ ngx_log_error(NGX_LOG_ERR, c->log, 0, ++ "non-ipv4 local address of mail connection ignored"); ++ } ++ else ++ { ++ octets = (u_char *)&sa.sin_addr.s_addr; ++ ++ ngx_log_error (NGX_LOG_WARN, c->log, 0, ++ "entering blocking network call (gethostbyaddr)"); ++ ++ host = gethostbyaddr( ++ &sa.sin_addr, ++ sizeof(sa.sin_addr), ++ AF_INET); ++ ++ if (host == NULL) ++ { ++ ngx_log_error (NGX_LOG_ERR, c->log, 0, ++ "cannot lookup host by IP address, err:%d", ++ h_errno); ++ } ++ else ++ { ++ ngx_log_error (NGX_LOG_INFO, c->log, 0, ++ "resolved incoming IP %d.%d.%d.%d to host:%s", ++ octets[0], ++ octets[1], ++ octets[2], ++ octets[3], ++ host->h_name); ++ ++ fqdn = host->h_name; ++ } ++ } ++ } ++ else ++ { ++ ngx_log_error(NGX_LOG_ERR, c->log, 0, ++ "cannot get local address of mail connection, err:%d", ++ ngx_errno); ++ } ++ } ++ ++ rc = sasl_server_new ++ ( ++ service, ++ fqdn, ++ NULL, ++ NULL, ++ NULL, ++ callbacks, ++ 0, ++ &s->saslconn ++ ); ++ ++ if (rc != SASL_OK) ++ { ++ ngx_log_error (NGX_LOG_ERR, c->log, 0, ++ "cannot create SASL context (%V), err:%d,%s", ++ &cscf->sasl_service_name, ++ rc, sasl_errstring (rc,NULL,NULL) ++ ); ++ s->saslconn = NULL; ++ } ++ else ++ { ++ const char * mechlist; ++ unsigned menLen; ++ int num; ++ rc = sasl_listmech(s->saslconn, NULL, "{", ", ", "}", &mechlist, &menLen, &num); ++ ngx_log_error(NGX_LOG_INFO, c->log,0, "mech list is: %s", mechlist); ++ ngx_log_debug2 (NGX_LOG_DEBUG_MAIL, c->log, 0, ++ "created SASL context (%V), 0x%p", ++ &cscf->sasl_service_name, ++ s->saslconn ++ ); ++ ++ rsec.min_ssf = 0; ++ rsec.max_ssf = 0; ++ rsec.maxbufsize = 4096; ++ rsec.property_names = NULL; ++ rsec.property_values = NULL; ++ rsec.security_flags = 0; ++ ++ rc = sasl_setprop(s->saslconn, SASL_SEC_PROPS, &rsec); ++ } ++ } ++ ++ return rc; ++} ++ ++static void ++ngx_mail_dispose_sasl_context (ngx_mail_session_t *s) ++{ ++ if (s->saslconn != NULL) ++ { ++ ngx_log_debug1(NGX_LOG_DEBUG_MAIL,s->connection->log,0, ++ "disposing SASL context:%p",s->saslconn); ++ sasl_dispose(&s->saslconn); ++ ngx_log_debug1(NGX_LOG_DEBUG_MAIL,s->connection->log,0, ++ "disposed SASL context:%p",s->saslconn); ++ s->saslconn = NULL; ++ } ++ return; ++} ++ ++static int ++ngx_mail_sasl_startstep ( ++ ngx_connection_t *c, ++ const char *mech, ++ ngx_str_t *response, ++ ngx_str_t *challenge ++ ) ++{ ++ ngx_mail_session_t *s; ++ ngx_str_t r; ++ int rc; ++ const char *saslstr,*authc,*authz; ++ unsigned sasls; ++ ngx_str_t ch64, ch; ++ ngx_mail_core_srv_conf_t *cscf; ++ u_char *p; ++ ngx_flag_t inheritAuthZ, needRealm; ++ size_t len; ++ ++ s = c->data; ++ cscf = ngx_mail_get_module_srv_conf(s,ngx_mail_core_module); ++ ++ /* saslfr (fr = first response) indicates whether the client has ++ issued at least one SASL response to the server ++ saslfr starts out as 0, and is immediately set to 1 when the ++ server starts processing the client responses ++ */ ++ if (!s->saslfr) ++ { ++ ngx_log_debug0 (NGX_LOG_DEBUG_MAIL, c->log, 0, ++ "beginning SASL auth negotiation"); ++ ++ if (response == NULL) ++ { ++ ngx_log_debug0 (NGX_LOG_DEBUG_MAIL, c->log, 0, ++ "using NULL client response"); ++ ++ r.data = NULL; ++ r.len = 0; ++ } ++ else ++ { ++ ngx_log_debug1 (NGX_LOG_DEBUG_MAIL, c->log, 0, ++ "using response %V", response); ++ ++ r.len = ngx_base64_decoded_length (response->len); ++ r.data = ngx_palloc (c->pool, r.len); ++ ++ if (r.data == NULL) { ++ return SASL_FAIL; ++ } ++ ++ if (ngx_decode_base64 (&r, response) != NGX_OK) ++ { ++ ngx_log_error (NGX_LOG_ERR, c->log, 0, ++ "invalid base64 response sent by client"); ++ ++ return SASL_FAIL; ++ } ++ else ++ { ++ ngx_log_debug2 (NGX_LOG_DEBUG_MAIL, c->log, 0, ++ "%d bytes of base64-challenge decoded to %d sasl-bytes", ++ response->len, r.len); ++ } ++ } ++ ++ rc = sasl_server_start ++ ( ++ s->saslconn, ++ mech, ++ (char *)r.data, ++ r.len, ++ &saslstr, ++ &sasls ++ ); ++ ++ s->saslfr = 1; ++ } ++ else ++ { ++ ngx_log_debug0 (NGX_LOG_DEBUG_MAIL, c->log, 0, ++ "continuing SASL auth negotiation"); ++ ++ r.len = ngx_base64_decoded_length (response->len); ++ r.data = ngx_palloc (c->pool, r.len); ++ ++ if (r.data == NULL) { ++ return SASL_FAIL; ++ } ++ ++ if (ngx_decode_base64 (&r, response) != NGX_OK) ++ { ++ ngx_log_error (NGX_LOG_ERR, c->log, 0, ++ "invalid base64 response sent by client"); ++ ++ return SASL_FAIL; ++ } ++ ++ rc = sasl_server_step ++ ( ++ s->saslconn, ++ (char *)r.data, ++ r.len, ++ &saslstr, ++ &sasls ++ ); ++ } ++ ++ if ((rc != SASL_OK) && (rc != SASL_CONTINUE)) ++ { ++ ngx_log_error (NGX_LOG_ERR, c->log, 0, ++ "SASL auth negotiation failed, err:%d (%s)", ++ rc, sasl_errstring(rc,NULL,NULL)); ++ } ++ else ++ { ++ /* construct the challenge depending upon the protocol */ ++ ++ ngx_log_debug1 (NGX_LOG_DEBUG_MAIL, c->log, 0, ++ "constructing protocol specific response for %d bytes of challenge", ++ sasls); ++ ++ if (saslstr == NULL || sasls == 0) ++ { ++ ch64.data = (u_char *)""; ++ ch64.len = 0; ++ } ++ else ++ { ++ ch.len = sasls; ++ ch.data = (u_char *)saslstr; ++ ++ ch64.len = ngx_base64_encoded_length(ch.len); ++ ch64.data = ngx_palloc (c->pool, ch64.len); ++ ++ if (ch64.data == NULL) { ++ return SASL_FAIL; ++ } ++ ++ ngx_encode_base64 (&ch64, &ch); ++ } ++ ++ if (rc == SASL_CONTINUE) ++ { ++ /* For IMAP/POP, we need to send "+" SP CRLF */ ++ if (s->protocol == NGX_MAIL_IMAP_PROTOCOL || ++ s->protocol == NGX_MAIL_POP3_PROTOCOL ++ ) ++ { ++ challenge->len = sizeof("+ ") -1 + ch64.len + sizeof(CRLF) -1; ++ challenge->data = ngx_palloc (c->pool,challenge->len); ++ ++ if (challenge->data == NULL) { ++ return SASL_FAIL; ++ } ++ ++ memcpy (challenge->data,"+ ",sizeof("+ ") - 1); ++ memcpy (challenge->data+sizeof("+ ")-1,ch64.data,ch64.len); ++ memcpy (challenge->data+sizeof("+ ")-1+ch64.len,CRLF, ++ sizeof(CRLF)-1); ++ } ++ else ++ { ++ challenge->data = ch64.data; ++ challenge->len = ch64.len; ++ } ++ } ++ else /* SASL_OK */ ++ { ++ ngx_log_debug0 (NGX_LOG_DEBUG_MAIL, c->log, 0, ++ "SASL auth negotiation complete"); ++ ++ authc = NULL; ++ authz = NULL; ++ ++ sasl_getprop(s->saslconn, SASL_AUTHUSER, (const void **)&authc); ++ sasl_getprop(s->saslconn, SASL_USERNAME, (const void **)&authz); ++ ++ ngx_log_debug2 (NGX_LOG_DEBUG_MAIL, c->log, 0, ++ "sasl: authc=%s,authz=%s", ++ authc == NULL ? "null" : authc, ++ authz == NULL ? "null" : authz ++ ); ++ ++ /* authc must always be present ++ if authc doesn't end in @realm, then we append the default realm ++ from the config file ++ */ ++ ++ /* s->login is authz if present, otherwise it is authc ++ */ ++ ++ if (authc == NULL) ++ { ++ ngx_log_debug0(NGX_LOG_DEBUG_MAIL,c->log,0, ++ "sasl: cannot get authc, authentication will fail"); ++ rc = SASL_BADAUTH; ++ } ++ else ++ { ++ if (strrchr(authc,'@') == NULL) { ++ needRealm = 1; ++ } else { ++ needRealm = 0; ++ } ++ ++ if ((authz == NULL) || (ngx_strcmp(authc,authz) == 0)) { ++ inheritAuthZ = 1; ++ } else { ++ inheritAuthZ = 0; ++ } ++ ++ len = ngx_strlen(authc); ++ ++ if (needRealm) { ++ if (cscf->default_realm.len > 0) { ++ ngx_log_debug1(NGX_LOG_DEBUG_MAIL,c->log,0, ++ "No realm found in AUTHC, using config default %V", &cscf->default_realm); ++ len += (1 + cscf->default_realm.len); ++ } else { ++ ngx_log_error(NGX_LOG_ERR,c->log, 0, ++ "SASL realm required, but no realm found in authenticating principal"); ++ ngx_log_error(NGX_LOG_ERR,c->log, 0, ++ "Authentication will fail. Set the `default_realm' variable to the default kerberos realm"); ++ } ++ } ++ ++ s->authid.data = ngx_palloc(c->pool,len); ++ if (s->authid.data == NULL) { ++ s->authid.data = (u_char *)""; ++ s->authid.len = 0; ++ rc = SASL_NOMEM; ++ } else { ++ s->authid.len = len; ++ p = s->authid.data; ++ p = ngx_cpymem (p,authc,strlen(authc)); ++ ++ if (needRealm) { ++ if (cscf->default_realm.len > 0) { ++ *p++ = '@'; ++ p = ngx_cpymem (p,cscf->default_realm.data,cscf->default_realm.len); ++ } ++ } ++ } ++ ++ if (inheritAuthZ) { ++ /* no separate authz was specified, or authz was same as authc ++ therefore the same changes made to authc must apply to authz ++ */ ++ s->login.data = ngx_pstrdup(c->pool,&s->authid); ++ if (s->login.data == NULL) { ++ s->login.data = (u_char*)""; ++ s->login.len = 0; ++ rc = SASL_NOMEM; ++ } else { ++ s->login.len = s->authid.len; ++ } ++ } else { ++ /* a separate authz was specified */ ++ s->login.len = ngx_strlen(authz); ++ s->login.data = ngx_palloc(c->pool,s->login.len); ++ if (s->login.data == NULL) { ++ s->login.data = (u_char*)""; ++ s->login.len = 0; ++ rc = SASL_NOMEM; ++ } else { ++ ngx_memcpy(s->login.data,authz,s->login.len); ++ } ++ } ++ } ++ ++ if(rc == SASL_OK) ++ { ++ ngx_log_debug2(NGX_LOG_DEBUG_MAIL,c->log,0, ++ "sasl: auth exchange completed, login:%V, authc:%V", ++ &s->login, &s->authid); ++ } ++ ++ /* we don't need the SASL object after authentication because ++ we don't negotiate a security layer with any ssf ++ */ ++ ++ ngx_mail_dispose_sasl_context(s); ++ } ++ } ++ ++ return rc; ++} ++ ++ ++ngx_int_t + ngx_mail_auth_external(ngx_mail_session_t *s, ngx_connection_t *c, + ngx_uint_t n) + { +@@ -794,7 +1782,7 @@ + } + + if (s->quit) { +- ngx_mail_close_connection(c); ++ ngx_mail_end_session(s); + return; + } + +@@ -825,6 +1813,84 @@ + } + + ++void ++ngx_mail_do_auth(ngx_mail_session_t *s, ngx_connection_t *c) ++{ ++ throttle_callback_t *callback; ++ ngx_mail_throttle_srv_conf_t *tscf; ++ ngx_mail_zmauth_conf_t *zmcf; ++ ++ zmcf = (ngx_mail_zmauth_conf_t *)ngx_mail_get_module_srv_conf(s, ngx_mail_zmauth_module); ++ if (zmcf->use_zmauth != 1) { ++ s->qlogin = s->login; ++ ngx_mail_auth(s, c); ++ return; ++ } ++ ++ /* all auth mechanisms for all protocols pass through ngx_mail_do_auth() ++ here. Therefore, it is best to just look at the zimbra extensions ++ *once* at this point, rather than peppering that code all across ++ */ ++ ++ if (has_zimbra_extensions(s->login)) { ++ s->zlogin = get_zimbra_extension(s->login); ++ s->login.len -= s->zlogin.len; ++ } else { ++ s->zlogin.data = (u_char *)""; ++ s->zlogin.len = 0; ++ } ++ ++ if (s->usedauth) ++ { ++ if (has_zimbra_extensions(s->dusr)) { ++ s->zusr = get_zimbra_extension(s->dusr); ++ s->dusr.len -= s->zusr.len; ++ } else { ++ s->zusr.data = (u_char *)""; ++ s->zusr.len = 0; ++ } ++ } ++ ++ if (s->usedauth) { ++ /* technically, zimbra extensions are not allowed in authc ++ but it is too troublesome to reject the login appropriately ++ at this point (with the correct message), therefore it is ++ less bother to just pass the authc + {wm,ni,tb} to upstream ++ */ ++ if (s->login.len == s->dusr.len && ++ ngx_memcmp(s->login.data, s->dusr.data, s->login.len) == 0) { ++ s->qualifydauth = 1; ++ } ++ } ++ ++ callback = ngx_pcalloc(c->pool, sizeof(throttle_callback_t)); ++ if (callback == NULL) { ++ ngx_mail_session_internal_server_error(s); ++ return; ++ } ++ ++ callback->check_only = 1; /* just check the counter's value */ ++ callback->session = s; ++ callback->connection = c; ++ callback->log = ngx_cycle->log; ++ callback->pool = c->pool; ++ callback->on_allow = ngx_mail_allow_userauth; ++ callback->on_deny = ngx_mail_choke_userauth; ++ ++ /* because of DOS attacks against legitimate users, throttling is ++ postponed till after authentication ++ */ ++ tscf = ngx_mail_get_module_srv_conf (s, ngx_mail_throttle_module); ++ if (tscf->mail_login_user_max == 0) { ++ callback->on_allow(callback); ++ } else { ++ ngx_mail_throttle_user(s->login, callback); ++ } ++ ++ /* previous body of ngx_mail_do_auth() now in ngx_mail_allow_userauth */ ++} ++ ++ + ngx_int_t + ngx_mail_read_command(ngx_mail_session_t *s, ngx_connection_t *c) + { +@@ -922,17 +1988,72 @@ + } + + ++/* send a protocol-suitable internal error message to downstream ++ close the downstream connection immediately afterwards ++ */ + void + ngx_mail_session_internal_server_error(ngx_mail_session_t *s) + { + ngx_mail_core_srv_conf_t *cscf; ++ ngx_connection_t *c; + + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + + s->out = cscf->protocol->internal_server_error; +- s->quit = 1; + ++ c = s->connection->write->data; + ngx_mail_send(s->connection->write); ++ if (c->destroyed) { ++ return; ++ } ++ /* clean up */ ++ ngx_mail_cleanup_t * cln = s->cleanup; ++ while (cln != NULL) { ++ cln->handler(cln->data); ++ cln = cln->next; ++ } ++ ++ ngx_mail_close_connection (s->connection); ++} ++ ++ ++/* send a protocol-suitable bye message to downstream ++ close the downstream connection immediately afterwards ++ */ ++void ++ngx_mail_end_session(ngx_mail_session_t *s) ++{ ++ ngx_str_t bye = ngx_mail_session_getquitmsg(s); ++ ngx_connection_t *c = s->connection; ++ ++ if (bye.len > 0) { ++ c->send(c, bye.data, bye.len); ++ } ++ ++ /* clean up */ ++ ngx_mail_cleanup_t * cln = s->cleanup; ++ while (cln != NULL) { ++ cln->handler(cln->data); ++ cln = cln->next; ++ } ++ ++ ngx_mail_close_connection (c); ++} ++ ++/* return protocol-specific bye message */ ++ngx_str_t ngx_mail_session_getquitmsg(ngx_mail_session_t *s) ++{ ++ ngx_mail_core_srv_conf_t *cscf; ++ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); ++ return cscf->protocol->quit_msg; ++} ++ ++/* return protocol-specific internal error message */ ++ngx_str_t ngx_mail_session_geterrmsg(ngx_mail_session_t *s) ++{ ++ ngx_mail_core_srv_conf_t *cscf; ++ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); ++ return cscf->protocol->internal_server_error; + } + + +@@ -969,6 +2090,13 @@ + } + + ++/* note -- we want to log the local and remote host/port information for the ++ mail proxy sessions. however, nginx allows a mail servers to be specified as ++ listening on a unix domain socket. the code below assumes that the sockaddr ++ structure is pointing to an IPv4 address, and prints the address information ++ accordingly. we will need to modify the code in case we want to support ++ printing of unix domain socket information ++ */ + u_char * + ngx_mail_log_error(ngx_log_t *log, u_char *buf, size_t len) + { +@@ -984,7 +2112,11 @@ + + ctx = log->data; + +- p = ngx_snprintf(buf, len, ", client: %V", ctx->client); ++ if (ctx->client_port && ctx->client_port < 65536) { ++ p = ngx_snprintf(buf, len, ", client: %V:%ui", ctx->client, ctx->client_port); ++ } else { ++ p = ngx_snprintf(buf, len, ", client: %V", ctx->client, ctx->client_port); ++ } + len -= p - buf; + buf = p; + +@@ -1012,7 +2144,127 @@ + return p; + } + ++ /* with proxy, output the proxy relationship */ ++ ++ u_char dw_host[NGX_SOCKADDRLEN], ++ dw_peer[NGX_SOCKADDRLEN], ++ up_host[NGX_SOCKADDRLEN], ++ up_peer[NGX_SOCKADDRLEN]; ++ ++ socklen_t dw_host_len, dw_peer_len, ++ up_host_len, up_peer_len, ++ n; ++ ++ ngx_memzero (dw_peer, NGX_SOCKADDRLEN); ++ ngx_memzero (dw_host, NGX_SOCKADDRLEN); ++ ngx_memzero (up_host, NGX_SOCKADDRLEN); ++ ngx_memzero (up_peer, NGX_SOCKADDRLEN); ++ ++ dw_host_len = dw_peer_len = up_host_len = up_peer_len = NGX_SOCKADDRLEN; ++ ++ if (s->connection) { ++ getsockname ++ (s->connection->fd, (struct sockaddr *)dw_host, &dw_host_len); ++ ++ getpeername ++ (s->connection->fd, (struct sockaddr *)dw_peer, &dw_peer_len); ++ } ++ ++ if (s->proxy->upstream.connection) { ++ getsockname (s->proxy->upstream.connection->fd, ++ (struct sockaddr *)up_host, &up_host_len); ++ getpeername (s->proxy->upstream.connection->fd, ++ (struct sockaddr *)up_peer, &up_peer_len); ++ } ++ + p = ngx_snprintf(buf, len, ", upstream: %V", s->proxy->upstream.name); + ++ len -= p - buf; ++ buf = p; ++ ++ /* generate "(dw_peer->dw_host) <=> (up_host->up_peer)" */ ++ p = ngx_snprintf(buf, len, " ("); ++ len -= p - buf; ++ buf = p; ++ ++ n = ngx_sock_ntop((struct sockaddr *)dw_peer, dw_peer_len, buf, len, 1); ++ len -= n; ++ buf += n; ++ ++ *buf++ = '-'; ++ len--; ++ *buf++ = '>'; ++ len--; ++ ++ n = ngx_sock_ntop((struct sockaddr *)dw_host, dw_host_len, buf, len, 1); ++ len -= n; ++ buf += n; ++ ++ p = ngx_snprintf(buf, len, ") <=> ("); ++ len -= p - buf; ++ buf = p; ++ ++ n = ngx_sock_ntop((struct sockaddr *)up_host, up_host_len, buf, len, 1); ++ len -= n; ++ buf += n; ++ ++ *buf++ = '-'; ++ len--; ++ *buf++ = '>'; ++ len--; ++ ++ n = ngx_sock_ntop((struct sockaddr *)up_peer, up_peer_len, buf, len, 1); ++ len -= n; ++ buf += n; ++ ++ *buf++ = ')'; ++ len--; ++ ++ p = buf; ++ + return p; + } ++ ++ ++/* ++ * Giving a socket, return its local addr string representation IP. The ++ * string will be allocated on "pool". ++ */ ++ngx_str_t ngx_mail_get_socket_local_addr_str (ngx_pool_t *pool, ngx_socket_t s) ++{ ++ int family; ++ static ngx_str_t res; ++ struct sockaddr_in *sin; ++#if (NGX_HAVE_INET6) ++ struct sockaddr_in6 *sin6; ++#endif ++ u_char *p; ++ socklen_t len, strlen; ++ u_char sockaddr[NGX_SOCKADDRLEN]; ++ ++ len = NGX_SOCKADDRLEN; ++ ngx_memzero(sockaddr, len); ++ getsockname(s, (struct sockaddr*)sockaddr, &len); ++ ++ family = ((struct sockaddr *)sockaddr)->sa_family; ++ if (family == AF_INET) { ++ sin = (struct sockaddr_in *)sockaddr; ++ p = ngx_palloc(pool, NGX_INET_ADDRSTRLEN); ++ strlen = ngx_inet_ntop (family, &(sin->sin_addr.s_addr), p, ++ NGX_INET_ADDRSTRLEN); ++ ++#if (NGX_HAVE_INET6) ++ } else { ++ sin6 = (struct sockaddr_in6 *)sockaddr; ++ p = ngx_palloc(pool, NGX_INET6_ADDRSTRLEN); ++ strlen = ngx_inet_ntop (family, &(sin6->sin6_addr.s6_addr), ++ p, NGX_INET6_ADDRSTRLEN); ++#endif ++ ++ } ++ ++ res.data = p; ++ res.len = strlen; ++ ++ return res; ++} diff --git a/thirdparty/nginx/patches/nginx_src_mail_ngx_mail_imap_handler.patch b/thirdparty/nginx/patches/nginx_src_mail_ngx_mail_imap_handler.patch new file mode 100644 index 000000000..31b3cc899 --- /dev/null +++ b/thirdparty/nginx/patches/nginx_src_mail_ngx_mail_imap_handler.patch @@ -0,0 +1,462 @@ +--- nginx/src/mail/ngx_mail_imap_handler.c 2023-03-07 16:26:44.718094500 +0530 ++++ nginx/src/mail/ngx_mail_imap_handler.c 2023-09-14 18:47:12.625941200 +0530 +@@ -20,27 +20,34 @@ + ngx_connection_t *c); + static ngx_int_t ngx_mail_imap_starttls(ngx_mail_session_t *s, + ngx_connection_t *c); ++static ngx_int_t ngx_mail_imap_id(ngx_mail_session_t *s, ++ ngx_connection_t *c); + +- +-static u_char imap_greeting[] = "* OK IMAP4 ready" CRLF; +-static u_char imap_star[] = "* "; ++static u_char imap_star[] = "*"; + static u_char imap_ok[] = "OK completed" CRLF; +-static u_char imap_next[] = "+ OK" CRLF; ++static u_char imap_next[] = "+ " CRLF; + static u_char imap_plain_next[] = "+ " CRLF; ++static u_char imap_gssapi_next[] = "+ " CRLF; + static u_char imap_username[] = "+ VXNlcm5hbWU6" CRLF; + static u_char imap_password[] = "+ UGFzc3dvcmQ6" CRLF; +-static u_char imap_bye[] = "* BYE" CRLF; + static u_char imap_invalid_command[] = "BAD invalid command" CRLF; ++static u_char imap_unsupported_mech[] = "NO mechanism not supported" CRLF; ++static u_char imap_nocleartext[] = "NO cleartext logins disabled" CRLF; ++static u_char imap_authaborted[] = "BAD AUTHENTICATE aborted" CRLF; ++static u_char imap_login_failed[] = "NO LOGIN failed" CRLF; ++static u_char imap_authenticate_failed[] = "NO AUTHENTICATE failed" CRLF; + + + void + ngx_mail_imap_init_session(ngx_mail_session_t *s, ngx_connection_t *c) + { + ngx_mail_core_srv_conf_t *cscf; ++ ngx_mail_imap_srv_conf_t *iscf; + + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); ++ iscf = ngx_mail_get_module_srv_conf(s, ngx_mail_imap_module); + +- ngx_str_set(&s->out, imap_greeting); ++ s->out = iscf->greeting; + + c->read->handler = ngx_mail_imap_init_protocol; + +@@ -91,6 +98,7 @@ + } + } + ++ ngx_mail_set_imap_parse_state_start(s); + s->mail_state = ngx_imap_start; + c->read->handler = ngx_mail_imap_auth_state; + +@@ -115,7 +123,7 @@ + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); + c->timedout = 1; +- ngx_mail_close_connection(c); ++ ngx_mail_end_session(s); /* send IMAP BYE on timeout */ + return; + } + +@@ -149,8 +157,8 @@ + } + + tag = 1; +- s->text.len = 0; + ngx_str_set(&s->out, imap_ok); ++ s->text.len = 0; + + if (rc == NGX_OK) { + +@@ -169,7 +177,6 @@ + + case NGX_IMAP_AUTHENTICATE: + rc = ngx_mail_imap_authenticate(s, c); +- tag = (rc != NGX_OK); + break; + + case NGX_IMAP_CAPABILITY: +@@ -178,7 +185,6 @@ + + case NGX_IMAP_LOGOUT: + s->quit = 1; +- ngx_str_set(&s->text, imap_bye); + break; + + case NGX_IMAP_NOOP: +@@ -188,6 +194,10 @@ + rc = ngx_mail_imap_starttls(s, c); + break; + ++ case NGX_IMAP_ID: ++ rc = ngx_mail_imap_id(s, c); ++ break; ++ + default: + rc = NGX_MAIL_PARSE_INVALID_COMMAND; + break; +@@ -198,10 +208,10 @@ + case ngx_imap_auth_login_username: + rc = ngx_mail_auth_login_username(s, c, 0); + +- tag = 0; +- ngx_str_set(&s->out, imap_password); +- s->mail_state = ngx_imap_auth_login_password; +- ++ if (rc == NGX_MAIL_AUTH_ARGUMENT) { ++ ngx_str_set(&s->out, imap_password); ++ s->mail_state = ngx_imap_auth_login_password; ++ } + break; + + case ngx_imap_auth_login_password: +@@ -212,6 +222,18 @@ + rc = ngx_mail_auth_plain(s, c, 0); + break; + ++ case ngx_imap_auth_gssapi: ++ { ++ ngx_str_t output; ++ ngx_str_set(&output, ""); ++ rc = ngx_mail_auth_gssapi(s, c, &output); ++ if (rc == NGX_MAIL_AUTH_ARGUMENT) { ++ s->mail_state = ngx_imap_auth_gssapi; ++ s->out = output; ++ } ++ break; ++ } ++ + case ngx_imap_auth_cram_md5: + rc = ngx_mail_auth_cram_md5(s, c); + break; +@@ -233,17 +255,75 @@ + switch (rc) { + + case NGX_DONE: +- ngx_mail_auth(s, c); ++ ngx_mail_do_auth(s, c); + return; + ++ case NGX_OK: ++ ngx_mail_set_imap_parse_state_start(s); ++ s->arg_start = NULL; ++ ngx_mail_reset_parse_buffer(s); ++ break; ++ ++ case NGX_MAIL_AUTH_ABORT: ++ ngx_str_set(&s->out, imap_authaborted); ++ s->mail_state = ngx_imap_start; ++ ngx_mail_set_imap_parse_state_start(s); ++ s->arg_start = NULL; ++ ngx_mail_reset_parse_buffer(s); ++ break; ++ + case NGX_ERROR: + ngx_mail_session_internal_server_error(s); + return; + ++ case NGX_MAIL_AUTH_FAILED: ++ ngx_str_set(&s->out, imap_authenticate_failed); ++ s->mail_state = ngx_imap_start; ++ ngx_mail_set_imap_parse_state_start(s); ++ s->arg_start = NULL; ++ ngx_mail_reset_parse_buffer(s); ++ break; ++ ++ case NGX_MAIL_LOGIN_FAILED: ++ ngx_str_set(&s->out, imap_login_failed); ++ s->mail_state = ngx_imap_start; ++ ngx_mail_set_imap_parse_state_start(s); ++ s->arg_start = NULL; ++ ngx_mail_reset_parse_buffer(s); ++ break; ++ ++ case NGX_MAIL_PARSE_INVALID_AUTH_MECH: ++ ngx_log_debug0 (NGX_LOG_DEBUG_MAIL, c->log, 0, ++ "unsupported IMAP auth mechanism"); ++ ngx_str_set(&s->out, imap_unsupported_mech); ++ s->mail_state = ngx_imap_start; ++ ngx_mail_set_imap_parse_state_start(s); ++ s->arg_start = NULL; ++ ngx_mail_reset_parse_buffer(s); ++ break; ++ + case NGX_MAIL_PARSE_INVALID_COMMAND: +- s->state = 0; + ngx_str_set(&s->out, imap_invalid_command); + s->mail_state = ngx_imap_start; ++ ngx_mail_set_imap_parse_state_start(s); ++ s->arg_start = NULL; ++ ngx_mail_reset_parse_buffer(s); ++ break; ++ ++ case NGX_MAIL_AUTH_ARGUMENT: ++ ngx_mail_set_imap_parse_state_argument(s); ++ /* preserve tag, since tag's memory is allocated in buffer, need to set the ++ * buffer pos after tag */ ++ s->arg_start = s->buffer->start + s->tag.len; ++ s->buffer->pos = s->arg_start; ++ s->buffer->last = s->arg_start; ++ tag = 0; // just output s->out ++ break; ++ ++ case NGX_IMAP_NEXT: ++ /* do nothing, preserve all the state, including s->state, s->mail_state, ++ * , s->buffer, s->arg_start ++ */ + break; + } + +@@ -252,8 +332,8 @@ + ngx_str_set(&s->tag, imap_star); + } + +- if (s->tagged_line.len < s->tag.len + s->text.len + s->out.len) { +- s->tagged_line.len = s->tag.len + s->text.len + s->out.len; ++ if (s->tagged_line.len < s->tag.len + s->text.len + s->out.len + 1) { ++ s->tagged_line.len = s->tag.len + s->text.len + s->out.len + 1; + s->tagged_line.data = ngx_pnalloc(c->pool, s->tagged_line.len); + if (s->tagged_line.data == NULL) { + ngx_mail_close_connection(c); +@@ -268,9 +348,10 @@ + } + + p = ngx_cpymem(p, s->tag.data, s->tag.len); ++ *p++ = ' '; /* the space between tag and out */ + ngx_memcpy(p, s->out.data, s->out.len); + +- s->out.len = s->text.len + s->tag.len + s->out.len; ++ s->out.len = s->text.len + s->tag.len + 1 /*for space*/ + s->out.len; + s->out.data = s->tagged_line.data; + } + +@@ -283,8 +364,8 @@ + + } else { + if (s->buffer->pos == s->buffer->last) { +- s->buffer->pos = s->buffer->start; +- s->buffer->last = s->buffer->start; ++ s->buffer->pos = s->buffer->start; ++ s->buffer->last = s->buffer->start; + } + + s->tag.len = 0; +@@ -307,6 +388,7 @@ + + #if (NGX_MAIL_SSL) + if (ngx_mail_starttls_only(s, c)) { ++ ngx_str_set(&s->text, imap_nocleartext); + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + #endif +@@ -317,6 +399,10 @@ + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + ++ if (arg[0].len > NGX_MAIL_MAX_LOGIN_LEN) { ++ return NGX_MAIL_LOGIN_FAILED; ++ } ++ + s->login.len = arg[0].len; + s->login.data = ngx_pnalloc(c->pool, s->login.len); + if (s->login.data == NULL) { +@@ -325,6 +411,10 @@ + + ngx_memcpy(s->login.data, arg[0].data, s->login.len); + ++ if (arg[1].len > NGX_MAIL_MAX_PASSWORD_LEN) { ++ return NGX_MAIL_LOGIN_FAILED; ++ } ++ + s->passwd.len = arg[1].len; + s->passwd.data = ngx_pnalloc(c->pool, s->passwd.len); + if (s->passwd.data == NULL) { +@@ -342,6 +432,8 @@ + "imap login:\"%V\"", &s->login); + #endif + ++ s->auth_method = NGX_MAIL_AUTH_PASSWD; ++ s->usedauth = 0; + return NGX_DONE; + } + +@@ -349,7 +441,7 @@ + static ngx_int_t + ngx_mail_imap_authenticate(ngx_mail_session_t *s, ngx_connection_t *c) + { +- ngx_int_t rc; ++ ngx_int_t rc, res; + ngx_mail_core_srv_conf_t *cscf; + ngx_mail_imap_srv_conf_t *iscf; + +@@ -359,37 +451,85 @@ + } + #endif + +- iscf = ngx_mail_get_module_srv_conf(s, ngx_mail_imap_module); +- + rc = ngx_mail_auth_parse(s, c); + ++ iscf = ngx_mail_get_module_srv_conf(s, ngx_mail_imap_module); ++ + switch (rc) { + + case NGX_MAIL_AUTH_LOGIN: + ++ if (!(iscf->auth_methods & NGX_MAIL_AUTH_LOGIN_ENABLED)) { ++ return NGX_MAIL_PARSE_INVALID_AUTH_MECH; ++ } + ngx_str_set(&s->out, imap_username); + s->mail_state = ngx_imap_auth_login_username; + +- return NGX_OK; ++ return NGX_MAIL_AUTH_ARGUMENT; + + case NGX_MAIL_AUTH_LOGIN_USERNAME: + +- ngx_str_set(&s->out, imap_password); +- s->mail_state = ngx_imap_auth_login_password; ++ if (!(iscf->auth_methods & NGX_MAIL_AUTH_LOGIN_ENABLED)) { ++ return NGX_MAIL_PARSE_INVALID_AUTH_MECH; ++ } ++ ++ res = ngx_mail_auth_login_username(s, c, 1); ++ if (res == NGX_MAIL_AUTH_ARGUMENT) { ++ ngx_str_set(&s->out, imap_password); ++ s->mail_state = ngx_imap_auth_login_password; ++ return NGX_MAIL_AUTH_ARGUMENT; ++ } else { ++ return res; ++ } + +- return ngx_mail_auth_login_username(s, c, 1); + + case NGX_MAIL_AUTH_PLAIN: + ++ if (!(iscf->auth_methods & NGX_MAIL_AUTH_PLAIN_ENABLED)) { ++ return NGX_MAIL_PARSE_INVALID_AUTH_MECH; ++ } + ngx_str_set(&s->out, imap_plain_next); + s->mail_state = ngx_imap_auth_plain; + +- return NGX_OK; ++ return NGX_MAIL_AUTH_ARGUMENT; ++ ++ case NGX_MAIL_AUTH_PLAIN_IR: ++ ++ if (!(iscf->auth_methods & NGX_MAIL_AUTH_PLAIN_ENABLED)) { ++ return NGX_MAIL_PARSE_INVALID_AUTH_MECH; ++ } ++ return ngx_mail_auth_plain(s, c, 1); ++ ++ case NGX_MAIL_AUTH_GSSAPI: ++ ++ if (!(iscf->auth_methods & NGX_MAIL_AUTH_GSSAPI_ENABLED)) { ++ return NGX_MAIL_PARSE_INVALID_AUTH_MECH; ++ } ++ ngx_str_set(&s->out, imap_gssapi_next); ++ s->mail_state = ngx_imap_auth_gssapi; ++ ++ return NGX_MAIL_AUTH_ARGUMENT; ++ ++ case NGX_MAIL_AUTH_GSSAPI_IR: ++ ++ if (!(iscf->auth_methods & NGX_MAIL_AUTH_GSSAPI_ENABLED)) { ++ return NGX_MAIL_PARSE_INVALID_AUTH_MECH; ++ } ++ s->mail_state = ngx_imap_auth_gssapi; ++ ngx_str_t output; ++ ngx_str_set(&output, ""); ++ res = ngx_mail_auth_gssapi(s, c, &output); ++ if(res == NGX_MAIL_AUTH_ARGUMENT) { ++ s->out = output; ++ return NGX_MAIL_AUTH_ARGUMENT; ++ } else { ++ return res; ++ } + + case NGX_MAIL_AUTH_CRAM_MD5: + + if (!(iscf->auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED)) { +- return NGX_MAIL_PARSE_INVALID_COMMAND; ++ return NGX_MAIL_PARSE_INVALID_AUTH_MECH; + } + + if (s->salt.data == NULL) { +@@ -402,7 +542,7 @@ + + if (ngx_mail_auth_cram_md5_salt(s, c, "+ ", 2) == NGX_OK) { + s->mail_state = ngx_imap_auth_cram_md5; +- return NGX_OK; ++ return NGX_MAIL_AUTH_ARGUMENT; + } + + return NGX_ERROR; +@@ -475,3 +615,67 @@ + + return NGX_MAIL_PARSE_INVALID_COMMAND; + } ++ ++ ++static ngx_int_t ++ngx_mail_imap_id(ngx_mail_session_t *s, ngx_connection_t * c) ++{ ++ size_t i; ++ ngx_mail_imap_srv_conf_t *iscf; ++ ngx_str_t *key, * value; ++ ++ iscf = ngx_mail_get_module_srv_conf(s, ngx_mail_imap_module); ++ ngx_log_debug1 (NGX_LOG_DEBUG_MAIL,s->connection->log,0, ++ "imap id received %d parameters from client", s->args.nelts); ++ if (s->args.nelts) { ++ ngx_log_debug1 (NGX_LOG_DEBUG_MAIL, s->connection->log, 0, ++ "client ID params [%d pair(s)]", ++ s->args.nelts/2); ++ for (i = 0; i < s->args.nelts / 2; ++i) { ++ ngx_log_debug3 (NGX_LOG_DEBUG_MAIL, ++ s->connection->log, 0, ++ "[pair %d] field:'%V' value:'%V'", ++ i+1, ++ (ngx_str_t*)s->args.elts + 2 * i, ++ (ngx_str_t*)s->args.elts + 2 * i + 1 ++ ); ++ key = (ngx_str_t*)s->args.elts + 2 * i; ++ /* bug 64978, add support to "name" and "version" field */ ++ if (key->len == sizeof ("name") - 1 && ++ (key->data[0] == 'N' || key->data[0] == 'n') && ++ (key->data[1] == 'A' || key->data[1] == 'a') && ++ (key->data[2] == 'M' || key->data[2] == 'm') && ++ (key->data[3] == 'E' || key->data[3] == 'e')) { ++ value = (ngx_str_t*)s->args.elts + 2 * i + 1; ++ ++ s->id_name.data = ngx_pnalloc(c->pool, value->len); ++ if (s->id_name.data == NULL) { ++ return NGX_ERROR; ++ } ++ ++ ngx_memcpy(s->id_name.data, value->data, value->len); ++ s->id_name.len = value->len; ++ } else if (key->len == sizeof ("version") - 1 && ++ (key->data[0] == 'V' || key->data[0] == 'v') && ++ (key->data[1] == 'E' || key->data[1] == 'e') && ++ (key->data[2] == 'R' || key->data[2] == 'r') && ++ (key->data[3] == 'S' || key->data[3] == 's') && ++ (key->data[4] == 'I' || key->data[4] == 'i') && ++ (key->data[5] == 'O' || key->data[5] == 'o') && ++ (key->data[6] == 'N' || key->data[6] == 'n')) { ++ value = (ngx_str_t*)s->args.elts + 2 * i + 1; ++ ++ s->id_version.data = ngx_pnalloc(c->pool, value->len); ++ if (s->id_version.data == NULL) { ++ return NGX_ERROR; ++ } ++ ++ ngx_memcpy(s->id_version.data, value->data, value->len); ++ s->id_version.len = value->len; ++ } ++ } ++ } ++ s->text = iscf->id; ++ ++ return NGX_OK; ++} diff --git a/thirdparty/nginx/patches/nginx_src_mail_ngx_mail_imap_module_c.patch b/thirdparty/nginx/patches/nginx_src_mail_ngx_mail_imap_module_c.patch new file mode 100644 index 000000000..8f83bbbee --- /dev/null +++ b/thirdparty/nginx/patches/nginx_src_mail_ngx_mail_imap_module_c.patch @@ -0,0 +1,420 @@ +--- nginx/src/mail/ngx_mail_imap_module.c 2023-03-07 16:26:44.721101600 +0530 ++++ nginx/src/mail/ngx_mail_imap_module.c 2023-09-14 18:47:12.632886600 +0530 +@@ -15,7 +15,9 @@ + static void *ngx_mail_imap_create_srv_conf(ngx_conf_t *cf); + static char *ngx_mail_imap_merge_srv_conf(ngx_conf_t *cf, void *parent, + void *child); ++static char *ngx_mail_imap_id (ngx_conf_t *cf, ngx_command_t *cmd, void *conf); + ++static ngx_str_t default_imap_greeting = ngx_string("* OK IMAP4rev1 proxy server ready"); + + static ngx_str_t ngx_mail_imap_default_capabilities[] = { + ngx_string("IMAP4"), +@@ -30,18 +32,20 @@ + { ngx_string("login"), NGX_MAIL_AUTH_LOGIN_ENABLED }, + { ngx_string("cram-md5"), NGX_MAIL_AUTH_CRAM_MD5_ENABLED }, + { ngx_string("external"), NGX_MAIL_AUTH_EXTERNAL_ENABLED }, ++ { ngx_string("gssapi"), NGX_MAIL_AUTH_GSSAPI_ENABLED }, + { ngx_null_string, 0 } + }; + + ++/* zimbra's merge_conf method doesn't use this + static ngx_str_t ngx_mail_imap_auth_methods_names[] = { + ngx_string("AUTH=PLAIN"), + ngx_string("AUTH=LOGIN"), +- ngx_null_string, /* APOP */ ++ ngx_null_string, // APOP + ngx_string("AUTH=CRAM-MD5"), + ngx_string("AUTH=EXTERNAL"), +- ngx_null_string /* NONE */ +-}; ++ ngx_null_string // NONE ++};*/ + + + static ngx_mail_protocol_t ngx_mail_imap_protocol = { +@@ -57,7 +61,8 @@ + + ngx_string("* BAD internal server error" CRLF), + ngx_string("* BYE SSL certificate error" CRLF), +- ngx_string("* BYE No required SSL certificate" CRLF) ++ ngx_string("* BYE No required SSL certificate" CRLF), ++ ngx_string("* BYE IMAP server terminating connection" CRLF) + }; + + +@@ -77,6 +82,13 @@ + offsetof(ngx_mail_imap_srv_conf_t, capabilities), + NULL }, + ++ { ngx_string("imap_id"), ++ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE, ++ ngx_mail_imap_id, ++ NGX_MAIL_SRV_CONF_OFFSET, ++ offsetof(ngx_mail_imap_srv_conf_t, id_params), ++ NULL }, ++ + { ngx_string("imap_auth"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE, + ngx_conf_set_bitmask_slot, +@@ -84,6 +96,20 @@ + offsetof(ngx_mail_imap_srv_conf_t, auth_methods), + &ngx_mail_imap_auth_methods }, + ++ { ngx_string("imap_literalauth"), ++ NGX_MAIL_MAIN_CONF|NGX_CONF_TAKE1, ++ ngx_conf_set_flag_slot, ++ NGX_MAIL_SRV_CONF_OFFSET, ++ offsetof(ngx_mail_imap_srv_conf_t, literalauth), ++ NULL }, ++ ++ { ngx_string("imap_greeting"), ++ NGX_MAIL_MAIN_CONF|NGX_CONF_TAKE1, ++ ngx_conf_set_str_slot, ++ NGX_MAIL_SRV_CONF_OFFSET, ++ offsetof(ngx_mail_imap_srv_conf_t, greeting), ++ NULL }, ++ + ngx_null_command + }; + +@@ -133,6 +159,17 @@ + return NULL; + } + ++ if (ngx_array_init(&iscf->id_params, cf->pool, 4, sizeof(ngx_str_t)) ++ != NGX_OK) ++ { ++ return NULL; ++ } ++ ++ iscf->literalauth = NGX_CONF_UNSET; ++ ++ ngx_str_null(&iscf->ua_name); ++ ngx_str_null(&iscf->ua_version); ++ + return iscf; + } + +@@ -143,20 +180,23 @@ + ngx_mail_imap_srv_conf_t *prev = parent; + ngx_mail_imap_srv_conf_t *conf = child; + +- u_char *p, *auth; +- size_t size; ++ u_char *p, *p1, *p2, *p3; ++ size_t size, s1, s2, s3; + ngx_str_t *c, *d; +- ngx_uint_t i, m; ++ ngx_uint_t i; + + ngx_conf_merge_size_value(conf->client_buffer_size, + prev->client_buffer_size, +- (size_t) ngx_pagesize); ++ (size_t) 4 * ngx_pagesize); + + ngx_conf_merge_bitmask_value(conf->auth_methods, + prev->auth_methods, + (NGX_CONF_BITMASK_SET + |NGX_MAIL_AUTH_PLAIN_ENABLED)); + ++ if (conf->id_params.nelts == 0) { ++ conf->id_params = prev->id_params; ++ } + + if (conf->capabilities.nelts == 0) { + conf->capabilities = prev->capabilities; +@@ -174,85 +214,245 @@ + } + } + +- size = sizeof("* CAPABILITY" CRLF) - 1; ++ s1 = sizeof("* CAPABILITY" CRLF) - 1; ++ s2 = s1; ++ s3 = s1; + + c = conf->capabilities.elts; + for (i = 0; i < conf->capabilities.nelts; i++) { +- size += 1 + c[i].len; ++ s1 += 1 + c[i].len; ++ s2 += 1 + c[i].len; ++ s3 += 1 + c[i].len; + } + +- for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0; +- m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED; +- m <<= 1, i++) +- { +- if (m & conf->auth_methods) { +- size += 1 + ngx_mail_imap_auth_methods_names[i].len; +- } ++ if (conf->auth_methods & NGX_MAIL_AUTH_LOGIN_ENABLED) { ++ s1 += sizeof (" AUTH=LOGIN") - 1; ++ s2 += sizeof (" AUTH=LOGIN") - 1; ++ } ++ if (conf->auth_methods & NGX_MAIL_AUTH_PLAIN_ENABLED) { ++ s1 += sizeof (" AUTH=PLAIN") - 1; ++ s2 += sizeof (" AUTH=PLAIN") - 1; ++ } ++ if (conf->auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED) { ++ s1 += sizeof (" AUTH=CRAM-MD5") - 1; ++ s2 += sizeof (" AUTH=CRAM-MD5") - 1; ++ } ++ if (conf->auth_methods & NGX_MAIL_AUTH_GSSAPI_ENABLED) { ++ s1 += sizeof (" AUTH=GSSAPI") - 1; ++ s2 += sizeof (" AUTH=GSSAPI") - 1; ++ s3 += sizeof (" AUTH=GSSAPI") - 1; + } + +- p = ngx_pnalloc(cf->pool, size); +- if (p == NULL) { ++ s2 += sizeof (" STARTTLS") - 1; ++ s3 += sizeof (" STARTTLS") - 1; ++ s3 += sizeof (" LOGINDISABLED") - 1; ++ ++ p1 = ngx_palloc(cf->pool, s1); ++ if (p1 == NULL) { ++ return NGX_CONF_ERROR; ++ } ++ p2 = ngx_palloc(cf->pool, s2); ++ if (p2 == NULL) { ++ return NGX_CONF_ERROR; ++ } ++ p3 = ngx_palloc(cf->pool, s3); ++ if (p3 == NULL) { + return NGX_CONF_ERROR; + } + +- conf->capability.len = size; +- conf->capability.data = p; ++ conf->capability.len = s1; ++ conf->capability.data = p1; + +- p = ngx_cpymem(p, "* CAPABILITY", sizeof("* CAPABILITY") - 1); ++ conf->starttls_capability.len = s2; ++ conf->starttls_capability.data = p2; + +- for (i = 0; i < conf->capabilities.nelts; i++) { +- *p++ = ' '; +- p = ngx_cpymem(p, c[i].data, c[i].len); +- } ++ conf->starttls_only_capability.len = s3; ++ conf->starttls_only_capability.data = p3; + +- auth = p; ++ p1 = ngx_cpymem(p1, "* CAPABILITY", sizeof("* CAPABILITY") - 1); ++ p2 = ngx_cpymem(p2, "* CAPABILITY", sizeof("* CAPABILITY") - 1); ++ p3 = ngx_cpymem(p3, "* CAPABILITY", sizeof("* CAPABILITY") - 1); + +- for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0; +- m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED; +- m <<= 1, i++) +- { +- if (m & conf->auth_methods) { +- *p++ = ' '; +- p = ngx_cpymem(p, ngx_mail_imap_auth_methods_names[i].data, +- ngx_mail_imap_auth_methods_names[i].len); +- } ++ c = conf->capabilities.elts; ++ for (i = 0; i < conf->capabilities.nelts; i++) { ++ *p1++ = ' '; ++ p1 = ngx_cpymem(p1,c[i].data,c[i].len); ++ *p2++ = ' '; ++ p2 = ngx_cpymem(p2,c[i].data,c[i].len); ++ *p3++ = ' '; ++ p3 = ngx_cpymem(p3,c[i].data,c[i].len); ++ } ++ ++ if (conf->auth_methods & NGX_MAIL_AUTH_LOGIN_ENABLED) { ++ p1 = ngx_cpymem(p1," AUTH=LOGIN", sizeof(" AUTH=LOGIN") - 1); ++ p2 = ngx_cpymem(p2," AUTH=LOGIN", sizeof(" AUTH=LOGIN") - 1); ++ } ++ if (conf->auth_methods & NGX_MAIL_AUTH_PLAIN_ENABLED) { ++ p1 = ngx_cpymem(p1," AUTH=PLAIN", sizeof(" AUTH=PLAIN") - 1); ++ p2 = ngx_cpymem(p2," AUTH=PLAIN", sizeof(" AUTH=PLAIN") - 1); ++ } ++ if (conf->auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED) { ++ p1 = ngx_cpymem(p1," AUTH=CRAM-MD5", sizeof(" AUTH=CRAM-MD5") - 1); ++ p2 = ngx_cpymem(p2," AUTH=CRAM-MD5", sizeof(" AUTH=CRAM-MD5") - 1); ++ } ++ if (conf->auth_methods & NGX_MAIL_AUTH_GSSAPI_ENABLED) { ++ p1 = ngx_cpymem(p1," AUTH=GSSAPI", sizeof(" AUTH=GSSAPI") - 1); ++ p2 = ngx_cpymem(p2," AUTH=GSSAPI", sizeof(" AUTH=GSSAPI") - 1); ++ p3 = ngx_cpymem(p3," AUTH=GSSAPI", sizeof(" AUTH=GSSAPI") - 1); ++ } ++ ++ p2 = ngx_cpymem(p2," STARTTLS",sizeof(" STARTTLS")-1); ++ p3 = ngx_cpymem(p3," STARTTLS",sizeof(" STARTTLS")-1); ++ p3 = ngx_cpymem(p3," LOGINDISABLED",sizeof(" LOGINDISABLED")-1); ++ ++ *p1++ = CR; *p1++ = LF; ++ *p2++ = CR; *p2++ = LF; ++ *p3++ = CR; *p3++ = LF; ++ ++ ngx_conf_merge_str_value(conf->greeting, prev->greeting, ""); ++ if (conf->greeting.len == 0) { ++ conf->greeting = default_imap_greeting; ++ } ++ ++ p = ngx_pnalloc(cf->pool, conf->greeting.len + 2); ++ if (p == NULL) { ++ return NGX_CONF_ERROR; + } + +- *p++ = CR; *p = LF; ++ ngx_memcpy(p, conf->greeting.data, conf->greeting.len); ++ ngx_memcpy(p + conf->greeting.len, CRLF, sizeof(CRLF)-1); ++ conf->greeting.data = p; ++ conf->greeting.len += 2; ++ ++ size = sizeof ("* ID ()" CRLF) - 1; ++ ++ c = conf->id_params.elts; ++ for (i = 0; i < conf->id_params.nelts; ++i) { ++ if (!((c[i].len == 3) && ++ (c[i].data[0] == 'n' || c[i].data[0] == 'N') && ++ (c[i].data[1] == 'i' || c[i].data[1] == 'I') && ++ (c[i].data[2] == 'l' || c[i].data[2] == 'L')) ++ ) ++ { ++ size += 2; // for enclosing quotes ++ } + ++ size += c[i].len; ++ size += 1; // for following SP ++ } + +- size += sizeof(" STARTTLS") - 1; ++ if (conf->id_params.nelts > 0) { ++ --size; // no SP follows the last parameter ++ } else { ++ size = size - 2 + 3; // take away the () and put nil ++ } + + p = ngx_pnalloc(cf->pool, size); + if (p == NULL) { + return NGX_CONF_ERROR; + } + +- conf->starttls_capability.len = size; +- conf->starttls_capability.data = p; ++ conf->id.len = size; ++ conf->id.data = p; ++ ++ p = ngx_cpymem (p, "* ID ", sizeof ("* ID ") -1); ++ ++ if (conf->id_params.nelts > 0) ++ { ++ *p++ = '('; ++ ++ for (i = 0; i < conf->id_params.nelts; ++i) ++ { ++ if (!((c[i].len == 3) && ++ (c[i].data[0] == 'n' || c[i].data[0] == 'N') && ++ (c[i].data[1] == 'i' || c[i].data[1] == 'I') && ++ (c[i].data[2] == 'l' || c[i].data[2] == 'L')) ++ ) ++ { ++ *p++ = '"'; ++ p = ngx_cpymem(p, c[i].data, c[i].len); ++ *p++ = '"'; ++ } ++ else ++ { ++ p = ngx_cpymem(p, c[i].data, c[i].len); ++ } ++ ++ if (i < conf->id_params.nelts - 1) ++ *p++ = ' '; ++ } ++ ++ *p++ = ')'; ++ } ++ else ++ { ++ p = ngx_cpymem (p, "nil", sizeof("nil") - 1); ++ } + +- p = ngx_cpymem(p, conf->capability.data, +- conf->capability.len - (sizeof(CRLF) - 1)); +- p = ngx_cpymem(p, " STARTTLS", sizeof(" STARTTLS") - 1); + *p++ = CR; *p = LF; ++ ngx_conf_merge_value (conf->literalauth, prev->literalauth,1); ++ ngx_conf_merge_str_value (conf->ua_name, prev->ua_name, "ZCS"); ++ ngx_conf_merge_str_value (conf->ua_version, prev->ua_version, "Unknown Version"); ++ ++ return NGX_CONF_OK; ++} + ++static char * ++ngx_mail_imap_id (ngx_conf_t *cf, ngx_command_t *cmd, void *conf) ++{ ++ char *p = conf; ++ ngx_mail_imap_srv_conf_t *iscf; ++ ngx_str_t *c, *value, *elt; ++ ngx_uint_t i; ++ ngx_array_t *a; + +- size = (auth - conf->capability.data) + sizeof(CRLF) - 1 +- + sizeof(" STARTTLS LOGINDISABLED") - 1; ++ iscf = (ngx_mail_imap_srv_conf_t *)conf; ++ ++ value = cf->args->elts; ++ ++ if (cf->args->nelts % 2 == 0) ++ { ++ // ID response must contain id param field-value pairs ++ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, ++ "unmatched pair in IMAP ID string: %V", ++ value + cf->args->nelts - 1); + +- p = ngx_pnalloc(cf->pool, size); +- if (p == NULL) { + return NGX_CONF_ERROR; + } ++ else ++ { ++ a = (ngx_array_t *) (p + cmd->offset); ++ for (i = 1; i < cf->args->nelts; ++i) ++ { ++ c = ngx_array_push (a); ++ if (c == NULL) { ++ return NGX_CONF_ERROR; ++ } + +- conf->starttls_only_capability.len = size; +- conf->starttls_only_capability.data = p; ++ *c = value[i]; ++ } + +- p = ngx_cpymem(p, conf->capability.data, +- auth - conf->capability.data); +- p = ngx_cpymem(p, " STARTTLS LOGINDISABLED", +- sizeof(" STARTTLS LOGINDISABLED") - 1); +- *p++ = CR; *p = LF; ++ for (i = 0; i < a->nelts; i += 2 ) { ++ elt = ((ngx_str_t *) a->elts + i); ++ if (elt->len == sizeof ("NAME") - 1 && ++ (elt->data[0] == 'N' || elt->data[0] == 'n') && ++ (elt->data[1] == 'A' || elt->data[1] == 'a') && ++ (elt->data[2] == 'M' || elt->data[2] == 'm') && ++ (elt->data[3] == 'E' || elt->data[0] == 'e')) { ++ iscf->ua_name = *(elt + 1); ++ }else if (elt->len == sizeof ("VERSION") - 1 && ++ (elt->data[0] == 'V' || elt->data[0] == 'v') && ++ (elt->data[1] == 'E' || elt->data[1] == 'e') && ++ (elt->data[2] == 'R' || elt->data[2] == 'r') && ++ (elt->data[3] == 'S' || elt->data[3] == 's') && ++ (elt->data[4] == 'I' || elt->data[4] == 'i') && ++ (elt->data[5] == 'O' || elt->data[5] == 'o') && ++ (elt->data[6] == 'N' || elt->data[6] == 'n')) { ++ iscf->ua_version = *(elt + 1); ++ } ++ } + +- return NGX_CONF_OK; ++ return NGX_CONF_OK; ++ } + } ++ diff --git a/thirdparty/nginx/patches/nginx_src_mail_ngx_mail_imap_module_h.patch b/thirdparty/nginx/patches/nginx_src_mail_ngx_mail_imap_module_h.patch new file mode 100644 index 000000000..49e037c26 --- /dev/null +++ b/thirdparty/nginx/patches/nginx_src_mail_ngx_mail_imap_module_h.patch @@ -0,0 +1,18 @@ +--- nginx/src/mail/ngx_mail_imap_module.h 2023-03-07 16:26:44.722000700 +0530 ++++ nginx/src/mail/ngx_mail_imap_module.h 2023-03-04 13:25:59.938112400 +0530 +@@ -24,6 +24,15 @@ + ngx_uint_t auth_methods; + + ngx_array_t capabilities; ++ ++ ngx_array_t id_params; ++ ngx_str_t id; ++ ++ ngx_str_t ua_name; /* user agent name coming from ID field "NAME" */ ++ ngx_str_t ua_version; /* user agent version coming from ID field "VERSION" */ ++ ++ ngx_flag_t literalauth; ++ ngx_str_t greeting; + } ngx_mail_imap_srv_conf_t; + + diff --git a/thirdparty/nginx/patches/nginx_src_mail_ngx_mail_parse.patch b/thirdparty/nginx/patches/nginx_src_mail_ngx_mail_parse.patch new file mode 100644 index 000000000..b006cd76e --- /dev/null +++ b/thirdparty/nginx/patches/nginx_src_mail_ngx_mail_parse.patch @@ -0,0 +1,1342 @@ +--- nginx/src/mail/ngx_mail_parse.c 2023-10-03 16:49:31.548168900 +0530 ++++ nginx/src/mail/ngx_mail_parse.c 2023-10-09 19:09:23.434448700 +0530 +@@ -14,21 +14,104 @@ + #include + + ++static ngx_int_t ngx_mail_imap_parse_id_params(ngx_mail_session_t *s, u_char *p); ++ ++typedef enum { ++ swi_start = 0, ++ swi_tag, ++ swi_invalid, ++ swi_spaces_before_command, ++ swi_command, ++ swi_spaces_before_argument, ++ swi_argument, ++ swi_backslash, ++ swi_literal, ++ swi_no_sync_literal_argument, ++ swi_start_literal_argument, ++ swi_literal_argument, ++ swi_end_literal_argument, ++ swi_almost_done, ++ swi_begin_idparams = 15, ++ swi_end_idparams, ++ swi_done_idparams, ++ swi_almost_done_idparams, ++ swi_begin_idfield, ++ swi_id_n, ++ swi_id_ni, ++ swi_id_nil, ++ swi_idfield, ++ swi_idfield_len, ++ swi_idfield_len_plus, ++ swi_begin_idfield_l, ++ swi_idfield_l, ++ swi_SP_before_idvalue, ++ swi_X_before_idfield, ++ swi_begin_idvalue, ++ swi_idvalue, ++ swi_idvalue_n, ++ swi_idvalue_ni, ++ swi_idvalue_nil, ++ swi_idvalue_len, ++ swi_idvalue_len_plus, ++ swi_begin_idvalue_l, ++ swi_idvalue_l, ++} ngx_imap_parse_state_e; ++ ++typedef enum { ++ swp_start = 0, ++ swp_command, ++ swp_invalid, ++ swp_spaces_before_argument, ++ swp_argument, ++ swp_almost_done ++} ngx_pop3_parse_state_e; ++ ++typedef enum { ++ sws_start = 0, ++ sws_command, ++ sws_spaces_before_argument, ++ sws_argument, ++ sws_almost_done, ++ sws_invalid ++} ngx_smtp_parse_state_e; ++ ++inline void ngx_mail_set_imap_parse_state_start(ngx_mail_session_t * s) { ++ s->state = swi_start; ++} ++ ++inline void ngx_mail_set_pop3_parse_state_start(ngx_mail_session_t * s) { ++ s->state = swp_start; ++} ++ ++inline void ngx_mail_set_smtp_parse_state_start(ngx_mail_session_t * s) { ++ s->state = sws_start; ++} ++ ++inline void ngx_mail_set_imap_parse_state_argument(ngx_mail_session_t * s) { ++ s->state = swi_argument; ++} ++ ++inline void ngx_mail_set_pop3_parse_state_argument(ngx_mail_session_t * s) { ++ s->state = swp_argument; ++} ++ ++inline void ngx_mail_set_smtp_parse_state_argument(ngx_mail_session_t * s) { ++ s->state = sws_argument; ++} ++ ++inline void ngx_mail_reset_parse_buffer(ngx_mail_session_t * s) { ++ s->buffer->pos = s->buffer->start; ++ s->buffer->last = s->buffer->start; ++} ++ ++ + ngx_int_t + ngx_mail_pop3_parse_command(ngx_mail_session_t *s) + { + u_char ch, *p, *c, c0, c1, c2, c3; + ngx_str_t *arg; +- enum { +- sw_start = 0, +- sw_command, +- sw_invalid, +- sw_spaces_before_argument, +- sw_argument, +- sw_almost_done +- } state; + +- state = s->state; ++ ngx_pop3_parse_state_e state = s->state; + + for (p = s->buffer->pos; p < s->buffer->last; p++) { + ch = *p; +@@ -36,13 +119,13 @@ + switch (state) { + + /* POP3 command */ +- case sw_start: ++ case swp_start: + s->cmd_start = p; +- state = sw_command; ++ state = swp_command; + + /* fall through */ + +- case sw_command: ++ case swp_command: + if (ch == ' ' || ch == CR || ch == LF) { + c = s->cmd_start; + +@@ -98,10 +181,10 @@ + + switch (ch) { + case ' ': +- state = sw_spaces_before_argument; ++ state = swp_spaces_before_argument; + break; + case CR: +- state = sw_almost_done; ++ state = swp_almost_done; + break; + case LF: + goto done; +@@ -115,21 +198,21 @@ + + break; + +- case sw_invalid: ++ case swp_invalid: + goto invalid; + +- case sw_spaces_before_argument: ++ case swp_spaces_before_argument: + switch (ch) { + case ' ': + break; + case CR: +- state = sw_almost_done; ++ state = swp_almost_done; + break; + case LF: + goto done; + default: + if (s->args.nelts <= 2) { +- state = sw_argument; ++ state = swp_argument; + s->arg_start = p; + break; + } +@@ -137,7 +220,7 @@ + } + break; + +- case sw_argument: ++ case swp_argument: + switch (ch) { + + case ' ': +@@ -167,10 +250,10 @@ + + switch (ch) { + case ' ': +- state = sw_spaces_before_argument; ++ state = swp_spaces_before_argument; + break; + case CR: +- state = sw_almost_done; ++ state = swp_almost_done; + break; + case LF: + goto done; +@@ -182,7 +265,7 @@ + } + break; + +- case sw_almost_done: ++ case swp_almost_done: + switch (ch) { + case LF: + goto done; +@@ -200,22 +283,20 @@ + done: + + s->buffer->pos = p + 1; +- s->state = (s->command != NGX_POP3_AUTH) ? sw_start : sw_argument; +- + return NGX_OK; + + invalid: + +- s->state = sw_invalid; ++ s->state = swp_invalid; + + /* skip invalid command till LF */ + + for ( /* void */ ; p < s->buffer->last; p++) { + if (*p == LF) { +- s->state = sw_start; ++ s->state = swp_start; + s->buffer->pos = p + 1; +- return NGX_MAIL_PARSE_INVALID_COMMAND; +- } ++ return NGX_MAIL_PARSE_INVALID_COMMAND; ++} + } + + s->buffer->pos = p; +@@ -228,25 +309,22 @@ + ngx_mail_imap_parse_command(ngx_mail_session_t *s) + { + u_char ch, *p, *c, *dst, *src, *end; ++ ngx_int_t rc; + ngx_str_t *arg; +- enum { +- sw_start = 0, +- sw_tag, +- sw_invalid, +- sw_spaces_before_command, +- sw_command, +- sw_spaces_before_argument, +- sw_argument, +- sw_backslash, +- sw_literal, +- sw_no_sync_literal_argument, +- sw_start_literal_argument, +- sw_literal_argument, +- sw_end_literal_argument, +- sw_almost_done +- } state; ++ p = NULL; /* for avoid warning */ ++ ngx_imap_parse_state_e state = s->state; + +- state = s->state; ++ if(state >= swi_begin_idparams) { /* beyond the states handled in ID parse function */ ++ rc = ngx_mail_imap_parse_id_params(s, s->buffer->pos); ++ switch (rc) { ++ case NGX_OK: ++ goto done; ++ case NGX_MAIL_PARSE_INVALID_COMMAND: ++ goto invalid; ++ default: /* NGX_ERROR, NGX_AGAIN or NGX_IMAP_NEXT */ ++ return rc; ++ } ++ } + + for (p = s->buffer->pos; p < s->buffer->last; p++) { + ch = *p; +@@ -254,21 +332,22 @@ + switch (state) { + + /* IMAP tag */ +- case sw_start: ++ case swi_start: + s->tag_start = p; +- state = sw_tag; ++ state = swi_tag; + + /* fall through */ + +- case sw_tag: ++ case swi_tag: + switch (ch) { + case ' ': +- s->tag.len = p - s->tag_start + 1; ++ s->tag.len = p - s->tag_start; + s->tag.data = s->tag_start; +- state = sw_spaces_before_command; ++ state = swi_spaces_before_command; + break; + case CR: + case LF: ++ case '\x0': + goto invalid; + default: + if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z') +@@ -279,35 +358,59 @@ + } + if (p - s->tag_start > 31) { + goto invalid; +- } +- break; ++ } ++ break; + } + break; + +- case sw_invalid: ++ case swi_invalid: + goto invalid; + +- case sw_spaces_before_command: ++ case swi_spaces_before_command: + switch (ch) { + case ' ': + break; + case CR: + case LF: ++ case '\x0': + goto invalid; + default: + s->cmd_start = p; +- state = sw_command; ++ state = swi_command; + break; + } + break; + +- case sw_command: ++ case swi_command: + if (ch == ' ' || ch == CR || ch == LF) { + + c = s->cmd_start; + + switch (p - c) { + ++ case 2: ++ if ((c[0] == 'I' || c[0] == 'i') ++ && (c[1] == 'D' || c[1] == 'd')) ++ { ++ s->command = NGX_IMAP_ID; ++ /* RFC 2971 */ ++ ngx_int_t rc; ++ s->state = swi_begin_idparams; ++ p++; //move to the next character ++ rc = ngx_mail_imap_parse_id_params(s, p); ++ switch (rc) { ++ case NGX_OK: ++ goto done; ++ case NGX_MAIL_PARSE_INVALID_COMMAND: ++ goto invalid; ++ default: /* NGX_ERROR or NGX_IMAP_NEXT */ ++ return rc; ++ } ++ } else { ++ goto invalid; ++ } ++ break; ++ + case 4: + if ((c[0] == 'N' || c[0] == 'n') + && (c[1] == 'O'|| c[1] == 'o') +@@ -315,7 +418,7 @@ + && (c[3] == 'P'|| c[3] == 'p')) + { + s->command = NGX_IMAP_NOOP; +- ++ s->eargs = 0; + } else { + goto invalid; + } +@@ -329,7 +432,7 @@ + && (c[4] == 'N'|| c[4] == 'n')) + { + s->command = NGX_IMAP_LOGIN; +- ++ s->eargs = 2; + } else { + goto invalid; + } +@@ -344,7 +447,7 @@ + && (c[5] == 'T'|| c[5] == 't')) + { + s->command = NGX_IMAP_LOGOUT; +- ++ s->eargs = 0; + } else { + goto invalid; + } +@@ -362,7 +465,7 @@ + && (c[7] == 'S'|| c[7] == 's')) + { + s->command = NGX_IMAP_STARTTLS; +- ++ s->eargs = 0; + } else { + goto invalid; + } +@@ -382,7 +485,7 @@ + && (c[9] == 'Y'|| c[9] == 'y')) + { + s->command = NGX_IMAP_CAPABILITY; +- ++ s->eargs = 0; + } else { + goto invalid; + } +@@ -402,7 +505,12 @@ + && (c[10] == 'T'|| c[10] == 't') + && (c[11] == 'E'|| c[11] == 'e')) + { +- s->command = NGX_IMAP_AUTHENTICATE; ++ if (ch != ' ') { ++ goto invalid; ++ } else { ++ s->command = NGX_IMAP_AUTHENTICATE; ++ s->eargs = 1; ++ } + + } else { + goto invalid; +@@ -418,10 +526,13 @@ + + switch (ch) { + case ' ': +- state = sw_spaces_before_argument; ++ if (s->command == NGX_IMAP_CAPABILITY) { ++ goto invalid; ++ } ++ state = swi_spaces_before_argument; + break; + case CR: +- state = sw_almost_done; ++ state = swi_almost_done; + break; + case LF: + goto done; +@@ -435,40 +546,46 @@ + + break; + +- case sw_spaces_before_argument: ++ case swi_spaces_before_argument: + switch (ch) { + case ' ': + break; + case CR: +- state = sw_almost_done; ++ if (s->args.nelts == 0) { ++ goto invalid; //no argument ++ } ++ state = swi_almost_done; + break; + case LF: ++ if ( s->args.nelts == 0) { ++ goto invalid; // no argument ++ } + goto done; + case '"': +- if (s->args.nelts <= 2) { ++ if (s->args.nelts <= s->eargs) { + s->quoted = 1; + s->arg_start = p + 1; +- state = sw_argument; ++ state = swi_argument; + break; + } + goto invalid; + case '{': +- if (s->args.nelts <= 2) { +- state = sw_literal; ++ if (s->args.nelts <= s->eargs) { ++ state = swi_literal; + break; + } + goto invalid; + default: +- if (s->args.nelts <= 2) { ++ if (s->args.nelts <= s->eargs) { + s->arg_start = p; +- state = sw_argument; ++ state = swi_argument; + break; + } + goto invalid; + } + break; + +- case sw_argument: ++ case swi_argument: + if (ch == ' ' && s->quoted) { + break; + } +@@ -510,10 +627,10 @@ + switch (ch) { + case '"': + case ' ': +- state = sw_spaces_before_argument; ++ state = swi_spaces_before_argument; + break; + case CR: +- state = sw_almost_done; ++ state = swi_almost_done; + break; + case LF: + goto done; +@@ -522,46 +639,52 @@ + case '\\': + if (s->quoted) { + s->backslash = 1; +- state = sw_backslash; ++ state = swi_backslash; + } + break; + } + break; + +- case sw_backslash: ++ case swi_backslash: + switch (ch) { + case CR: + case LF: + goto invalid; ++ // (RFC3501) ++ // a backslash may only escape another backslash, or a double quote ++ case '\\': ++ case '"': ++ state = swi_argument; ++ break; + default: +- state = sw_argument; ++ goto invalid; + } + break; + +- case sw_literal: ++ case swi_literal: + if (ch >= '0' && ch <= '9') { + s->literal_len = s->literal_len * 10 + (ch - '0'); + break; + } + if (ch == '}') { +- state = sw_start_literal_argument; ++ state = swi_start_literal_argument; + break; + } + if (ch == '+') { +- state = sw_no_sync_literal_argument; ++ state = swi_no_sync_literal_argument; + break; + } + goto invalid; + +- case sw_no_sync_literal_argument: ++ case swi_no_sync_literal_argument: + if (ch == '}') { + s->no_sync_literal = 1; +- state = sw_start_literal_argument; ++ state = swi_start_literal_argument; + break; + } + goto invalid; + +- case sw_start_literal_argument: ++ case swi_start_literal_argument: + switch (ch) { + case CR: + break; +@@ -569,10 +692,10 @@ + s->buffer->pos = p + 1; + s->arg_start = p + 1; + if (s->no_sync_literal == 0) { +- s->state = sw_literal_argument; ++ s->state = swi_literal_argument; + return NGX_IMAP_NEXT; + } +- state = sw_literal_argument; ++ state = swi_literal_argument; + s->no_sync_literal = 0; + break; + default: +@@ -580,7 +703,7 @@ + } + break; + +- case sw_literal_argument: ++ case swi_literal_argument: + if (s->literal_len && --s->literal_len) { + break; + } +@@ -592,36 +715,38 @@ + arg->len = p + 1 - s->arg_start; + arg->data = s->arg_start; + s->arg_start = NULL; +- state = sw_end_literal_argument; ++ state = swi_end_literal_argument; + + break; + +- case sw_end_literal_argument: ++ case swi_end_literal_argument: + switch (ch) { + case '{': +- if (s->args.nelts <= 2) { +- state = sw_literal; ++ if (s->args.nelts <= s->eargs) { ++ state = swi_literal; + break; + } + goto invalid; + case CR: +- state = sw_almost_done; ++ state = swi_almost_done; + break; + case LF: + goto done; + default: +- state = sw_spaces_before_argument; ++ state = swi_spaces_before_argument; + break; + } + break; + +- case sw_almost_done: ++ case swi_almost_done: + switch (ch) { + case LF: + goto done; + default: + goto invalid; + } ++ default: ++ break; /* for avoid warning only */ + } + } + +@@ -633,13 +758,11 @@ + done: + + s->buffer->pos = p + 1; +- s->state = (s->command != NGX_IMAP_AUTHENTICATE) ? sw_start : sw_argument; +- + return NGX_OK; + + invalid: + +- s->state = sw_invalid; ++ s->state = swi_invalid; + s->quoted = 0; + s->backslash = 0; + s->no_sync_literal = 0; +@@ -649,7 +772,7 @@ + + for ( /* void */ ; p < s->buffer->last; p++) { + if (*p == LF) { +- s->state = sw_start; ++ s->state = swi_start; + s->buffer->pos = p + 1; + + /* detect non-synchronizing literals */ +@@ -676,21 +799,500 @@ + } + + ++static ngx_int_t ++ngx_mail_imap_parse_id_params(ngx_mail_session_t *s, u_char *p) ++{ ++ u_char ch; ++ ngx_imap_parse_state_e state; ++ ngx_str_t *arg; ++ state = s->state; ++ ++ for (; p < s->buffer->last; p++) { ++ ch = *p; ++ switch(state) { ++ case swi_begin_idparams: ++ switch (ch) { ++ case '(': ++ state = swi_begin_idfield; ++ break; ++ case 'n': ++ case 'N': ++ state = swi_id_n; ++ break; ++ default: ++ ngx_log_debug1 (NGX_LOG_DEBUG_MAIL, s->connection->log, 0, ++ "swi_begin_idparams: expected (/n/N, got '%c'", ch); ++ return NGX_MAIL_PARSE_INVALID_COMMAND; ++ } ++ break; ++ ++ case swi_end_idparams: ++ switch (ch) ++ { ++ case ')': ++ state = swi_done_idparams; ++ break; ++ default: ++ ngx_log_debug1 (NGX_LOG_DEBUG_MAIL, s->connection->log, 0, ++ "swi_end_idparams: expected ), got '%c'", ch); ++ return NGX_MAIL_PARSE_INVALID_COMMAND; ++ } ++ ++ break; ++ ++ case swi_done_idparams: ++ switch (ch) ++ { ++ case CR: ++ state = swi_almost_done; ++ break; ++ case LF: ++ return NGX_OK; ++ default: ++ ngx_log_debug1 (NGX_LOG_DEBUG_MAIL, s->connection->log, 0, ++ "swi_done_idparams: expected CR/LF, got '%c'", ch); ++ return NGX_MAIL_PARSE_INVALID_COMMAND; ++ } ++ break; ++ ++ case swi_id_n: ++ switch (ch) { ++ case 'i': ++ case 'I': ++ state = swi_id_ni; ++ break; ++ default: ++ ngx_log_debug1 (NGX_LOG_DEBUG_MAIL, s->connection->log, 0, ++ "swi_id_n: expected i/I, got '%c'", ch); ++ return NGX_MAIL_PARSE_INVALID_COMMAND; ++ } ++ break; ++ ++ case swi_id_ni: ++ switch (ch) { ++ case 'l': ++ case 'L': ++ state = swi_id_nil; ++ break; ++ default: ++ ngx_log_debug1 (NGX_LOG_DEBUG_MAIL, s->connection->log, 0, ++ "swi_id_ni: expected l/L, got '%c'", ch); ++ return NGX_MAIL_PARSE_INVALID_COMMAND; ++ } ++ break; ++ ++ case swi_id_nil: ++ switch (ch) { ++ case CR: ++ state = swi_almost_done; ++ break; ++ case LF: ++ return NGX_OK; ++ default: ++ return NGX_MAIL_PARSE_INVALID_COMMAND; ++ } ++ break; ++ ++ case swi_begin_idfield: ++ switch (ch) { ++ case '{': ++ s->literal_len = 0; ++ state = swi_idfield_len; ++ break; ++ case '"': ++ s->quoted = 1; ++ s->backslash = 0; ++ s->arg_start = p+1; ++ state = swi_idfield; ++ break; ++ default: ++ ngx_log_debug1 (NGX_LOG_DEBUG_MAIL, s->connection->log, 0, ++ "swi_begin_idfield: expected \"/{, got '%c'", ch); ++ return NGX_MAIL_PARSE_INVALID_COMMAND; ++ } ++ break; ++ ++ case swi_idfield_len: ++ if (ch >= '0' && ch <= '9') { ++ s->literal_len = s->literal_len * 10 + (ch - '0'); ++ break; ++ } ++ if (ch == '+') { ++ state = swi_idfield_len_plus; /* literalplus stuff */ ++ break; ++ } ++ if (ch == '}') { ++ s->no_sync_literal = 0; ++ state = swi_begin_idfield_l; ++ break; ++ } ++ ngx_log_debug1 (NGX_LOG_DEBUG_MAIL, s->connection->log, 0, ++ "swi_idfield_len: expected 0-9/+/}, got '%c'", ch); ++ return NGX_MAIL_PARSE_INVALID_COMMAND; ++ ++ case swi_idfield_len_plus: ++ if (ch == '}') { ++ s->no_sync_literal = 1; ++ state = swi_begin_idfield_l; ++ break; ++ } ++ ngx_log_debug1 (NGX_LOG_DEBUG_MAIL, s->connection->log, 0, ++ "swi_idfield_len_plus: expected }, got '%c'", ch); ++ return NGX_MAIL_PARSE_INVALID_COMMAND; ++ ++ case swi_begin_idfield_l: ++ switch (ch) ++ { ++ case CR: ++ break; ++ case LF: ++ if (s->literal_len) { ++ s->buffer->pos = p + 1; ++ s->arg_start = p + 1; ++ state = swi_idfield_l; ++ } else { ++ s->buffer->pos = p + 1; ++ s->arg_start = NULL; ++ arg = ngx_array_push (&s->args); ++ if (arg == NULL) { return NGX_ERROR; } ++ arg->data = (u_char *)""; ++ arg->len = 0; ++ state = swi_SP_before_idvalue; ++ } ++ if (s->no_sync_literal == 1) { ++ s->no_sync_literal = 0; ++ break; ++ } else { ++ s->state = state; ++ return NGX_IMAP_NEXT; ++ } ++ default: ++ ngx_log_debug1 (NGX_LOG_DEBUG_MAIL, s->connection->log, 0, ++ "swi_begin_idfield_l: expected CR/LF, got '%c'", ch); ++ return NGX_MAIL_PARSE_INVALID_COMMAND; ++ } ++ break; ++ ++ case swi_idfield_l: ++ if (s->literal_len && --s->literal_len) { ++ break; ++ } ++ ++ arg = ngx_array_push (&s->args); ++ if (arg == NULL) { ++ return NGX_ERROR; ++ } ++ ++ arg->len = p + 1 - s->arg_start; ++ arg->data = s->arg_start; ++ s->arg_start = NULL; ++ state = swi_SP_before_idvalue; ++ break; ++ ++ case swi_idfield: ++ switch (ch) { ++ case '\\': ++ if (!s->backslash) { ++ s->backslash = 1; ++ } else { ++ if (ch == '\\' && ch == '"') ++ s->backslash = 0; ++ else { ++ ngx_log_debug1 (NGX_LOG_DEBUG_MAIL, ++ s->connection->log, 0, ++ "swi_idfield: \\ escapes non-quoted special '%c'", ++ ch); ++ return NGX_MAIL_PARSE_INVALID_COMMAND; ++ } ++ } ++ break; ++ ++ case '"': ++ if (s->backslash) { ++ s->backslash = 0; ++ break; ++ } ++ s->quoted = 0; ++ arg = ngx_array_push(&s->args); ++ if (arg == NULL) { ++ return NGX_ERROR; ++ } ++ ++ arg->len = p - s->arg_start; ++ arg->data = s->arg_start; ++ s->arg_start = NULL; ++ state = swi_SP_before_idvalue; ++ break; ++ ++ case CR: ++ case LF: ++ ngx_log_debug0 (NGX_LOG_DEBUG_MAIL, s->connection->log, 0, ++ "swi_idfield: CR/LF breaks id field"); ++ return NGX_MAIL_PARSE_INVALID_COMMAND; ++ default: ++ break; ++ } ++ break; ++ ++ case swi_begin_idvalue: ++ switch (ch) ++ { ++ case '"': ++ s->quoted = 1; ++ s->backslash = 0; ++ s->arg_start = p+1; ++ state = swi_idvalue; ++ break; ++ case 'n': ++ case 'N': ++ state = swi_idvalue_n; ++ break; ++ case '{': ++ s->literal_len = 0; ++ state = swi_idvalue_len; ++ break; ++ default: ++ ngx_log_debug1 (NGX_LOG_DEBUG_MAIL, s->connection->log, 0, ++ "swi_begin_idvalue: expected \"/n/N/{, got '%c'", ch); ++ return NGX_MAIL_PARSE_INVALID_COMMAND; ++ } ++ break; ++ ++ case swi_idvalue_len: ++ if (ch >= '0' && ch <= '9') { ++ s->literal_len = s->literal_len + (ch - '0'); ++ break; ++ } ++ if (ch == '+') { ++ state = swi_idvalue_len_plus; ++ break; ++ } ++ if (ch == '}') { ++ s->no_sync_literal = 0; ++ state = swi_begin_idvalue_l; ++ break; ++ } ++ ngx_log_debug1 (NGX_LOG_DEBUG_MAIL, s->connection->log, 0, ++ "swi_idvalue_len: expected 0-9/}, got '%c'", ch); ++ return NGX_MAIL_PARSE_INVALID_COMMAND; ++ ++ case swi_idvalue_len_plus: ++ if (ch == '}') { ++ s->no_sync_literal = 1; ++ state = swi_begin_idvalue_l; ++ break; ++ } ++ ngx_log_debug1 (NGX_LOG_DEBUG_MAIL, s->connection->log, 0, ++ "swi_idvalue_len_plus: expected }, got '%c'", ch); ++ return NGX_MAIL_PARSE_INVALID_COMMAND; ++ ++ case swi_begin_idvalue_l: ++ switch (ch) ++ { ++ case CR: ++ break; ++ case LF: ++ if (s->literal_len) { ++ s->buffer->pos = p + 1; ++ s->arg_start = p + 1; ++ state = swi_idvalue_l; ++ } else { ++ s->buffer->pos = p + 1; ++ s->arg_start = NULL; ++ arg = ngx_array_push (&s->args); ++ if (arg == NULL) { return NGX_ERROR; } ++ arg->data = (u_char *)""; ++ arg->len = 0; ++ state = swi_X_before_idfield; ++ } ++ if (s->no_sync_literal == 1) { ++ s->no_sync_literal = 0; ++ break; ++ } else { ++ s->state = state; ++ return NGX_IMAP_NEXT; ++ } ++ default: ++ ngx_log_debug1 (NGX_LOG_DEBUG_MAIL, s->connection->log, 0, ++ "swi_begin_idvalue_l: expected CR/LF, got '%c'", ch); ++ return NGX_MAIL_PARSE_INVALID_COMMAND; ++ } ++ break; ++ ++ case swi_idvalue_l: ++ if (s->literal_len && --s->literal_len) { ++ break; ++ } ++ ++ arg = ngx_array_push (&s->args); ++ if (arg == NULL) { ++ return NGX_ERROR; ++ } ++ ++ arg->len = p + 1 - s->arg_start; ++ arg->data = s->arg_start; ++ s->arg_start = NULL; ++ state = swi_X_before_idfield; ++ break; ++ ++ case swi_idvalue_n: ++ switch (ch) ++ { ++ case 'i': ++ case 'I': ++ state = swi_idvalue_ni; ++ break; ++ default: ++ ngx_log_debug1 (NGX_LOG_DEBUG_MAIL, s->connection->log, 0, ++ "swi_idvalue_n: expected i/I, got '%c'", ch); ++ return NGX_MAIL_PARSE_INVALID_COMMAND; ++ } ++ break; ++ ++ case swi_idvalue_ni: ++ switch (ch) ++ { ++ case 'l': ++ case 'L': ++ state = swi_idvalue_nil; ++ break; ++ default: ++ ngx_log_debug1 (NGX_LOG_DEBUG_MAIL, s->connection->log, 0, ++ "swi_idvalue_ni: expected l/L, got '%c'", ch); ++ return NGX_MAIL_PARSE_INVALID_COMMAND; ++ } ++ break; ++ ++ case swi_idvalue_nil: ++ switch (ch) ++ { ++ case ' ': ++ state = swi_begin_idfield; ++ arg = ngx_array_push (&s->args); ++ if (arg == NULL) { ++ return NGX_ERROR; ++ } ++ arg->data = (u_char *)""; ++ arg->len = 0; ++ break; ++ case ')': ++ state = swi_done_idparams; ++ arg = ngx_array_push (&s->args); ++ if (arg == NULL) { ++ return NGX_ERROR; ++ } ++ arg->data = (u_char *)""; ++ arg->len = 0; ++ break; ++ default: ++ ngx_log_debug1 (NGX_LOG_DEBUG_MAIL, s->connection->log, 0, ++ "swi_idvalue_nil: expected SP/), got '%c'", ch); ++ return NGX_MAIL_PARSE_INVALID_COMMAND; ++ } ++ break; ++ ++ case swi_SP_before_idvalue: ++ switch (ch) ++ { ++ case ' ': ++ state = swi_begin_idvalue; ++ break; ++ default: ++ ngx_log_debug1 (NGX_LOG_DEBUG_MAIL, s->connection->log, 0, ++ "swi_SP_before_idvalue: expected SP, got '%c'", ch); ++ return NGX_MAIL_PARSE_INVALID_COMMAND; ++ } ++ break; ++ ++ case swi_X_before_idfield: ++ switch (ch) ++ { ++ case ' ': ++ state = swi_begin_idfield; ++ break; ++ case ')': ++ state = swi_done_idparams; ++ break; ++ default: ++ ngx_log_debug1 (NGX_LOG_DEBUG_MAIL, s->connection->log, 0, ++ "swi_X_before_idfield: expected SP/), got '%c'", ch); ++ return NGX_MAIL_PARSE_INVALID_COMMAND; ++ } ++ break; ++ ++ case swi_idvalue: ++ switch (ch) ++ { ++ case '\\': ++ if (!s->backslash) { ++ s->backslash = 1; ++ } else { ++ if (ch == '\\' || ch == '"') ++ s->backslash = 0; ++ else { ++ ngx_log_debug1 (NGX_LOG_DEBUG_MAIL, ++ s->connection->log, 0, ++ "swi_idvalue: \\ escapes non-quoted special '%c'", ++ ch); ++ return NGX_MAIL_PARSE_INVALID_COMMAND; ++ } ++ } ++ break; ++ ++ case '"': ++ if (s->backslash) ++ { ++ s->backslash = 0; ++ break; ++ } ++ s->quoted = 0; ++ arg = ngx_array_push (&s->args); ++ if (arg == NULL) { ++ return NGX_ERROR; ++ } ++ ++ arg->len = p - s->arg_start; ++ arg->data = s->arg_start; ++ s->arg_start = NULL; ++ state = swi_X_before_idfield; ++ break; ++ ++ case CR: ++ case LF: ++ ngx_log_debug0 (NGX_LOG_DEBUG_MAIL, s->connection->log, 0, ++ "swi_idvalue: CR/LF breaks id value"); ++ return NGX_MAIL_PARSE_INVALID_COMMAND; ++ default: ++ break; ++ } ++ break; ++ case swi_almost_done: ++ switch (ch) { ++ case LF: ++ return NGX_OK; ++ default: ++ return NGX_MAIL_PARSE_INVALID_COMMAND; ++ } ++ ++ default: ++ break; /* for avoid warning only */ ++ } /* switch (state) */ ++ } /* for */ ++ ++ s->buffer->pos = p; ++ s->state = state; ++ ++ return NGX_AGAIN; ++} ++ ++ + ngx_int_t + ngx_mail_smtp_parse_command(ngx_mail_session_t *s) + { + u_char ch, *p, *c, c0, c1, c2, c3; + ngx_str_t *arg; +- enum { +- sw_start = 0, +- sw_command, +- sw_invalid, +- sw_spaces_before_argument, +- sw_argument, +- sw_almost_done +- } state; + +- state = s->state; ++ ngx_smtp_parse_state_e state = s->state; + + for (p = s->buffer->pos; p < s->buffer->last; p++) { + ch = *p; +@@ -698,13 +1300,13 @@ + switch (state) { + + /* SMTP command */ +- case sw_start: ++ case sws_start: + s->cmd_start = p; +- state = sw_command; ++ state = sws_command; + + /* fall through */ + +- case sw_command: ++ case sws_command: + if (ch == ' ' || ch == CR || ch == LF) { + c = s->cmd_start; + +@@ -789,10 +1391,10 @@ + + switch (ch) { + case ' ': +- state = sw_spaces_before_argument; ++ state = sws_spaces_before_argument; + break; + case CR: +- state = sw_almost_done; ++ state = sws_almost_done; + break; + case LF: + goto done; +@@ -806,21 +1408,21 @@ + + break; + +- case sw_invalid: ++ case sws_invalid: + goto invalid; + +- case sw_spaces_before_argument: ++ case sws_spaces_before_argument: + switch (ch) { + case ' ': + break; + case CR: +- state = sw_almost_done; ++ state = sws_almost_done; + break; + case LF: + goto done; + default: + if (s->args.nelts <= 10) { +- state = sw_argument; ++ state = sws_argument; + s->arg_start = p; + break; + } +@@ -828,7 +1430,7 @@ + } + break; + +- case sw_argument: ++ case sws_argument: + switch (ch) { + case ' ': + case CR: +@@ -843,10 +1445,10 @@ + + switch (ch) { + case ' ': +- state = sw_spaces_before_argument; ++ state = sws_spaces_before_argument; + break; + case CR: +- state = sw_almost_done; ++ state = sws_almost_done; + break; + case LF: + goto done; +@@ -858,7 +1460,7 @@ + } + break; + +- case sw_almost_done: ++ case sws_almost_done: + switch (ch) { + case LF: + goto done; +@@ -876,19 +1478,19 @@ + done: + + s->buffer->pos = p + 1; +- s->state = (s->command != NGX_SMTP_AUTH) ? sw_start : sw_argument; ++ + + return NGX_OK; + + invalid: + +- s->state = sw_invalid; ++ s->state = sws_invalid; + + /* skip invalid command till LF */ + + for ( /* void */ ; p < s->buffer->last; p++) { + if (*p == LF) { +- s->state = sw_start; ++ s->state = sws_start; + s->buffer->pos = p + 1; + return NGX_MAIL_PARSE_INVALID_COMMAND; + } +@@ -905,12 +1507,6 @@ + { + ngx_str_t *arg; + +-#if (NGX_MAIL_SSL) +- if (ngx_mail_starttls_only(s, c)) { +- return NGX_MAIL_PARSE_INVALID_COMMAND; +- } +-#endif +- + if (s->args.nelts == 0) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } +@@ -925,7 +1521,7 @@ + return NGX_MAIL_AUTH_LOGIN; + } + +- if (s->args.nelts == 2) { ++ if (s->args.nelts == 2) { //initial response + return NGX_MAIL_AUTH_LOGIN_USERNAME; + } + +@@ -938,14 +1534,28 @@ + return NGX_MAIL_AUTH_PLAIN; + } + +- if (s->args.nelts == 2) { +- return ngx_mail_auth_plain(s, c, 1); ++ if (s->args.nelts == 2) { //initial response ++ return NGX_MAIL_AUTH_PLAIN_IR; + } + } + + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + ++ if (arg[0].len == 6) { ++ if (ngx_strncasecmp(arg[0].data, (u_char *) "GSSAPI", 6) == 0 ) { ++ if (s->args.nelts == 1) { ++ return NGX_MAIL_AUTH_GSSAPI; ++ } ++ ++ if (s->args.nelts == 2) { //initial response ++ return NGX_MAIL_AUTH_GSSAPI_IR; ++ } ++ } ++ ++ return NGX_MAIL_PARSE_INVALID_COMMAND; ++ } ++ + if (arg[0].len == 8) { + + if (ngx_strncasecmp(arg[0].data, (u_char *) "CRAM-MD5", 8) == 0) { +@@ -971,5 +1581,5 @@ + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + +- return NGX_MAIL_PARSE_INVALID_COMMAND; ++ return NGX_MAIL_PARSE_INVALID_AUTH_MECH; + } diff --git a/thirdparty/nginx/patches/nginx_src_mail_ngx_mail_pop3_handler.patch b/thirdparty/nginx/patches/nginx_src_mail_ngx_mail_pop3_handler.patch new file mode 100644 index 000000000..e703a4ed3 --- /dev/null +++ b/thirdparty/nginx/patches/nginx_src_mail_ngx_mail_pop3_handler.patch @@ -0,0 +1,386 @@ +--- nginx/src/mail/ngx_mail_pop3_handler.c 2023-03-07 16:26:44.725901500 +0530 ++++ nginx/src/mail/ngx_mail_pop3_handler.c 2023-09-14 18:47:12.646461800 +0530 +@@ -21,12 +21,18 @@ + static ngx_int_t ngx_mail_pop3_auth(ngx_mail_session_t *s, ngx_connection_t *c); + + +-static u_char pop3_greeting[] = "+OK POP3 ready" CRLF; ++/*static u_char pop3_greeting[] = "+OK POP3 ready" CRLF; zimbra uses the greeting specified by the directive */ + static u_char pop3_ok[] = "+OK" CRLF; + static u_char pop3_next[] = "+ " CRLF; ++static u_char pop3_gssapi_next[] = "+ " CRLF; + static u_char pop3_username[] = "+ VXNlcm5hbWU6" CRLF; + static u_char pop3_password[] = "+ UGFzc3dvcmQ6" CRLF; + static u_char pop3_invalid_command[] = "-ERR invalid command" CRLF; ++static u_char pop3_unsupported_mech[] = "-ERR mechanism not supported" CRLF; ++static u_char pop3_nocleartext[] = "-ERR cleartext logins disabled" CRLF; ++static u_char pop3_authaborted[] = "-ERR authentication aborted" CRLF; ++static u_char pop3_login_failed[] = "-ERR invalid username/password" CRLF; ++static u_char pop3_auth_failed[] = "-ERR line is too long" CRLF; + + + void +@@ -47,20 +53,20 @@ + return; + } + +- s->out.data = ngx_pnalloc(c->pool, sizeof(pop3_greeting) + s->salt.len); ++ s->out.data = ngx_pnalloc(c->pool, pscf->greeting.len + s->salt.len - 1); + if (s->out.data == NULL) { + ngx_mail_session_internal_server_error(s); + return; + } + +- p = ngx_cpymem(s->out.data, pop3_greeting, sizeof(pop3_greeting) - 3); ++ p = ngx_cpymem(s->out.data, pscf->greeting.data, pscf->greeting.len - 2); + *p++ = ' '; + p = ngx_cpymem(p, s->salt.data, s->salt.len); + + s->out.len = p - s->out.data; + + } else { +- ngx_str_set(&s->out, pop3_greeting); ++ s->out = pscf->greeting; + } + + c->read->handler = ngx_mail_pop3_init_protocol; +@@ -80,6 +86,7 @@ + { + ngx_connection_t *c; + ngx_mail_session_t *s; ++ ngx_mail_pop3_srv_conf_t *pscf; + + c = rev->data; + +@@ -102,13 +109,16 @@ + return; + } + +- s->buffer = ngx_create_temp_buf(c->pool, 128); ++ pscf = ngx_mail_get_module_srv_conf(s, ngx_mail_pop3_module); ++ ++ s->buffer = ngx_create_temp_buf(c->pool, pscf->client_buffer_size); + if (s->buffer == NULL) { + ngx_mail_session_internal_server_error(s); + return; + } + } + ++ ngx_mail_set_pop3_parse_state_start(s); + s->mail_state = ngx_pop3_start; + c->read->handler = ngx_mail_pop3_auth_state; + +@@ -143,7 +153,7 @@ + ngx_mail_close_connection(c); + return; + } +- ++ + return; + } + +@@ -175,6 +185,7 @@ + + case NGX_POP3_USER: + rc = ngx_mail_pop3_user(s, c); ++ s->mail_state = ngx_pop3_user; + break; + + case NGX_POP3_CAPA: +@@ -213,6 +224,7 @@ + + case NGX_POP3_PASS: + rc = ngx_mail_pop3_pass(s, c); ++ s->mail_state = ngx_pop3_user; + break; + + case NGX_POP3_CAPA: +@@ -240,8 +252,10 @@ + case ngx_pop3_auth_login_username: + rc = ngx_mail_auth_login_username(s, c, 0); + +- ngx_str_set(&s->out, pop3_password); +- s->mail_state = ngx_pop3_auth_login_password; ++ if(rc == NGX_MAIL_AUTH_ARGUMENT) { ++ ngx_str_set(&s->out, pop3_password); ++ s->mail_state = ngx_pop3_auth_login_password; ++ } + break; + + case ngx_pop3_auth_login_password: +@@ -256,6 +270,18 @@ + rc = ngx_mail_auth_cram_md5(s, c); + break; + ++ case ngx_pop3_auth_gssapi: ++ { ++ ngx_str_t output; ++ ngx_str_set(&output, ""); ++ rc = ngx_mail_auth_gssapi(s, c, &output); ++ if (rc == NGX_MAIL_AUTH_ARGUMENT) { ++ s->mail_state = ngx_pop3_auth_gssapi; ++ s->out = output; ++ } ++ break; ++ } ++ + case ngx_pop3_auth_external: + rc = ngx_mail_auth_external(s, c, 0); + break; +@@ -269,43 +295,73 @@ + switch (rc) { + + case NGX_DONE: +- ngx_mail_auth(s, c); ++ ngx_mail_do_auth(s, c); + return; + ++ case NGX_OK: ++ s->arg_start = NULL; ++ ngx_mail_set_pop3_parse_state_start(s); ++ break; ++ ++ case NGX_MAIL_AUTH_ABORT: ++ ngx_str_set(&s->out, pop3_authaborted); ++ s->mail_state = ngx_pop3_start; ++ s->arg_start = NULL; ++ ngx_mail_set_pop3_parse_state_start(s); ++ break; ++ + case NGX_ERROR: + ngx_mail_session_internal_server_error(s); + return; + +- case NGX_MAIL_PARSE_INVALID_COMMAND: ++ case NGX_MAIL_LOGIN_FAILED: ++ ngx_str_set(&s->out, pop3_login_failed); + s->mail_state = ngx_pop3_start; +- s->state = 0; +- +- ngx_str_set(&s->out, pop3_invalid_command); +- +- /* fall through */ +- +- case NGX_OK: ++ s->arg_start = NULL; ++ ngx_mail_set_pop3_parse_state_start(s); ++ break; + +- s->args.nelts = 0; +- +- if (s->buffer->pos == s->buffer->last) { +- s->buffer->pos = s->buffer->start; +- s->buffer->last = s->buffer->start; +- } ++ case NGX_MAIL_AUTH_FAILED: ++ ngx_str_set(&s->out, pop3_auth_failed); ++ s->mail_state = ngx_pop3_start; ++ s->arg_start = NULL; ++ ngx_mail_set_pop3_parse_state_start(s); ++ break; ++ ++ case NGX_MAIL_PARSE_INVALID_AUTH_MECH: ++ ngx_log_debug0 (NGX_LOG_DEBUG_MAIL, c->log, 0, ++ "unsupported POP auth mechanism"); ++ ngx_str_set(&s->out, pop3_unsupported_mech); ++ s->mail_state = ngx_pop3_start; ++ s->arg_start = NULL; ++ ngx_mail_set_pop3_parse_state_start(s); ++ break; + +- if (s->state) { +- s->arg_start = s->buffer->pos; +- } ++ case NGX_MAIL_PARSE_INVALID_COMMAND: ++ ngx_str_set(&s->out, pop3_invalid_command); ++ s->mail_state = ngx_pop3_start; ++ s->arg_start = NULL; ++ ngx_mail_set_pop3_parse_state_start(s); ++ break; ++ ++ case NGX_MAIL_AUTH_ARGUMENT: ++ s->arg_start = s->buffer->start; ++ ngx_mail_set_pop3_parse_state_argument(s); ++ break; ++ } + +- if (ngx_handle_read_event(c->read, 0) != NGX_OK) { +- ngx_mail_session_internal_server_error(s); +- return; +- } ++ s->args.nelts = 0; ++ ngx_mail_reset_parse_buffer(s); + +- ngx_mail_send(c->write); ++ if (ngx_handle_read_event(c->read, 0) != NGX_OK) { ++ ngx_mail_session_internal_server_error(s); ++ return; + } ++ ++ ngx_mail_send(c->write); + } + ++ + static ngx_int_t + ngx_mail_pop3_user(ngx_mail_session_t *s, ngx_connection_t *c) + { +@@ -313,6 +369,7 @@ + + #if (NGX_MAIL_SSL) + if (ngx_mail_starttls_only(s, c)) { ++ ngx_str_set(&s->out, pop3_nocleartext); + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + #endif +@@ -322,6 +379,10 @@ + } + + arg = s->args.elts; ++ if(arg[0].len > NGX_MAIL_MAX_LOGIN_LEN) { ++ ngx_str_null(&s->login); ++ return NGX_OK; ++ } + s->login.len = arg[0].len; + s->login.data = ngx_pnalloc(c->pool, s->login.len); + if (s->login.data == NULL) { +@@ -333,8 +394,6 @@ + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, + "pop3 login: \"%V\"", &s->login); + +- s->mail_state = ngx_pop3_user; +- + return NGX_OK; + } + +@@ -349,6 +408,11 @@ + } + + arg = s->args.elts; ++ ++ if (s->login.len == 0 || arg[0].len > NGX_MAIL_MAX_PASSWORD_LEN) { ++ return NGX_MAIL_LOGIN_FAILED; ++ } ++ + s->passwd.len = arg[0].len; + s->passwd.data = ngx_pnalloc(c->pool, s->passwd.len); + if (s->passwd.data == NULL) { +@@ -362,6 +426,9 @@ + "pop3 passwd: \"%V\"", &s->passwd); + #endif + ++ s->auth_method = NGX_MAIL_AUTH_PASSWD; ++ s->usedauth = 0; ++ + return NGX_DONE; + } + +@@ -464,6 +531,7 @@ + "pop3 apop: \"%V\" \"%V\"", &s->login, &s->passwd); + + s->auth_method = NGX_MAIL_AUTH_APOP; ++ s->usedauth = 0; + + return NGX_DONE; + } +@@ -484,10 +552,7 @@ + pscf = ngx_mail_get_module_srv_conf(s, ngx_mail_pop3_module); + + if (s->args.nelts == 0) { +- s->out = pscf->auth_capability; +- s->state = 0; +- +- return NGX_OK; ++ return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + rc = ngx_mail_auth_parse(s, c); +@@ -496,34 +561,74 @@ + + case NGX_MAIL_AUTH_LOGIN: + ++ if (!(pscf->auth_methods & NGX_MAIL_AUTH_LOGIN_ENABLED)) { ++ return NGX_MAIL_PARSE_INVALID_AUTH_MECH; ++ } + ngx_str_set(&s->out, pop3_username); + s->mail_state = ngx_pop3_auth_login_username; + +- return NGX_OK; ++ return NGX_MAIL_AUTH_ARGUMENT; + + case NGX_MAIL_AUTH_LOGIN_USERNAME: + ++ if (!(pscf->auth_methods & NGX_MAIL_AUTH_LOGIN_ENABLED)) { ++ return NGX_MAIL_PARSE_INVALID_AUTH_MECH; ++ } + ngx_str_set(&s->out, pop3_password); + s->mail_state = ngx_pop3_auth_login_password; + +- return ngx_mail_auth_login_username(s, c, 1); ++ return ngx_mail_auth_login_username(s, c, 0); + + case NGX_MAIL_AUTH_PLAIN: + ++ if (!(pscf->auth_methods & NGX_MAIL_AUTH_PLAIN_ENABLED)) { ++ return NGX_MAIL_PARSE_INVALID_AUTH_MECH; ++ } + ngx_str_set(&s->out, pop3_next); + s->mail_state = ngx_pop3_auth_plain; + +- return NGX_OK; ++ return NGX_MAIL_AUTH_ARGUMENT; ++ ++ case NGX_MAIL_AUTH_PLAIN_IR: ++ ++ if (!(pscf->auth_methods & NGX_MAIL_AUTH_PLAIN_ENABLED)) { ++ return NGX_MAIL_PARSE_INVALID_AUTH_MECH; ++ } ++ return ngx_mail_auth_plain(s, c, 1); ++ ++ case NGX_MAIL_AUTH_GSSAPI: ++ if( !(pscf->auth_methods & NGX_MAIL_AUTH_GSSAPI_ENABLED)) { ++ return NGX_MAIL_PARSE_INVALID_AUTH_MECH; ++ } ++ ngx_str_set(&s->out, pop3_gssapi_next); ++ s->mail_state = ngx_pop3_auth_gssapi; ++ return NGX_MAIL_AUTH_ARGUMENT; ++ ++ case NGX_MAIL_AUTH_GSSAPI_IR: ++ ++ if (!(pscf->auth_methods & NGX_MAIL_AUTH_GSSAPI_ENABLED)) { ++ return NGX_MAIL_PARSE_INVALID_AUTH_MECH; ++ } ++ s->mail_state = ngx_pop3_auth_gssapi; ++ ngx_str_t output; ++ ngx_str_set(&output, ""); ++ int res = ngx_mail_auth_gssapi(s, c, &output); ++ if(res == NGX_MAIL_AUTH_ARGUMENT) { ++ s->out = output; ++ return NGX_MAIL_AUTH_ARGUMENT; ++ } else { ++ return res; ++ } + + case NGX_MAIL_AUTH_CRAM_MD5: + + if (!(pscf->auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED)) { +- return NGX_MAIL_PARSE_INVALID_COMMAND; ++ return NGX_MAIL_PARSE_INVALID_AUTH_MECH; + } + + if (ngx_mail_auth_cram_md5_salt(s, c, "+ ", 2) == NGX_OK) { + s->mail_state = ngx_pop3_auth_cram_md5; +- return NGX_OK; ++ return NGX_MAIL_AUTH_ARGUMENT; + } + + return NGX_ERROR; +@@ -538,6 +643,9 @@ + s->mail_state = ngx_pop3_auth_external; + + return NGX_OK; ++ ++ default: ++ break; + } + + return rc; diff --git a/thirdparty/nginx/patches/nginx_src_mail_ngx_mail_pop3_module_c.patch b/thirdparty/nginx/patches/nginx_src_mail_ngx_mail_pop3_module_c.patch new file mode 100644 index 000000000..bc831928f --- /dev/null +++ b/thirdparty/nginx/patches/nginx_src_mail_ngx_mail_pop3_module_c.patch @@ -0,0 +1,370 @@ +--- nginx/src/mail/ngx_mail_pop3_module.c 2023-03-07 16:26:44.727853600 +0530 ++++ nginx/src/mail/ngx_mail_pop3_module.c 2023-09-14 18:47:12.652834400 +0530 +@@ -16,6 +16,7 @@ + static char *ngx_mail_pop3_merge_srv_conf(ngx_conf_t *cf, void *parent, + void *child); + ++static ngx_str_t default_pop3_greeting = ngx_string("+OK POP3 ready"); + + static ngx_str_t ngx_mail_pop3_default_capabilities[] = { + ngx_string("TOP"), +@@ -28,7 +29,9 @@ + static ngx_conf_bitmask_t ngx_mail_pop3_auth_methods[] = { + { ngx_string("plain"), NGX_MAIL_AUTH_PLAIN_ENABLED }, + { ngx_string("apop"), NGX_MAIL_AUTH_APOP_ENABLED }, ++ { ngx_string("login"), NGX_MAIL_AUTH_LOGIN_ENABLED }, + { ngx_string("cram-md5"), NGX_MAIL_AUTH_CRAM_MD5_ENABLED }, ++ { ngx_string("gssapi"), NGX_MAIL_AUTH_GSSAPI_ENABLED }, + { ngx_string("external"), NGX_MAIL_AUTH_EXTERNAL_ENABLED }, + { ngx_null_string, 0 } + }; +@@ -44,6 +47,21 @@ + }; + + ++static ngx_str_t ngx_mail_pop3_auth_plain_capability = ++ ngx_string("+OK methods supported:" CRLF ++ "LOGIN" CRLF ++ "PLAIN" CRLF ++ "." CRLF); ++ ++ ++static ngx_str_t ngx_mail_pop3_auth_cram_md5_capability = ++ ngx_string("+OK methods supported:" CRLF ++ "LOGIN" CRLF ++ "PLAIN" CRLF ++ "CRAM-MD5" CRLF ++ "." CRLF); ++ ++ + static ngx_mail_protocol_t ngx_mail_pop3_protocol = { + ngx_string("pop3"), + ngx_string("\x04pop3"), +@@ -57,12 +75,20 @@ + + ngx_string("-ERR internal server error" CRLF), + ngx_string("-ERR SSL certificate error" CRLF), +- ngx_string("-ERR No required SSL certificate" CRLF) ++ ngx_string("-ERR No required SSL certificate" CRLF), ++ ngx_string("") + }; + + + static ngx_command_t ngx_mail_pop3_commands[] = { + ++ { ngx_string("pop3_client_buffer"), ++ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, ++ ngx_conf_set_size_slot, ++ NGX_MAIL_SRV_CONF_OFFSET, ++ offsetof(ngx_mail_pop3_srv_conf_t, client_buffer_size), ++ NULL }, ++ + { ngx_string("pop3_capabilities"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE, + ngx_mail_capabilities, +@@ -77,6 +103,13 @@ + offsetof(ngx_mail_pop3_srv_conf_t, auth_methods), + &ngx_mail_pop3_auth_methods }, + ++ { ngx_string("pop3_greeting"), ++ NGX_MAIL_MAIN_CONF|NGX_CONF_TAKE1, ++ ngx_conf_set_str_slot, ++ NGX_MAIL_SRV_CONF_OFFSET, ++ offsetof(ngx_mail_pop3_srv_conf_t, greeting), ++ NULL }, ++ + ngx_null_command + }; + +@@ -118,6 +151,8 @@ + return NULL; + } + ++ pscf->client_buffer_size = NGX_CONF_UNSET_SIZE; ++ + if (ngx_array_init(&pscf->capabilities, cf->pool, 4, sizeof(ngx_str_t)) + != NGX_OK) + { +@@ -134,19 +169,30 @@ + ngx_mail_pop3_srv_conf_t *prev = parent; + ngx_mail_pop3_srv_conf_t *conf = child; + +- u_char *p; +- size_t size, stls_only_size; ++ u_char *p, *p1, *p2, *p3; ++ size_t s1, s2, s3; + ngx_str_t *c, *d; +- ngx_uint_t i, m; ++ ngx_uint_t i; ++ ++ ngx_conf_merge_size_value(conf->client_buffer_size, ++ prev->client_buffer_size, ++ (size_t) 4*ngx_pagesize); + + ngx_conf_merge_bitmask_value(conf->auth_methods, + prev->auth_methods, +- (NGX_CONF_BITMASK_SET +- |NGX_MAIL_AUTH_PLAIN_ENABLED)); ++ NGX_CONF_BITMASK_SET); + ++ /* + if (conf->auth_methods & NGX_MAIL_AUTH_PLAIN_ENABLED) { + conf->auth_methods |= NGX_MAIL_AUTH_LOGIN_ENABLED; + } ++ */ ++ ++ ngx_conf_merge_str_value(conf->greeting, prev->greeting,""); ++ ++ if (conf->greeting.len == 0) { ++ conf->greeting = default_pop3_greeting; ++ } + + if (conf->capabilities.nelts == 0) { + conf->capabilities = prev->capabilities; +@@ -164,160 +210,124 @@ + } + } + +- size = sizeof("+OK Capability list follows" CRLF) - 1 +- + sizeof("." CRLF) - 1; +- +- stls_only_size = size + sizeof("STLS" CRLF) - 1; ++ s1 = sizeof("+OK Capability list follows" CRLF) - 1 ++ + sizeof("." CRLF)-1; ++ if (conf->auth_methods & ++ (NGX_MAIL_AUTH_PLAIN_ENABLED | NGX_MAIL_AUTH_GSSAPI_ENABLED)) ++ s1 += sizeof("SASL" CRLF)-1; ++ s2 = s1; ++ s3 = s1; + + c = conf->capabilities.elts; +- for (i = 0; i < conf->capabilities.nelts; i++) { +- size += c[i].len + sizeof(CRLF) - 1; +- +- if (ngx_strcasecmp(c[i].data, (u_char *) "USER") == 0) { +- continue; ++ for (i=0; icapabilities.nelts; ++i) ++ { ++ s1 += c[i].len + sizeof (CRLF)-1; ++ s2 += c[i].len + sizeof (CRLF)-1; ++ if (ngx_strcasecmp(c[i].data, (u_char *) "USER") != 0) { ++ s3 += c[i].len + sizeof (CRLF)-1; + } +- +- stls_only_size += c[i].len + sizeof(CRLF) - 1; + } + +- size += sizeof("SASL") - 1 + sizeof(CRLF) - 1; ++ if (conf->auth_methods & NGX_MAIL_AUTH_PLAIN_ENABLED) { ++ s1 += sizeof(" PLAIN") - 1; ++ s2 += sizeof(" PLAIN") - 1; ++ s3 += sizeof(" PLAIN") - 1; ++ } ++ if (conf->auth_methods & NGX_MAIL_AUTH_GSSAPI_ENABLED) { ++ s1 += sizeof(" GSSAPI") - 1; ++ s2 += sizeof(" GSSAPI") - 1; ++ s3 += sizeof(" GSSAPI") - 1; ++ } + +- for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0; +- m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED; +- m <<= 1, i++) +- { +- if (ngx_mail_pop3_auth_methods_names[i].len == 0) { +- continue; +- } ++ s2 += sizeof("STLS" CRLF) - 1; ++ s3 += sizeof("STLS" CRLF) - 1; + +- if (m & conf->auth_methods) { +- size += 1 + ngx_mail_pop3_auth_methods_names[i].len; +- } ++ p1 = ngx_pnalloc(cf->pool,s1); ++ if (p1 == NULL) { ++ return NGX_CONF_ERROR; + } +- +- p = ngx_pnalloc(cf->pool, size); +- if (p == NULL) { ++ p2 = ngx_palloc(cf->pool,s2); ++ if (p2 == NULL) { + return NGX_CONF_ERROR; + } +- +- conf->capability.len = size; +- conf->capability.data = p; +- +- p = ngx_cpymem(p, "+OK Capability list follows" CRLF, +- sizeof("+OK Capability list follows" CRLF) - 1); +- +- for (i = 0; i < conf->capabilities.nelts; i++) { +- p = ngx_cpymem(p, c[i].data, c[i].len); +- *p++ = CR; *p++ = LF; ++ p3 = ngx_pnalloc(cf->pool,s3); ++ if (p3 == NULL) { ++ return NGX_CONF_ERROR; + } + +- p = ngx_cpymem(p, "SASL", sizeof("SASL") - 1); ++ conf->capability.len = s1; ++ conf->capability.data = p1; ++ conf->starttls_capability.len = s2; ++ conf->starttls_capability.data = p2; ++ conf->starttls_only_capability.len = s3; ++ conf->starttls_only_capability.data = p3; ++ ++ p1 = ngx_cpymem(p1, "+OK Capability list follows" CRLF, ++ sizeof("+OK Capability list follows" CRLF) - 1); ++ p2 = ngx_cpymem(p2, "+OK Capability list follows" CRLF, ++ sizeof("+OK Capability list follows" CRLF) - 1); ++ p3 = ngx_cpymem(p3, "+OK Capability list follows" CRLF, ++ sizeof("+OK Capability list follows" CRLF) - 1); + +- for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0; +- m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED; +- m <<= 1, i++) ++ c = conf->capabilities.elts; ++ for (i = 0; i < conf->capabilities.nelts; ++i) + { +- if (ngx_mail_pop3_auth_methods_names[i].len == 0) { +- continue; +- } +- +- if (m & conf->auth_methods) { +- *p++ = ' '; +- p = ngx_cpymem(p, ngx_mail_pop3_auth_methods_names[i].data, +- ngx_mail_pop3_auth_methods_names[i].len); ++ p1 = ngx_cpymem(p1,c[i].data,c[i].len); ++ p2 = ngx_cpymem(p2,c[i].data,c[i].len); ++ *p1++ = CR; *p1++ = LF; ++ *p2++ = CR; *p2++ = LF; ++ if (ngx_strcasecmp(c[i].data, (u_char *) "USER") != 0) { ++ p3 = ngx_cpymem(p3,c[i].data,c[i].len); ++ *p3++ = CR; *p3++ = LF; + } + } + +- *p++ = CR; *p++ = LF; +- +- *p++ = '.'; *p++ = CR; *p = LF; +- +- +- size += sizeof("STLS" CRLF) - 1; +- +- p = ngx_pnalloc(cf->pool, size); +- if (p == NULL) { +- return NGX_CONF_ERROR; +- } +- +- conf->starttls_capability.len = size; +- conf->starttls_capability.data = p; +- +- p = ngx_cpymem(p, conf->capability.data, +- conf->capability.len - (sizeof("." CRLF) - 1)); ++ if (conf->auth_methods & ++ (NGX_MAIL_AUTH_PLAIN_ENABLED | NGX_MAIL_AUTH_GSSAPI_ENABLED)) { ++ p1 = ngx_cpymem(p1,"SASL",sizeof("SASL") - 1); ++ p2 = ngx_cpymem(p2,"SASL",sizeof("SASL") - 1); ++ p3 = ngx_cpymem(p3,"SASL",sizeof("SASL") - 1); + +- p = ngx_cpymem(p, "STLS" CRLF, sizeof("STLS" CRLF) - 1); +- *p++ = '.'; *p++ = CR; *p = LF; +- +- +- size = sizeof("+OK methods supported:" CRLF) - 1 +- + sizeof("." CRLF) - 1; +- +- for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0; +- m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED; +- m <<= 1, i++) +- { +- if (ngx_mail_pop3_auth_methods_names[i].len == 0) { +- continue; ++ if (conf->auth_methods & NGX_MAIL_AUTH_PLAIN_ENABLED) { ++ p1 = ngx_cpymem(p1," PLAIN",sizeof(" PLAIN") - 1); ++ p2 = ngx_cpymem(p2," PLAIN",sizeof(" PLAIN") - 1); ++ p3 = ngx_cpymem(p3," PLAIN",sizeof(" PLAIN") - 1); + } +- +- if (m & conf->auth_methods) { +- size += ngx_mail_pop3_auth_methods_names[i].len +- + sizeof(CRLF) - 1; ++ if (conf->auth_methods & NGX_MAIL_AUTH_GSSAPI_ENABLED) { ++ p1 = ngx_cpymem(p1," GSSAPI",sizeof(" GSSAPI") - 1); ++ p2 = ngx_cpymem(p2," GSSAPI",sizeof(" GSSAPI") - 1); ++ p3 = ngx_cpymem(p3," GSSAPI",sizeof(" GSSAPI") - 1); + } +- } + +- p = ngx_pnalloc(cf->pool, size); +- if (p == NULL) { +- return NGX_CONF_ERROR; ++ *p1++ = CR; *p1++ = LF; ++ *p2++ = CR; *p2++ = LF; ++ *p3++ = CR; *p3++ = LF; + } + +- conf->auth_capability.data = p; +- conf->auth_capability.len = size; ++ p2 = ngx_cpymem(p2,"STLS" CRLF, sizeof("STLS" CRLF)-1); ++ p3 = ngx_cpymem(p3,"STLS" CRLF, sizeof("STLS" CRLF)-1); + +- p = ngx_cpymem(p, "+OK methods supported:" CRLF, +- sizeof("+OK methods supported:" CRLF) - 1); ++ *p1++ = '.'; *p1++ = CR; *p1++ = LF; ++ *p2++ = '.'; *p2++ = CR; *p2++ = LF; ++ *p3++ = '.'; *p3++ = CR; *p3++ = LF; + +- for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0; +- m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED; +- m <<= 1, i++) +- { +- if (ngx_mail_pop3_auth_methods_names[i].len == 0) { +- continue; +- } ++ /* not required */ ++ if (conf->auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED) { ++ conf->auth_capability = ngx_mail_pop3_auth_cram_md5_capability; + +- if (m & conf->auth_methods) { +- p = ngx_cpymem(p, ngx_mail_pop3_auth_methods_names[i].data, +- ngx_mail_pop3_auth_methods_names[i].len); +- *p++ = CR; *p++ = LF; +- } ++ } else { ++ conf->auth_capability = ngx_mail_pop3_auth_plain_capability; + } + +- *p++ = '.'; *p++ = CR; *p = LF; +- +- +- p = ngx_pnalloc(cf->pool, stls_only_size); ++ p = ngx_pnalloc(cf->pool,conf->greeting.len + 2); + if (p == NULL) { + return NGX_CONF_ERROR; + } + +- conf->starttls_only_capability.len = stls_only_size; +- conf->starttls_only_capability.data = p; +- +- p = ngx_cpymem(p, "+OK Capability list follows" CRLF, +- sizeof("+OK Capability list follows" CRLF) - 1); +- +- for (i = 0; i < conf->capabilities.nelts; i++) { +- if (ngx_strcasecmp(c[i].data, (u_char *) "USER") == 0) { +- continue; +- } +- +- p = ngx_cpymem(p, c[i].data, c[i].len); +- *p++ = CR; *p++ = LF; +- } +- +- p = ngx_cpymem(p, "STLS" CRLF, sizeof("STLS" CRLF) - 1); +- *p++ = '.'; *p++ = CR; *p = LF; ++ ngx_memcpy(p, conf->greeting.data, conf->greeting.len); ++ ngx_memcpy(p + conf->greeting.len, CRLF, sizeof(CRLF) - 1); ++ conf->greeting.data = p; ++ conf->greeting.len += 2; + + return NGX_CONF_OK; + } diff --git a/thirdparty/nginx/patches/nginx_src_mail_ngx_mail_pop3_module_h.patch b/thirdparty/nginx/patches/nginx_src_mail_ngx_mail_pop3_module_h.patch new file mode 100644 index 000000000..f28341938 --- /dev/null +++ b/thirdparty/nginx/patches/nginx_src_mail_ngx_mail_pop3_module_h.patch @@ -0,0 +1,24 @@ +--- nginx/src/mail/ngx_mail_pop3_module.h 2023-03-07 16:26:44.730781700 +0530 ++++ nginx/src/mail/ngx_mail_pop3_module.h 2023-09-14 18:47:12.657858600 +0530 +@@ -15,6 +15,10 @@ + + + typedef struct { ++ // Zimbra customizations start here (Jira Tickets: ) ++ size_t client_buffer_size; ++ // Zimbra customizations end here ++ + ngx_str_t capability; + ngx_str_t starttls_capability; + ngx_str_t starttls_only_capability; +@@ -23,6 +27,10 @@ + ngx_uint_t auth_methods; + + ngx_array_t capabilities; ++ ++ // Zimbra customizations start here (Jira Tickets: ) ++ ngx_str_t greeting; ++ // Zimbra end start here + } ngx_mail_pop3_srv_conf_t; + + diff --git a/thirdparty/nginx/patches/nginx_src_mail_ngx_mail_proxy_module.patch b/thirdparty/nginx/patches/nginx_src_mail_ngx_mail_proxy_module.patch new file mode 100644 index 000000000..ac99d9827 --- /dev/null +++ b/thirdparty/nginx/patches/nginx_src_mail_ngx_mail_proxy_module.patch @@ -0,0 +1,1663 @@ +--- nginx/src/mail/ngx_mail_proxy_module.c 2023-07-23 13:21:27.110618600 +0530 ++++ nginx/src/mail/ngx_mail_proxy_module.c 2023-09-14 18:47:12.661845700 +0530 +@@ -5,20 +5,32 @@ + */ + + ++#include + #include + #include + #include + #include + #include ++#include ++#include ++#include ++#include ++#include + ++extern ngx_module_t ngx_mail_auth_http_module; + + typedef struct { + ngx_flag_t enable; + ngx_flag_t pass_error_message; ++ ngx_flag_t issue_pop3_xoip; ++ ngx_flag_t issue_imap_id; + ngx_flag_t xclient; ++ ngx_flag_t proxy_ssl; ++ ngx_ssl_t *ssl; + ngx_flag_t smtp_auth; + ngx_flag_t proxy_protocol; + size_t buffer_size; ++ ngx_msec_t ctimeout; + ngx_msec_t timeout; + } ngx_mail_proxy_conf_t; + +@@ -39,6 +51,31 @@ + static char *ngx_mail_proxy_merge_conf(ngx_conf_t *cf, void *parent, + void *child); + ++#if (NGX_MAIL_SSL) ++static char *ngx_mail_proxy_ssl(ngx_conf_t *cf, ngx_command_t *cmd, ++ void *conf); ++static void ngx_mail_proxy_ssl_init_connection(ngx_mail_session_t *s, ++ ngx_connection_t *c); ++static void ngx_mail_proxy_ssl_handshake(ngx_connection_t *c); ++/** TODO suport ssl session reuse ++static void ngx_mail_proxy_set_session(ngx_peer_connection_t *pc); ++static void ngx_mail_proxy_save_session(ngx_peer_connection_t *pc); ++*/ ++#endif ++ ++/* throttle */ ++static void ngx_mail_proxy_throttle_imap(ngx_event_t *rev); ++static void ngx_mail_proxy_allow_imap(throttle_callback_t *callback); ++static void ngx_mail_proxy_choke_imap(throttle_callback_t *callback); ++static void ngx_mail_proxy_throttle_pop3(ngx_event_t *rev); ++static void ngx_mail_proxy_allow_pop3(throttle_callback_t *callback); ++static void ngx_mail_proxy_choke_pop3(throttle_callback_t *callback); ++static void ngx_mail_proxy_choke_session(ngx_mail_session_t *s); ++ ++/* utility */ ++static ngx_str_t ngx_imap_quote_string(ngx_pool_t *pool, ngx_str_t *u); ++static void ngx_mail_proxy_auth_sleep_handler (ngx_event_t *rev); ++ + + static ngx_command_t ngx_mail_proxy_commands[] = { + +@@ -56,6 +93,13 @@ + offsetof(ngx_mail_proxy_conf_t, buffer_size), + NULL }, + ++ { ngx_string("proxy_ctimeout"), ++ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, ++ ngx_conf_set_msec_slot, ++ NGX_MAIL_SRV_CONF_OFFSET, ++ offsetof(ngx_mail_proxy_conf_t, ctimeout), ++ NULL }, ++ + { ngx_string("proxy_timeout"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, +@@ -70,6 +114,20 @@ + offsetof(ngx_mail_proxy_conf_t, pass_error_message), + NULL }, + ++ { ngx_string("proxy_issue_pop3_xoip"), ++ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, ++ ngx_conf_set_flag_slot, ++ NGX_MAIL_SRV_CONF_OFFSET, ++ offsetof(ngx_mail_proxy_conf_t, issue_pop3_xoip), ++ NULL }, ++ ++ { ngx_string("proxy_issue_imap_id"), ++ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, ++ ngx_conf_set_flag_slot, ++ NGX_MAIL_SRV_CONF_OFFSET, ++ offsetof(ngx_mail_proxy_conf_t, issue_imap_id), ++ NULL }, ++ + { ngx_string("xclient"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, +@@ -77,6 +135,17 @@ + offsetof(ngx_mail_proxy_conf_t, xclient), + NULL }, + ++#if (NGX_MAIL_SSL) ++ ++ { ngx_string("proxy_ssl"), ++ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG, ++ ngx_mail_proxy_ssl, ++ NGX_MAIL_SRV_CONF_OFFSET, ++ offsetof(ngx_mail_proxy_conf_t, proxy_ssl), ++ NULL }, ++ ++#endif ++ + { ngx_string("proxy_smtp_auth"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, +@@ -121,8 +190,12 @@ + NGX_MODULE_V1_PADDING + }; + +- ++static u_char pop3_authplain[] = "AUTH PLAIN" CRLF; ++static u_char pop3_authxzimbra[] = "AUTH X-ZIMBRA" CRLF; + static u_char smtp_auth_ok[] = "235 2.0.0 OK" CRLF; ++static u_char imap_login_no[] = "NO LOGIN failed" CRLF; ++static u_char imap_auth_no[] = "NO AUTHENTICATE failed" CRLF; ++static u_char imap_no[] = "NO" CRLF; + + + void +@@ -159,7 +232,9 @@ + return; + } + +- ngx_add_timer(p->upstream.connection->read, cscf->timeout); ++ pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module); ++ ++ ngx_add_timer(p->upstream.connection->read, pcf->ctimeout); + + p->upstream.connection->data = s; + p->upstream.connection->pool = s->connection->pool; +@@ -167,8 +242,6 @@ + s->connection->read->handler = ngx_mail_proxy_block_read; + p->upstream.connection->write->handler = ngx_mail_proxy_write_handler; + +- pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module); +- + s->proxy->buffer = ngx_create_temp_buf(s->connection->pool, + pcf->buffer_size); + if (s->proxy->buffer == NULL) { +@@ -180,6 +253,16 @@ + + s->out.len = 0; + ++/* bug 32683, ssl support on mail proxy connection */ ++#if (NGX_MAIL_SSL) ++ ++ if (pcf->proxy_ssl && p->upstream.connection->ssl == NULL) { ++ ngx_mail_proxy_ssl_init_connection(s, p->upstream.connection); ++ return; ++ } ++ ++#endif ++ + switch (s->protocol) { + + case NGX_MAIL_POP3_PROTOCOL: +@@ -228,10 +311,20 @@ + { + u_char *p; + ngx_int_t rc; ++ int family; + ngx_str_t line; ++ size_t len; + ngx_connection_t *c; + ngx_mail_session_t *s; + ngx_mail_proxy_conf_t *pcf; ++ struct sockaddr_in *sin; ++ ++#if (NGX_HAVE_INET6) ++ struct sockaddr_in6 *sin6; ++#endif ++ ++ ngx_mail_core_srv_conf_t *cscf; ++ ngx_str_t ap, ap64, tmp; + + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, + "mail proxy pop3 auth handler"); +@@ -239,6 +332,9 @@ + c = rev->data; + s = c->data; + ++ pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module); ++ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); ++ + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, + "upstream timed out"); +@@ -258,7 +354,7 @@ + return; + } + +- rc = ngx_mail_proxy_read_response(s, 0); ++ rc = ngx_mail_proxy_read_response(s, s->mail_state); + + if (rc == NGX_AGAIN) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { +@@ -270,29 +366,158 @@ + } + + if (rc == NGX_ERROR) { +- ngx_mail_proxy_upstream_error(s); +- return; ++ if (s->mail_state == ngx_pop3_passwd || ++ s->mail_state == ngx_pop3_auth_plain_response ++ ) ++ { ++ if(s->proxy->upstream.connection->read->timer_set) { ++ ngx_del_timer(s->proxy->upstream.connection->read); ++ } ++ ++ ngx_add_timer(s->connection->read,cscf->auth_wait_intvl); ++ s->connection->read->handler = ngx_mail_proxy_auth_sleep_handler; ++ return; ++ } ++ else ++ { ++ ngx_mail_proxy_upstream_error(s); ++ return; ++ } ++ } ++ ++ if (s->mail_state == ngx_pop3_start) ++ { ++ // bug 23349 -- conditionally (not always) issue pop3 xoip command ++ if (pcf->issue_pop3_xoip == 0) { ++ s->mail_state = ngx_pop3_xoip; ++ } + } + + switch (s->mail_state) { + + case ngx_pop3_start: +- ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail proxy send user"); ++ s->connection->log->action = ++ "sending POP3 XOIP command to upstream"; + +- s->connection->log->action = "sending user name to upstream"; ++ /* Bug 13325 - The upstream server needs to record the IP address of ++ the downstream client that connected. For this, the Zimbra server ++ has been modified to support the XOIP command that will allow ++ the proxy to pass the IP address of the downstream client. Both ++ ipv4 and ipv6 (bug 56383) has been supported ++ */ ++ family = s->connection->sockaddr->sa_family; ++ if (family == AF_INET) { ++ len = NGX_INET_ADDRSTRLEN; ++ sin = (struct sockaddr_in *) s->connection->sockaddr; ++ ++#if (NGX_HAVE_INET6) ++ } else { /* AF_INET6 */ ++ len = NGX_INET6_ADDRSTRLEN; ++ sin6 = (struct sockaddr_in6 *) s->connection->sockaddr; ++#endif ++ ++ } ++ ++ line.len = sizeof ("XOIP ") -1 + ++ len + ++ sizeof (CRLF) - 1; ++ ++ line.data = ngx_palloc(c->pool, line.len); + +- line.len = sizeof("USER ") - 1 + s->login.len + 2; +- line.data = ngx_pnalloc(c->pool, line.len); + if (line.data == NULL) { + ngx_mail_proxy_internal_server_error(s); + return; + } + +- p = ngx_cpymem(line.data, "USER ", sizeof("USER ") - 1); +- p = ngx_cpymem(p, s->login.data, s->login.len); +- *p++ = CR; *p = LF; ++ p = ngx_sprintf (line.data, "XOIP "); ++ tmp.data = p; ++ ++ if (family == AF_INET) { ++ len = ngx_inet_ntop (family, &(sin->sin_addr.s_addr), p, ++ line.data + line.len - p); ++ ++#if (NGX_HAVE_INET6) ++ } else { /* AF_INET6 */ ++ len = ngx_inet_ntop (family, &(sin6->sin6_addr.s6_addr), ++ p, line.data + line.len - p); ++#endif ++ ++ } ++ ++ p += len; ++ ++ tmp.len = p - tmp.data; ++ ++ *p++ = CR; ++ *p++ = LF; ++ ++ line.len = p - line.data; ++ ++ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, rev->log, 0, ++ "sending POP3 XOIP command (XOIP %V)", ++ &tmp); ++ ++ s->mail_state = ngx_pop3_xoip; ++ ++ break; ++ ++ case ngx_pop3_xoip: ++ if (!s->usedauth && (s->auth_method == NGX_MAIL_AUTH_PLAIN ++ || s->auth_method == NGX_MAIL_AUTH_PLAIN_IR)) ++ { ++ /* If auth plain was used, but no authz, then we must blank out ++ login+zlogin, and use dusr+zusr in authc to upstream ++ */ ++ s->dusr = s->qlogin; ++ s->zusr = s->zlogin; ++ s->qlogin.data = (u_char*)""; ++ s->qlogin.len = 0; ++ s->zlogin.data = (u_char*)""; ++ s->zlogin.len = 0; ++ s->usedauth = 1; ++ } ++ ++ if (!s->usedauth) ++ { ++ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, ++ "mail proxy send user"); ++ ++ s->connection->log->action = "sending user name to upstream"; ++ ++ line.len = sizeof("USER ") - 1 + s->qlogin.len + 2; ++ line.data = ngx_palloc(c->pool, line.len); ++ if (line.data == NULL) { ++ ngx_mail_proxy_internal_server_error(s); ++ return; ++ } ++ ++ p = ngx_cpymem(line.data, "USER ", sizeof("USER ") - 1); ++ p = ngx_cpymem(p, s->qlogin.data, s->qlogin.len); ++ *p++ = CR; *p = LF; ++ ++ s->mail_state = ngx_pop3_user; ++ } ++ else ++ { ++ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, ++ "mail proxy send auth plain"); ++ ++ if (s->auth_method == NGX_MAIL_AUTH_GSSAPI) ++ { ++ s->connection->log->action = "sending AUTH X-ZIMBRA to upstream"; ++ line.len = sizeof(pop3_authxzimbra) -1; ++ line.data = pop3_authxzimbra; ++ } ++ else ++ { ++ s->connection->log->action = "sending AUTH PLAIN to upstream"; ++ line.len = sizeof(pop3_authplain) - 1; ++ line.data = pop3_authplain; ++ } ++ ++ s->mail_state = ngx_pop3_auth_plain; ++ } + +- s->mail_state = ngx_pop3_user; + break; + + case ngx_pop3_user: +@@ -315,27 +540,67 @@ + break; + + case ngx_pop3_passwd: +- s->connection->read->handler = ngx_mail_proxy_handler; +- s->connection->write->handler = ngx_mail_proxy_handler; +- rev->handler = ngx_mail_proxy_handler; +- c->write->handler = ngx_mail_proxy_handler; ++ case ngx_pop3_auth_plain_response: ++ ngx_mail_proxy_throttle_pop3(rev); ++ return; + +- pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module); +- ngx_add_timer(s->connection->read, pcf->timeout); +- ngx_del_timer(c->read); ++ case ngx_pop3_auth_plain: ++ if (s->auth_method == NGX_MAIL_AUTH_GSSAPI) { ++ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, ++ "mail proxy send AUTH X-ZIMBRA response"); ++ s->connection->log->action = "sending AUTH X-ZIMBRA response to upstream"; ++ } ++ else { ++ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, ++ "mail proxy send AUTH PLAIN response"); ++ s->connection->log->action = "sending AUTH PLAIN response to upstream"; ++ } + +- c->log->action = NULL; +- ngx_log_error(NGX_LOG_INFO, c->log, 0, "client logged in"); + +- if (s->buffer->pos < s->buffer->last +- || s->connection->read->ready) +- { +- ngx_post_event(c->write, &ngx_posted_events); ++ ap.len = s->qlogin.len + 1 + ++ s->dusr.len + 1 + ++ s->dpasswd.len; ++ ap.data = ngx_palloc (c->pool, ap.len); ++ ++ if (ap.data == NULL) { ++ ngx_mail_proxy_internal_server_error(s); ++ return; + } + +- ngx_mail_proxy_handler(s->connection->write); ++ ngx_memcpy (ap.data, s->qlogin.data, s->qlogin.len); ++ ngx_memcpy (ap.data + s->qlogin.len, "\x0", 1); ++ ngx_memcpy (ap.data + s->qlogin.len + 1, ++ s->dusr.data, ++ s->dusr.len); ++ ngx_memcpy (ap.data + s->qlogin.len + 1 + s->dusr.len, ++ "\x0",1); ++ ngx_memcpy (ap.data + s->qlogin.len+1 + s->dusr.len + 1, ++ s->dpasswd.data, ++ s->dpasswd.len); + +- return; ++ ap64.len = ngx_base64_encoded_length(ap.len); ++ ap64.data = ngx_palloc(c->pool, ap64.len); ++ ++ if (ap64.data == NULL) { ++ ngx_mail_proxy_internal_server_error(s); ++ return; ++ } ++ ++ ngx_encode_base64 (&ap64, &ap); ++ ++ line.len = ap64.len + sizeof(CRLF) -1; ++ line.data = ngx_palloc (c->pool, line.len); ++ ++ if (line.data == NULL) { ++ ngx_mail_proxy_internal_server_error(s); ++ return; ++ } ++ ++ ngx_memcpy (line.data, ap64.data, ap64.len); ++ ngx_memcpy (line.data + ap64.len, CRLF, sizeof (CRLF) -1); ++ ++ s->mail_state = ngx_pop3_auth_plain_response; ++ break; + + default: + #if (NGX_SUPPRESS_WARN) +@@ -368,16 +633,33 @@ + { + u_char *p; + ngx_int_t rc; +- ngx_str_t line; ++ ngx_uint_t family; ++ size_t len; ++ ngx_str_t line, proxy_ip; + ngx_connection_t *c; + ngx_mail_session_t *s; + ngx_mail_proxy_conf_t *pcf; ++ struct sockaddr_in *sin; ++ ++#if (NGX_HAVE_INET6) ++ struct sockaddr_in6 *sin6; ++#endif ++ ++ ngx_mail_core_srv_conf_t *cscf; ++ ngx_mail_imap_srv_conf_t *iscf; ++ ngx_str_t challenge; ++ ngx_str_t ql, qp, login, tmp; ++ socklen_t socklen; ++ u_char sockaddr[NGX_SOCKADDRLEN]; + + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, + "mail proxy imap auth handler"); + + c = rev->data; + s = c->data; ++ pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module); ++ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); ++ iscf = ngx_mail_get_module_srv_conf(s, ngx_mail_imap_module); + + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, +@@ -409,32 +691,268 @@ + return; + } + ++ if (rc == NGX_ABORT) { ++ /* NGX_ABORT is borrowed to indicate nginx should send ++ * something coming from upstream directly. But don't return ++ * just yet as we need to replay all other responses from ++ * the upstream as well ++ */ ++ if (s->connection->send(s->connection, s->out.data, s->out.len) < ++ (ssize_t) s->out.len) { ++ ngx_mail_proxy_internal_server_error(s); ++ return; ++ } ++ s->proxy->buffer->pos = s->proxy->buffer->start; ++ s->proxy->buffer->last = s->proxy->buffer->start; ++ } ++ + if (rc == NGX_ERROR) { +- ngx_mail_proxy_upstream_error(s); +- return; ++ if (s->mail_state == ngx_imap_passwd || ++ s->mail_state == ngx_imap_auth_plain_ir ++ ) ++ { ++ if(s->proxy->upstream.connection->read->timer_set) { ++ ngx_del_timer(s->proxy->upstream.connection->read); ++ } ++ ++ ngx_add_timer(s->connection->read,cscf->auth_wait_intvl); ++ s->connection->read->handler = ngx_mail_proxy_auth_sleep_handler; ++ return; ++ } ++ else ++ { ++ ngx_mail_proxy_upstream_error(s); ++ return; ++ } ++ } ++ ++ if (s->mail_state == ngx_imap_start) ++ { ++ // bug 23349 -- conditionally (not always) issue imap id command ++ if (pcf->issue_imap_id == 0) { ++ s->mail_state = ngx_imap_id; ++ } + } + + switch (s->mail_state) { + + case ngx_imap_start: + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, +- "mail proxy send login"); ++ "mail proxy send id"); ++ ++ s->connection->log->action = ++ "sending IMAP ID extension command to upstream"; ++ ++ /* Bug 13325 - The upstream server needs to record the IP of the ++ original downstream client that connected, not just the proxy's IP ++ To pass on the information about the downstream client, we use the ++ IMAP ID extension command (rfc 2971) + +- s->connection->log->action = "sending LOGIN command to upstream"; ++ Currently both ipv4 and ipv6 (bug 56383) are supported ++ */ ++ family = s->connection->sockaddr->sa_family; ++ if (family == AF_INET) { ++ len = NGX_INET_ADDRSTRLEN; ++ sin = (struct sockaddr_in *) s->connection->sockaddr; ++ ++#if (NGX_HAVE_INET6) ++ } else { /* AF_INET6 */ ++ len = NGX_INET6_ADDRSTRLEN; ++ sin6 = (struct sockaddr_in6 *) s->connection->sockaddr; ++#endif ++ ++ } ++ ++ line.len = s->tag.len + 1 /* for space */ + ++ sizeof ("ID (\"X-ORIGINATING-IP\" \"") - 1 + ++ len + ++ sizeof ("\" \"name\" \"") - 1 + ++ iscf->ua_name.len + ++ sizeof("\" \"version\" \"") - 1 + ++ iscf->ua_version.len + ++ sizeof ("\" \"X-VIA\" \"(" NGINX_VER ")\")"CRLF) - 1; ++ ngx_memzero(sockaddr, NGX_SOCKADDRLEN); ++ socklen = NGX_SOCKADDRLEN; ++ if(getsockname(s->connection->fd, (struct sockaddr*)sockaddr, &socklen) < 0) { ++ ngx_log_error(NGX_LOG_WARN, s->connection->log, errno, "can't get proxy's IP address"); ++ } ++ ++ if(((struct sockaddr *)sockaddr)->sa_family == AF_INET) { ++ proxy_ip.len = NGX_INET_ADDRSTRLEN; ++ } else { ++ proxy_ip.len = NGX_INET6_ADDRSTRLEN; ++ } ++ proxy_ip.data = ngx_palloc(c->pool, proxy_ip.len); ++ proxy_ip.len = ngx_sock_ntop((struct sockaddr*)sockaddr, socklen, proxy_ip.data, proxy_ip.len, 0); ++ line.len += proxy_ip.len; ++ ++ if (s->id_name.len > 0) { ++ line.len += (s->id_name.len + sizeof(",") - 1); ++ if (s->id_version.len > 0) { ++ line.len += (s->id_version.len + sizeof("/") - 1); ++ } ++ } ++ ++ line.data = ngx_palloc(c->pool, line.len); + +- line.len = s->tag.len + sizeof("LOGIN ") - 1 +- + 1 + NGX_SIZE_T_LEN + 1 + 2; +- line.data = ngx_pnalloc(c->pool, line.len); + if (line.data == NULL) { + ngx_mail_proxy_internal_server_error(s); + return; + } + +- line.len = ngx_sprintf(line.data, "%VLOGIN {%uz}" CRLF, +- &s->tag, s->login.len) +- - line.data; ++ p = ngx_sprintf (line.data, "%V ID (\"X-ORIGINATING-IP\" \"", &s->tag); ++ tmp.data = p; ++ ++ if (family == AF_INET) { ++ len = ngx_inet_ntop (family, &(sin->sin_addr.s_addr), p, ++ line.data + line.len - p); ++#if (NGX_HAVE_INET6) ++ } else { /* AF_INET6 */ ++ len = ngx_inet_ntop (family, &(sin6->sin6_addr.s6_addr), ++ p, line.data + line.len - p); ++#endif ++ } ++ ++ p += len; ++ tmp.len = p - tmp.data; ++ ++ p = ngx_sprintf(p, "\" \"name\" \"%V\" \"version\" \"%V\" \"X-VIA\" \"", ++ &iscf->ua_name, &iscf->ua_version); ++ if (s->id_name.len > 0) { ++ p = ngx_cpymem(p, s->id_name.data, s->id_name.len); ++ if (s->id_version.len > 0) { ++ *p++ = '/'; ++ p = ngx_cpymem(p, s->id_version.data, s->id_version.len); ++ } ++ *p++ = ','; ++ } ++ p = ngx_sprintf(p, "%V(" NGINX_VER ")\")"CRLF, &proxy_ip); ++ ++ line.len = p - line.data; ++ ++ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, rev->log, 0, ++ "sending IMAP ID command %V", ++ &line); ++ ++ s->mail_state = ngx_imap_id; + +- s->mail_state = ngx_imap_login; ++ break; ++ ++ case ngx_imap_id: ++ /* If the downstream client has logged in with a sasl mechanism ++ that does not use clear-text passwords, we do not have the ++ end user credentials to log in to the upstream server, therefore ++ in this case, we need to log in with the master username and ++ master password, using auth plain to the upstream server ++ */ ++ ++ if (!s->usedauth && (s->auth_method == NGX_MAIL_AUTH_PLAIN ++ || s->auth_method == NGX_MAIL_AUTH_PLAIN_IR)) { ++ /* If auth plain was used, but no authz, then we must blank out ++ login+zlogin, and use dusr+zusr in authc to upstream ++ */ ++ s->dusr = s->qlogin; ++ s->zusr = s->zlogin; ++ s->qlogin.data = (u_char*)""; ++ s->qlogin.len = 0; ++ s->zlogin.data = (u_char*)""; ++ s->zlogin.len = 0; ++ s->usedauth = 1; ++ } ++ ++ if (!s->usedauth) ++ { ++ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, ++ "no delegated auth, login to upstream using LOGIN"); ++ ++ s->connection->log->action = "sending LOGIN command to upstream"; ++ ++ /* LOGIN with literal or quoted strings (imap_literalauth) */ ++ if (iscf->literalauth) ++ { ++ line.len = s->tag.len + 1 + sizeof("LOGIN ") - 1 ++ + 1 + NGX_SIZE_T_LEN + 1 + 2; ++ line.data = ngx_palloc(c->pool, line.len); ++ if (line.data == NULL) { ++ ngx_mail_proxy_internal_server_error(s); ++ return; ++ } ++ ++ line.len = ngx_sprintf(line.data, "%V LOGIN {%uz}" CRLF, ++ &s->tag, s->qlogin.len + s->zlogin.len) ++ - line.data; ++ ++ s->mail_state = ngx_imap_login; ++ } ++ else ++ { ++ /* merge back zimbra extensions (/tb|/wm|/ni), if any */ ++ ++ login.data = ngx_palloc(c->pool, s->qlogin.len + s->zlogin.len); ++ if (login.data == NULL) { ++ login = s->qlogin; ++ } else { ++ login.len = s->qlogin.len + s->zlogin.len; ++ ngx_memcpy(login.data, s->qlogin.data, s->qlogin.len); ++ ngx_memcpy(login.data + s->qlogin.len, s->zlogin.data, ++ s->zlogin.len); ++ } ++ ql = ngx_imap_quote_string(c->pool, &login); ++ qp = ngx_imap_quote_string(c->pool, &s->passwd); ++ ++ line.len = s->tag.len + 1 /* for space */ + sizeof("LOGIN ") - 1 + ++ ql.len + ++ sizeof(" ") - 1 + ++ qp.len + ++ sizeof(CRLF) - 1; ++ ++ line.data = ngx_palloc(c->pool,line.len); ++ if (line.data == NULL) { ++ ngx_mail_proxy_internal_server_error(s); ++ return; ++ } ++ ++ ngx_sprintf(line.data,"%V LOGIN %V %V" CRLF, &s->tag, &ql, &qp); ++ ++ s->mail_state = ngx_imap_passwd; ++ } ++ } ++ else ++ { ++ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, ++ "Using delegated auth to log in to upstream"); ++ ++ if (s->auth_method == NGX_MAIL_AUTH_GSSAPI) ++ { ++ s->connection->log->action = "sending AUTHENTICATE X-ZIMBRA to upstream"; ++ ++ line.len = s->tag.len + 1 /* for space */ + sizeof ("AUTHENTICATE X-ZIMBRA") - 1 + 2; ++ line.data = ngx_palloc (c->pool, line.len); ++ ++ if (line.data == NULL) { ++ ngx_mail_proxy_internal_server_error(s); ++ return; ++ } ++ ++ ngx_snprintf (line.data, line.len, "%V AUTHENTICATE X-ZIMBRA" CRLF, &s->tag); ++ } ++ else ++ { ++ s->connection->log->action = "sending AUTHENTICATE PLAIN to upstream"; ++ ++ line.len = s->tag.len + 1 /* for space */ + sizeof ("AUTHENTICATE PLAIN") - 1 + 2; ++ line.data = ngx_palloc (c->pool, line.len); ++ ++ if (line.data == NULL) { ++ ngx_mail_proxy_internal_server_error(s); ++ return; ++ } ++ ++ ngx_snprintf (line.data, line.len, "%V AUTHENTICATE PLAIN" CRLF, &s->tag); ++ } ++ ++ s->mail_state = ngx_imap_auth_plain; ++ } + break; + + case ngx_imap_login: +@@ -442,15 +960,15 @@ + + s->connection->log->action = "sending user name to upstream"; + +- line.len = s->login.len + 1 + 1 + NGX_SIZE_T_LEN + 1 + 2; ++ line.len = s->qlogin.len + 1 + 1 + NGX_SIZE_T_LEN + 1 + 2; + line.data = ngx_pnalloc(c->pool, line.len); + if (line.data == NULL) { + ngx_mail_proxy_internal_server_error(s); + return; + } + +- line.len = ngx_sprintf(line.data, "%V {%uz}" CRLF, +- &s->login, s->passwd.len) ++ line.len = ngx_sprintf(line.data, "%V%V {%uz}" CRLF, ++ &s->qlogin, &s->zlogin, s->passwd.len) + - line.data; + + s->mail_state = ngx_imap_user; +@@ -476,31 +994,67 @@ + break; + + case ngx_imap_passwd: +- s->connection->read->handler = ngx_mail_proxy_handler; +- s->connection->write->handler = ngx_mail_proxy_handler; +- rev->handler = ngx_mail_proxy_handler; +- c->write->handler = ngx_mail_proxy_handler; ++ case ngx_imap_auth_plain_ir: ++ ngx_mail_proxy_throttle_imap(rev); ++ return; + +- pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module); +- ngx_add_timer(s->connection->read, pcf->timeout); +- ngx_del_timer(c->read); ++ case ngx_imap_auth_plain: ++ /* RFC 4616 ++ message = [authzid] UTF8NUL authcid UTF8NUL passwd ++ */ + +- c->log->action = NULL; +- ngx_log_error(NGX_LOG_INFO, c->log, 0, "client logged in"); ++ challenge.len = s->qlogin.len + s->zlogin.len + 1 + ++ s->dusr.len + s->zusr.len + 1 + ++ s->dpasswd.len; ++ line.len = ngx_base64_encoded_length (challenge.len); + +- if (s->buffer->pos < s->buffer->last +- || s->connection->read->ready) +- { +- ngx_post_event(c->write, &ngx_posted_events); ++ challenge.data = ngx_palloc (c->pool, challenge.len); ++ if (challenge.data == NULL) { ++ ngx_mail_proxy_internal_server_error(s); ++ return; + } + +- ngx_mail_proxy_handler(s->connection->write); ++ line.data = ngx_palloc (c->pool, line.len + 2); /* +2 for CRLF */ ++ if (line.data == NULL) { ++ ngx_mail_proxy_internal_server_error(s); ++ return; ++ } + +- return; ++ /* construct the base64 challenge for auth-plain login to upstream */ ++ ++ p = challenge.data; ++ p = ngx_cpymem(p, s->qlogin.data, s->qlogin.len); /* authz */ ++ p = ngx_cpymem(p, s->zlogin.data, s->zlogin.len); /* [/wm|/ni|/tb] */ ++ *p++ = '\x0'; ++ p = ngx_cpymem(p, s->dusr.data, s->dusr.len); /* authc */ ++ p = ngx_cpymem(p, s->zusr.data, s->zusr.len); /* [/wm|/ni|/tb] for authc */ ++ *p++ = '\x0'; ++ p = ngx_cpymem(p, s->dpasswd.data, s->dpasswd.len); /* password */ ++ ++ ngx_encode_base64 (&line, &challenge); ++ ++ if (s->auth_method == NGX_MAIL_AUTH_GSSAPI) { ++ ngx_log_debug0 (NGX_LOG_DEBUG_MAIL, rev->log, 0, ++ "sending AUTH X-ZIMBRA challenge to upstream" ++ ); ++ } ++ else { ++ ngx_log_debug0 (NGX_LOG_DEBUG_MAIL, rev->log, 0, ++ "sending AUTH PLAIN challenge to upstream" ++ ); ++ } ++ ++ ngx_memcpy (line.data + line.len, CRLF, 2); ++ line.len += 2; ++ ++ s->mail_state = ngx_imap_auth_plain_ir; ++ ++ break; + + default: + #if (NGX_SUPPRESS_WARN) +- ngx_str_null(&line); ++ line.len = 0; ++ line.data = NULL; + #endif + break; + } +@@ -958,6 +1512,33 @@ + ssize_t n; + ngx_buf_t *b; + ngx_mail_proxy_conf_t *pcf; ++ int expect_chunk; ++ ++ /* (bug 21323) ++ ++ during the authentication phase, the upstream imap server can include an ++ optional untagged response to the LOGIN or to the ID command ++ if the `nio_imap_enabled' parameter is set to true, then the upstream ++ server's response is split into two tcp packets, the first chunk ++ contains the untagged response, and the next chunk contains the tagged ++ result ++ ++ in this function, nginx previously expected all the response to arrive ++ in a single chunk, which is not true in this case, and therefore, we ++ must maintain a state variable (expect_chunk), which has boolean ++ significance -- if the variable starts off as false, and it is marked ++ true when we encounter an untagged response ++ ++ when the tagged response eventually arrives (the tag identifier is at ++ the start of a new line of response), then this variable is signaled ++ to false again, which means that we don't expect another chunk of ++ data ++ ++ if we finish processing the data returned by the call to recv(), and ++ if expect_chunk is set to true, then we return NGX_AGAIN, which will ++ cause this handler to get invoked again, when the next chunk of data ++ becomes available ++ */ + + s->connection->log->action = "reading response from upstream"; + +@@ -976,7 +1557,12 @@ + + b->last += n; + +- if (b->last - b->pos < 4) { ++ /* if (b->last - b->pos < 5) { ++ return NGX_AGAIN; ++ } */ /* -- This won't work with zimbra */ ++ ++ if ((b->last - b->pos < 5) && (b->pos) && (*b->pos != '+')) ++ { + return NGX_AGAIN; + } + +@@ -992,13 +1578,20 @@ + return NGX_AGAIN; + } + ++ expect_chunk = 0; + p = b->pos; + + switch (s->protocol) { + + case NGX_MAIL_POP3_PROTOCOL: +- if (p[0] == '+' && p[1] == 'O' && p[2] == 'K') { +- return NGX_OK; ++ if (state == ngx_pop3_auth_plain) { ++ if (p[0] == '+' && p[1] == ' ') { ++ return NGX_OK; ++ } ++ } else { ++ if (p[0] == '+' && p[1] == 'O' && p[2] == 'K') { ++ return NGX_OK; ++ } + } + break; + +@@ -1006,23 +1599,86 @@ + switch (state) { + + case ngx_imap_start: +- if (p[0] == '*' && p[1] == ' ' && p[2] == 'O' && p[3] == 'K') { ++ if (ngx_strncmp(p, "* OK", sizeof("* OK") - 1) == 0) { ++ char *k = strchr((char *)p, '\n'); ++ if (k != NULL) { ++ /* bug 79531 identify the [ALERT] msg from upstream */ ++ if (ngx_strncmp(k+1, "* OK [ALERT]", sizeof("* OK [ALERT]") - 1) == 0) { ++ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, s->connection->log, 0, ++ "ALERT message recvd from upstream"); ++ s->out.len = b->last - b->pos; ++ s->out.data = b->pos; ++ return NGX_ABORT; ++ } ++ } + return NGX_OK; + } + break; + + case ngx_imap_login: + case ngx_imap_user: ++ case ngx_imap_auth_plain: + if (p[0] == '+') { + return NGX_OK; + } + break; + ++ case ngx_imap_id: ++ /* bug 68678 ignore the untagged error for the ID command */ ++ if (ngx_strncmp(p, "* BAD", sizeof("* BAD") - 1) == 0) { ++ return NGX_OK; ++ } ++ ++ /* fall through */ + case ngx_imap_passwd: +- if (ngx_strncmp(p, s->tag.data, s->tag.len) == 0) { +- p += s->tag.len; +- if (p[0] == 'O' && p[1] == 'K') { +- return NGX_OK; ++ case ngx_imap_auth_plain_ir: ++ /* Consume (optional, untagged response, plus) tagged response to ++ IMAP command previously issued ++ As the switch case indicates, we are prepared to handle this ++ after sending the ID command, or after sending the password to ++ the upstream imap server ++ In the former case, the IMAP server MAY optionally include an ++ untagged ID repsonse (RFC 2971, section 3.1) ++ In the latter case, the IMAP server MAY include a CAPABILITY ++ response code in the tagged OK response to a successful LOGIN ++ command (RFC 3501, section 6.2.3) ++ */ ++ ++ while ((p != NULL) && (p < b->last)) ++ { ++ if (ngx_strncmp(p, s->tag.data, s->tag.len) == 0) ++ { ++ /* This line is the tagged response */ ++ expect_chunk = 0; ++ ++ p += s->tag.len + 1 /*for space*/; ++ if (p[0] == 'O' && p[1] == 'K') ++ { ++ return NGX_OK; ++ } ++ else ++ { ++ break; ++ } ++ } ++ else ++ { ++ /* this line is any optional untagged response ++ */ ++ p = (u_char *)strstr ((char *)p, (char *)"\n"); ++ if (!p) ++ { ++ /* if we don't find the newline, it indicates an ++ invalid response ++ */ ++ break; ++ } ++ else ++ { ++ /* the first (possible) chunk has been read */ ++ expect_chunk = 1; ++ ++p; ++ } + } + } + break; +@@ -1090,6 +1746,11 @@ + break; + } + ++ if (expect_chunk == 1) { ++ return NGX_AGAIN; ++ } ++ ++ s->sendquitmsg = 1; + pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module); + + if (pcf->pass_error_message == 0) { +@@ -1143,6 +1804,7 @@ + "upstream timed out"); + } + ++ s->sendquitmsg = 1; + ngx_mail_proxy_close_session(s); + return; + } +@@ -1220,7 +1882,14 @@ + + n = src->recv(src, b->last, size); + +- if (n == NGX_AGAIN || n == 0) { ++ if (n == NGX_AGAIN) { ++ break; ++ } ++ ++ if (n == 0) { ++ if (c == s->connection && !ev->write) { ++ s->sendquitmsg = 1; ++ } + break; + } + +@@ -1283,6 +1952,10 @@ + } + + ++/* error out of an established proxy session ++ - close the upstream connection ++ - close the downstream connection after sending any pending data ++ */ + static void + ngx_mail_proxy_upstream_error(ngx_mail_session_t *s) + { +@@ -1291,6 +1964,14 @@ + "close mail proxy connection: %d", + s->proxy->upstream.connection->fd); + ++#if (NGX_MAIL_SSL) ++ ++ if (s->proxy->upstream.connection->ssl) { ++ s->proxy->upstream.connection->ssl->no_wait_shutdown = 1; ++ ngx_ssl_shutdown(s->proxy->upstream.connection); ++ } ++#endif ++ + ngx_close_connection(s->proxy->upstream.connection); + } + +@@ -1312,6 +1993,14 @@ + "close mail proxy connection: %d", + s->proxy->upstream.connection->fd); + ++#if (NGX_MAIL_SSL) ++ ++ if (s->proxy->upstream.connection->ssl) { ++ s->proxy->upstream.connection->ssl->no_wait_shutdown = 1; ++ ngx_ssl_shutdown(s->proxy->upstream.connection); ++ } ++#endif ++ + ngx_close_connection(s->proxy->upstream.connection); + } + +@@ -1327,10 +2016,22 @@ + "close mail proxy connection: %d", + s->proxy->upstream.connection->fd); + ++#if (NGX_MAIL_SSL) ++ ++ if (s->proxy->upstream.connection->ssl) { ++ s->proxy->upstream.connection->ssl->no_wait_shutdown = 1; ++ ngx_ssl_shutdown(s->proxy->upstream.connection); ++ } ++#endif ++ + ngx_close_connection(s->proxy->upstream.connection); + } + +- ngx_mail_close_connection(s->connection); ++ if (s->sendquitmsg) { ++ ngx_mail_end_session(s); ++ } else { ++ ngx_mail_close_connection(s->connection); ++ } + } + + +@@ -1346,11 +2047,16 @@ + + pcf->enable = NGX_CONF_UNSET; + pcf->pass_error_message = NGX_CONF_UNSET; ++ pcf->issue_pop3_xoip = NGX_CONF_UNSET; ++ pcf->issue_imap_id = NGX_CONF_UNSET; + pcf->xclient = NGX_CONF_UNSET; + pcf->smtp_auth = NGX_CONF_UNSET; + pcf->proxy_protocol = NGX_CONF_UNSET; + pcf->buffer_size = NGX_CONF_UNSET_SIZE; ++ pcf->ctimeout = NGX_CONF_UNSET_MSEC; + pcf->timeout = NGX_CONF_UNSET_MSEC; ++ pcf->proxy_ssl = NGX_CONF_UNSET; ++ pcf->ssl = NGX_CONF_UNSET_PTR; + + return pcf; + } +@@ -1364,12 +2070,500 @@ + + ngx_conf_merge_value(conf->enable, prev->enable, 0); + ngx_conf_merge_value(conf->pass_error_message, prev->pass_error_message, 0); ++ ngx_conf_merge_value(conf->issue_pop3_xoip, prev->issue_pop3_xoip, 1); ++ ngx_conf_merge_value(conf->issue_imap_id, prev->issue_imap_id, 1); + ngx_conf_merge_value(conf->xclient, prev->xclient, 1); ++ ngx_conf_merge_value(conf->proxy_ssl, prev->proxy_ssl, 0); ++ ngx_conf_merge_ptr_value(conf->ssl, prev->ssl, NULL); + ngx_conf_merge_value(conf->smtp_auth, prev->smtp_auth, 0); + ngx_conf_merge_value(conf->proxy_protocol, prev->proxy_protocol, 0); + ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size, + (size_t) ngx_pagesize); ++ ngx_conf_merge_msec_value(conf->ctimeout, prev->ctimeout, 2 * 60000); + ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 24 * 60 * 60000); + + return NGX_CONF_OK; + } ++ ++#if (NGX_MAIL_SSL) ++ ++static char * ++ngx_mail_proxy_ssl(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ++ ++ ngx_pool_cleanup_t *cln; ++ char *rc; ++ ngx_mail_proxy_conf_t *pcf; ++ ++ rc = ngx_conf_set_flag_slot(cf, cmd, conf); ++ if (rc != NGX_CONF_OK) { ++ return rc; ++ } ++ ++ pcf = (ngx_mail_proxy_conf_t *)conf; ++ ++ if (!pcf->proxy_ssl) { ++ return NGX_CONF_OK; ++ } ++ ++ pcf->ssl = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_t)); ++ ++ if (pcf->ssl == NULL) { ++ return NGX_CONF_ERROR; ++ } ++ ++ pcf->ssl->log = cf->log; ++ ++ // don't support SSLv2 anymore ++ if (ngx_ssl_create(pcf->ssl, ~(NGX_SSL_SSLv2|NGX_SSL_SSLv3), NULL) ++ != NGX_OK) { ++ return NGX_CONF_ERROR; ++ } ++ ++ cln = ngx_pool_cleanup_add(cf->pool, 0); ++ if (cln == NULL) { ++ return NGX_CONF_ERROR; ++ } ++ ++ cln->handler = ngx_ssl_cleanup_ctx; ++ cln->data = pcf->ssl; ++ ++ return NGX_CONF_OK; ++} ++ ++#endif ++/* throttle imap session if necessary (user has just finished logging in) */ ++static void ngx_mail_proxy_throttle_imap(ngx_event_t *rev) ++{ ++ throttle_callback_t *callback; ++ ngx_mail_throttle_srv_conf_t *tscf; ++ ngx_mail_proxy_conf_t *pcf; ++ ngx_mail_session_t *s; ++ ngx_connection_t *c; ++ ngx_pool_t *pool; ++ ngx_log_t *log; ++ ngx_str_t user; ++ ++ c = rev->data; ++ s = c->data; ++ pool = c->pool; ++ log = ngx_cycle->log; ++ tscf = ngx_mail_get_module_srv_conf(s, ngx_mail_throttle_module); ++ pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module); ++ ++ callback = ngx_pcalloc(pool, sizeof(throttle_callback_t)); ++ if (callback == NULL) { ++ ngx_mail_proxy_upstream_error(s); ++ return; ++ } ++ ++ callback->check_only = 0; /* increment the counter */ ++ callback->session = s; ++ callback->connection = c; ++ callback->rev = rev; ++ callback->config = pcf; ++ callback->log = log; ++ callback->pool = pool; ++ callback->on_allow = ngx_mail_proxy_allow_imap; ++ callback->on_deny = ngx_mail_proxy_choke_imap; ++ ++ if (tscf->mail_login_user_max == 0) { ++ callback->on_allow(callback); ++ } else { ++ user = s->qlogin; ++ if ((user.len == 0) && (s->auth_method == NGX_MAIL_AUTH_PLAIN ++ || s->auth_method == NGX_MAIL_AUTH_PLAIN_IR)) { ++ user = s->dusr; ++ } ++ ngx_mail_throttle_user(user, callback); ++ } ++} ++ ++static void ngx_mail_proxy_allow_imap(throttle_callback_t *callback) ++{ ++ ngx_mail_session_t *s = callback->session; ++ ngx_event_t *rev = callback->rev; ++ ngx_connection_t *c = callback->connection; ++ ngx_mail_proxy_conf_t *pcf = callback->config; ++ ++ s->connection->read->handler = ngx_mail_proxy_handler; ++ s->connection->write->handler = ngx_mail_proxy_handler; ++ rev->handler = ngx_mail_proxy_handler; ++ c->write->handler = ngx_mail_proxy_handler; ++ ++ ngx_add_timer(s->connection->read, pcf->timeout); ++ if (c->read->timer_set) { ++ ngx_del_timer(c->read); ++ } ++ ++ c->log->action = NULL; ++ ngx_log_error(NGX_LOG_INFO, c->log, 0, "client logged in"); ++ ++ ngx_mail_proxy_handler(s->connection->write); ++} ++ ++static void ngx_mail_proxy_choke_imap(throttle_callback_t *callback) ++{ ++ ngx_mail_session_t *s = callback->session; ++ ngx_mail_proxy_choke_session(s); ++} ++ ++/* throttle pop3 session if necessary (user has just finished logging in) */ ++static void ngx_mail_proxy_throttle_pop3(ngx_event_t *rev) ++{ ++ throttle_callback_t *callback; ++ ngx_mail_throttle_srv_conf_t *tscf; ++ ngx_mail_proxy_conf_t *pcf; ++ ngx_mail_session_t *s; ++ ngx_connection_t *c; ++ ngx_pool_t *pool; ++ ngx_log_t *log; ++ ngx_str_t user; ++ ++ c = rev->data; ++ s = c->data; ++ pool = c->pool; ++ log = ngx_cycle->log; ++ tscf = ngx_mail_get_module_srv_conf(s, ngx_mail_throttle_module); ++ pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module); ++ ++ callback = ngx_pcalloc(pool, sizeof(throttle_callback_t)); ++ if (callback == NULL) { ++ ngx_mail_proxy_upstream_error(s); ++ return; ++ } ++ ++ callback->check_only = 0; /* increment the counter */ ++ callback->session = s; ++ callback->connection = c; ++ callback->rev = rev; ++ callback->config = pcf; ++ callback->log = log; ++ callback->pool = pool; ++ callback->on_allow = ngx_mail_proxy_allow_pop3; ++ callback->on_deny = ngx_mail_proxy_choke_pop3; ++ ++ if (tscf->mail_login_user_max == 0) { ++ callback->on_allow(callback); ++ } else { ++ user = s->qlogin; ++ if ((user.len == 0) && (s->auth_method == NGX_MAIL_AUTH_PLAIN ++ || s->auth_method == NGX_MAIL_AUTH_PLAIN_IR)) { ++ user = s->dusr; ++ } ++ ngx_mail_throttle_user(user, callback); ++ } ++} ++ ++static void ngx_mail_proxy_allow_pop3(throttle_callback_t *callback) ++{ ++ ngx_mail_session_t *s = callback->session; ++ ngx_event_t *rev = callback->rev; ++ ngx_connection_t *c = callback->connection; ++ ngx_mail_proxy_conf_t *pcf = callback->config; ++ ++ s->connection->read->handler = ngx_mail_proxy_handler; ++ s->connection->write->handler = ngx_mail_proxy_handler; ++ rev->handler = ngx_mail_proxy_handler; ++ c->write->handler = ngx_mail_proxy_handler; ++ ++ ngx_add_timer(s->connection->read, pcf->timeout); ++ if (c->read->timer_set) { ++ ngx_del_timer(c->read); ++ } ++ ++ c->log->action = NULL; ++ ngx_log_error(NGX_LOG_INFO, c->log, 0, "client logged in"); ++ ++ ngx_mail_proxy_handler(s->connection->write); ++} ++ ++static void ngx_mail_proxy_choke_pop3(throttle_callback_t *callback) ++{ ++ ngx_mail_session_t *s = callback->session; ++ ngx_mail_proxy_choke_session(s); ++} ++ ++static void ngx_mail_proxy_choke_session(ngx_mail_session_t *s) ++{ ++ ngx_mail_throttle_srv_conf_t *tscf; ++ ngx_str_t bye, msg, umsg; ++ ngx_pool_t *pool; ++ u_char *p; ++ ++ tscf = ngx_mail_get_module_srv_conf(s, ngx_mail_throttle_module); ++ pool = s->connection->pool; ++ msg = tscf->mail_login_user_rejectmsg; ++ ++ if (s->proxy->upstream.connection) ++ { ++ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, s->connection->log, 0, ++ "close mail proxy connection: %d", ++ s->proxy->upstream.connection->fd); ++ ++#if (NGX_MAIL_SSL) ++ ++ if (s->proxy->upstream.connection->ssl) { ++ s->proxy->upstream.connection->ssl->no_wait_shutdown = 1; ++ ngx_ssl_shutdown(s->proxy->upstream.connection); ++ } ++#endif ++ ++ ngx_close_connection(s->proxy->upstream.connection); ++ } ++ ++ if (s->protocol == NGX_MAIL_IMAP_PROTOCOL) ++ { ++ bye.len = sizeof("* BYE ") - 1 + ++ msg.len + ++ sizeof(CRLF) - 1; ++ ++ bye.data = ngx_palloc(pool, bye.len); ++ ++ if (bye.data != NULL) ++ { ++ p = bye.data; ++ p = ngx_cpymem(p,"* BYE ",sizeof("* BYE ")-1); ++ p = ngx_cpymem(p, msg.data, msg.len); ++ *p++ = CR; ++ *p++ = LF; ++ bye.len = p - bye.data; ++ } ++ else ++ { ++ bye.data = (u_char *) ("* BYE" CRLF); ++ bye.len = sizeof("* BYE" CRLF)-1; ++ } ++ ++ s->out = bye; ++ s->quit = 0; ++ ngx_mail_send(s->connection->write); ++ ++ if (s->command == NGX_IMAP_LOGIN) { ++ umsg.data = imap_login_no; ++ umsg.len = sizeof(imap_login_no) - 1; ++ } else if (s->command == NGX_IMAP_AUTHENTICATE) { ++ umsg.data = imap_auth_no; ++ umsg.len = sizeof(imap_auth_no) - 1; ++ } else { ++ umsg.data = imap_no; ++ umsg.len = sizeof(imap_no) - 1; ++ } ++ ++ s->out.len = s->tag.len + 1 /*for space*/+ umsg.len; ++ s->out.data = ngx_palloc(pool, s->out.len); ++ ++ if (s->out.data == NULL) { ++ s->out.data = (u_char*)""; ++ s->out.len = 0; ++ ngx_mail_close_connection(s->connection); ++ } else { ++ ngx_memcpy(s->out.data, s->tag.data, s->tag.len); ++ *(s->out.data + s->tag.len) = ' '; ++ ngx_memcpy(s->out.data + s->tag.len + 1, umsg.data, umsg.len); ++ s->quit = 1; ++ ngx_mail_send(s->connection->write); ++ } ++ ++ return; ++ } ++ else if (s->protocol == NGX_MAIL_POP3_PROTOCOL) ++ { ++ bye.len = sizeof("-ERR ")-1 + ++ msg.len + ++ sizeof(CRLF)-1; ++ ++ bye.data = ngx_palloc(pool,bye.len); ++ ++ if (bye.data != NULL) ++ { ++ p = bye.data; ++ p = ngx_cpymem(p,"-ERR ",sizeof("-ERR ")-1); ++ p = ngx_cpymem(p,msg.data,msg.len); ++ *p++ = CR; ++ *p++ = LF; ++ bye.len = p-bye.data; ++ } ++ else ++ { ++ bye.data = (u_char *)("-ERR" CRLF); ++ bye.len = sizeof("-ERR" CRLF)-1; ++ } ++ ++ s->out = bye; ++ s->quit = 1; ++ ngx_mail_send(s->connection->write); ++ return; ++ } ++ else ++ { ++ /* TODO ?? reject SMTP ?? */ ++ bye.data = (u_char *)""; ++ bye.len = 0; ++ s->out = bye; ++ s->quit = 1; ++ ngx_mail_send(s->connection->write); ++ return; ++ } ++} ++ ++/* Quote an IMAP string according to RFC 3501, section 9 (formal syntax) */ ++static ngx_str_t ngx_imap_quote_string(ngx_pool_t *pool, ngx_str_t *u) ++{ ++ size_t s; ++ ngx_str_t k; ++ u_char *p,*q,*r; ++ ++ s = 2; ++ q = u->data; ++ r = q + u->len; ++ ++ while (qdata; ++ r = q + u->len; ++ *p++ = '"'; ++ while (qlog, 0, ++ "mail proxy auth sleep handler"); ++ ++ c = rev->data; ++ s = c->data; ++ ++ ngx_zm_lookup_delete_cache(s->key_alias, s->key_route); ++ ++ if (rev->timedout) { ++ ngx_mail_proxy_upstream_error(s); ++ return; ++ } ++ ++ if (rev->active) { ++ if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { ++ ngx_mail_proxy_upstream_error(s); ++ } ++ } ++} ++ ++ngx_flag_t ++ngx_mail_get_proxy_ssl (ngx_mail_session_t * s) ++{ ++ ngx_mail_proxy_conf_t * pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module); ++ return pcf->proxy_ssl; ++} ++ ++#if (NGX_HTTP_SSL) ++ ++static void ++ngx_mail_proxy_ssl_init_connection(ngx_mail_session_t *s, ngx_connection_t *c) ++{ ++ ngx_int_t rc; ++ ngx_mail_proxy_conf_t *pcf; ++ ++ pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module); ++ ++ if (ngx_ssl_create_connection(pcf->ssl, c, ++ NGX_SSL_BUFFER|NGX_SSL_CLIENT) ++ != NGX_OK) ++ { ++ ngx_mail_proxy_internal_server_error(s); ++ return; ++ } ++ ++ s->connection->log->action = "SSL handshaking to upstream"; ++ ++ rc = ngx_ssl_handshake(c); ++ ++ if (rc == NGX_AGAIN) { ++ c->ssl->handler = ngx_mail_proxy_ssl_handshake; ++ return; ++ } ++ ++ ngx_mail_proxy_ssl_handshake(c); ++} ++ ++ ++static void ++ngx_mail_proxy_ssl_handshake(ngx_connection_t *c) ++{ ++ ngx_mail_session_t *s; ++ s = c->data; ++ ++ if (c->ssl->handshaked) { ++ ++ c->write->handler = ngx_mail_proxy_write_handler; ++ switch (s->protocol) { ++ ++ case NGX_MAIL_POP3_PROTOCOL: ++ c->read->handler = ngx_mail_proxy_pop3_handler; ++ s->mail_state = ngx_pop3_start; ++ break; ++ ++ case NGX_MAIL_IMAP_PROTOCOL: ++ c->read->handler = ngx_mail_proxy_imap_handler; ++ s->mail_state = ngx_imap_start; ++ break; ++ ++ default: /* NGX_MAIL_SMTP_PROTOCOL */ ++ c->read->handler = ngx_mail_proxy_smtp_handler; ++ s->mail_state = ngx_smtp_start; ++ break; ++ } ++ ++ /* server might have send the intial welcome msg */ ++ c->read->handler(c->read); ++ } else { ++ /* when handshake fails, we should close the session */ ++ ngx_mail_proxy_upstream_error(s); ++ } ++} ++ ++/** TODO support ssl session reuse ++void ++ngx_mail_proxy_set_session (ngx_peer_connection_t *pc, ngx_mail_proxy_ctx_t *ctx) ++{ ++ ngx_ssl_session_t *ssl_session; ++ ngx_int_t rc; ++ ++ ssl_session = peer->ssl_session; ++ ++ rc = ngx_ssl_set_session(pc->connection, ssl_session); ++ ++ ngx_log_debug2(NGX_LOG_DEBUG_MAIL, pc->log, 0, ++ "set session: %p:%d", ++ ssl_session, ssl_session ? ssl_session->references : 0); ++ return; ++} ++ ++void ++ngx_mail_proxy_save_session (ngx_peer_connection_t *pc) ++{ ++ ++} ++*/ ++ ++#endif ++ diff --git a/thirdparty/nginx/patches/nginx_src_mail_ngx_mail_smtp_module.patch b/thirdparty/nginx/patches/nginx_src_mail_ngx_mail_smtp_module.patch new file mode 100644 index 000000000..1df46d629 --- /dev/null +++ b/thirdparty/nginx/patches/nginx_src_mail_ngx_mail_smtp_module.patch @@ -0,0 +1,14 @@ +--- nginx/src/mail/ngx_mail_smtp_module.c 2023-03-07 16:26:44.739565600 +0530 ++++ nginx/src/mail/ngx_mail_smtp_module.c 2023-09-14 18:47:12.670786200 +0530 +@@ -50,7 +50,10 @@ + + ngx_string("451 4.3.2 Internal server error" CRLF), + ngx_string("421 4.7.1 SSL certificate error" CRLF), +- ngx_string("421 4.7.1 No required SSL certificate" CRLF) ++ // Zimbra customizations start here (Jira Tickets: ) ++ ngx_string("421 4.7.1 No required SSL certificate" CRLF), ++ ngx_string("") /* zimbra add a quite_msg field in mail protocol */ ++ // Zimbra customizations end here + }; + + diff --git a/thirdparty/nginx/zimbra-nginx/debian/changelog b/thirdparty/nginx/zimbra-nginx/debian/changelog index 8a6c12723..7025a45ac 100644 --- a/thirdparty/nginx/zimbra-nginx/debian/changelog +++ b/thirdparty/nginx/zimbra-nginx/debian/changelog @@ -1,3 +1,9 @@ +zimbra-nginx (VERSION-1zimbra8.8b4ZAPPEND) unstable; urgency=medium + + * Upgraded zimbra-nginx to 1.24.0 + + -- Zimbra Packaging Services Mon, 05 Feb 2024 05:12:55 +0000 + zimbra-nginx (VERSION-1zimbra8.8b4ZAPPEND) unstable; urgency=medium * ZBUG-3355, Upgraded OpenSSL to 3.0.9 diff --git a/thirdparty/nginx/zimbra-nginx/debian/patches/series b/thirdparty/nginx/zimbra-nginx/debian/patches/series new file mode 100644 index 000000000..1f028f9e1 --- /dev/null +++ b/thirdparty/nginx/zimbra-nginx/debian/patches/series @@ -0,0 +1,23 @@ +nginx_auto_cc.patch +nginx_auto_lib.patch +nginx_auto_modules.patch +nginx_auto_options.patch +nginx_auto_sources.patch +nginx_README.patch +nginx_src_core.patch +nginx_src_event.patch +nginx_src_http.patch +nginx_src_mail_ngx_mail.patch +nginx_src_mail_ngx_mail_auth_http_module.patch +nginx_src_mail_ngx_mail_core_module.patch +nginx_src_mail_ngx_mail_handler.patch +nginx_src_mail_ngx_mail_imap_handler.patch +nginx_src_mail_ngx_mail_imap_module_c.patch +nginx_src_mail_ngx_mail_imap_module_h.patch +nginx_src_mail_ngx_mail_parse.patch +nginx_src_mail_ngx_mail_pop3_handler.patch +nginx_src_mail_ngx_mail_pop3_module_c.patch +nginx_src_mail_ngx_mail_pop3_module_h.patch +nginx_src_mail_ngx_mail_proxy_module.patch +nginx_src_mail_ngx_mail_smtp_module.patch +nginx_docs.patch \ No newline at end of file diff --git a/thirdparty/nginx/zimbra-nginx/debian/rules b/thirdparty/nginx/zimbra-nginx/debian/rules index 566d0cd03..eea5712f8 100755 --- a/thirdparty/nginx/zimbra-nginx/debian/rules +++ b/thirdparty/nginx/zimbra-nginx/debian/rules @@ -1,5 +1,6 @@ #!/usr/bin/make -f export DEB_BUILD_OPTIONS=nocheck +export DH_VERBOSE=1 # Ensure rpath is set correctly export DEB_LDFLAGS_MAINT_APPEND=-Wl,-rpath,OZCL @@ -9,8 +10,10 @@ export DEB_CFLAGS_MAINT_APPEND=-O0 %: dh $@ +override_dh_auto_clean: + override_dh_auto_configure: - ./auto/configure --prefix=OZC \ + ./configure --prefix=OZC \ --with-cc-opt="-g -IOZCI" \ --with-ld-opt="-Wl,-rpath,OZCL -LOZCL" \ --with-debug \ diff --git a/thirdparty/nginx/zimbra-nginx/debian/source/format b/thirdparty/nginx/zimbra-nginx/debian/source/format index 89ae9db8f..163aaf8d8 100644 --- a/thirdparty/nginx/zimbra-nginx/debian/source/format +++ b/thirdparty/nginx/zimbra-nginx/debian/source/format @@ -1 +1 @@ -3.0 (native) +3.0 (quilt) diff --git a/thirdparty/nginx/zimbra-nginx/rpm/SPECS/nginx.spec b/thirdparty/nginx/zimbra-nginx/rpm/SPECS/nginx.spec index 706338689..b5044e231 100644 --- a/thirdparty/nginx/zimbra-nginx/rpm/SPECS/nginx.spec +++ b/thirdparty/nginx/zimbra-nginx/rpm/SPECS/nginx.spec @@ -4,6 +4,30 @@ Version: VERSION Release: 1zimbra8.8b4ZAPPEND License: MIT Source: %{name}-%{version}.tar.gz +Patch0: nginx_auto_cc.patch +Patch1: nginx_auto_lib.patch +Patch2: nginx_auto_modules.patch +Patch3: nginx_auto_options.patch +Patch4: nginx_auto_sources.patch +Patch5: nginx_README.patch +Patch6: nginx_src_core.patch +Patch7: nginx_src_event.patch +Patch8: nginx_src_http.patch +Patch9: nginx_src_mail_ngx_mail.patch +Patch10: nginx_src_mail_ngx_mail_auth_http_module.patch +Patch11: nginx_src_mail_ngx_mail_core_module.patch +Patch12: nginx_src_mail_ngx_mail_handler.patch +Patch13: nginx_src_mail_ngx_mail_imap_handler.patch +Patch14: nginx_src_mail_ngx_mail_imap_module_c.patch +Patch15: nginx_src_mail_ngx_mail_imap_module_h.patch +Patch16: nginx_src_mail_ngx_mail_parse.patch +Patch17: nginx_src_mail_ngx_mail_pop3_handler.patch +Patch18: nginx_src_mail_ngx_mail_pop3_module_c.patch +Patch19: nginx_src_mail_ngx_mail_pop3_module_h.patch +Patch20: nginx_src_mail_ngx_mail_proxy_module.patch +Patch21: nginx_src_mail_ngx_mail_smtp_module.patch +Patch22: nginx_docs.patch + BuildRequires: pcre-devel, zlib-devel BuildRequires: zimbra-openssl-devel >= 3.0.9-1zimbra8.8b1ZAPPEND BuildRequires: zimbra-cyrus-sasl-devel >= 2.1.28-1zimbra8.7b4ZAPPEND @@ -17,6 +41,8 @@ URL: http://nginx.org The Zimbra nginx build %changelog +* Mon Feb 05 2024 Zimbra Packaging Services - VERSION-1zimbra8.8b4ZAPPEND +- Upgraded zimbra-nginx to 1.24.0 * Tue Jun 13 2023 Zimbra Packaging Services - VERSION-1zimbra8.8b4ZAPPEND - ZBUG-3355, Upgraded OpenSSL to 3.0.9 * Fri May 06 2022 Zimbra Packaging Services - VERSION-1zimbra8.8b3ZAPPEND @@ -62,11 +88,34 @@ The Zimbra nginx build %prep %setup -n nginx-%{version}-zimbra +%patch0 -p1 +%patch1 -p1 +%patch2 -p1 +%patch3 -p1 +%patch4 -p1 +%patch5 -p1 +%patch6 -p1 +%patch7 -p1 +%patch8 -p1 +%patch9 -p1 +%patch10 -p1 +%patch11 -p1 +%patch12 -p1 +%patch13 -p1 +%patch14 -p1 +%patch15 -p1 +%patch16 -p1 +%patch17 -p1 +%patch18 -p1 +%patch19 -p1 +%patch20 -p1 +%patch21 -p1 +%patch22 -p1 %build LDFLAGS="-Wl,-rpath,OZCL"; export LDFLAGS; \ CFLAGS="-g -O0"; export CFLAGS; \ -./auto/configure --prefix=OZC \ +./configure --prefix=OZC \ --with-cc-opt="-g -IOZCI" \ --with-ld-opt="-Wl,-rpath,OZCL -LOZCL" \ --with-debug \ diff --git a/thirdparty/nginx/zmmodules/http/upstreamzmauth/ngx_http_upstream_zmauth_module.c b/thirdparty/nginx/zmmodules/http/upstreamzmauth/ngx_http_upstream_zmauth_module.c index 9582dd96b..a9b74716e 100644 --- a/thirdparty/nginx/zmmodules/http/upstreamzmauth/ngx_http_upstream_zmauth_module.c +++ b/thirdparty/nginx/zmmodules/http/upstreamzmauth/ngx_http_upstream_zmauth_module.c @@ -82,8 +82,12 @@ static ngx_flag_t ngx_get_authheader_bearer(ngx_log_t *log, ngx_pool_t *pool, ngx_http_headers_in_t *headers_in, ngx_str_t *value); static ngx_flag_t ngx_claimdata_from_payload(ngx_log_t *log, ngx_pool_t *pool, ngx_str_t *payload, ngx_str_t *value); +/* +AMB : Function ngx_get_cookie_value changed to receive the ngx_table_elt_t* instead of ngx_array_t. As the type ngx_table_elt_t don't have members elts & nelts +This change occurs from upgrading the nginx library from version 1.20.0 to 1.24.0 +*/ static ngx_flag_t ngx_get_cookie_value(ngx_log_t *log, - ngx_table_elt_t **cookies, ngx_uint_t ncookies, ngx_str_t *name, + ngx_table_elt_t *cookies, ngx_uint_t ncookies, ngx_str_t *name, ngx_str_t *value); static ngx_flag_t ngx_get_query_string_arg(ngx_log_t *log, ngx_str_t *args, ngx_str_t *name, ngx_str_t *value); @@ -845,53 +849,65 @@ static ngx_flag_t ngx_http_upstream_zmserver_from_cookie static ngx_flag_t ngx_get_cookie_value(ngx_log_t *log, - ngx_table_elt_t **cookies, ngx_uint_t ncookies, ngx_str_t *name, + ngx_table_elt_t *cookies, ngx_uint_t ncookies, ngx_str_t *name, ngx_str_t *value) { - ngx_table_elt_t **c; + ngx_table_elt_t *c; u_char *s, *p, *e; ngx_str_t V, n, v; - ngx_flag_t f; - - for (c = cookies, f = 0; c < cookies + ncookies && f == 0; ++c) { - V = (*c)->value; - /* v is of the form "name=value; name=value;" */ - s = V.data; - e = s + V.len; - p = s; - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, - "zmauth: examining cookie value:%V",&V); - - while (p < e) { - n.data = p; - while (p < e && *p != '=') { - ++p; - } - if (p == e) { - break; - } - n.len = p - n.data; - ++p; // consume = - v.data = p; - while (p < e && *p != ';') { - ++p; - } - v.len = p - v.data; - if (n.len == name->len && ngx_memcmp(n.data, name->data, n.len) - == 0) { - *value = v; - f = 1; - break; - } - if (p == e) { - break; - } - ++p; // consume ; - while (p < e && (*p == ' ' || *p == '\t')) { - ++p; - } - } - } + ngx_flag_t f = 0; + + if (cookies && cookies->value.len) + { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, + "zmauth: printing cookie value:(AMB)%V",&cookies->value); + // AMB : changed cookies from ngx_array_t to ngx_table_elt_t when upgrading the nginx library from version 1.20.0 to 1.24.0 + for (c = cookies, f = 0; /*c < cookies + ncookies && f == 0*/;/* ++c*/) { + V = c->value; + /* v is of the form "name=value; name=value;" */ + s = V.data; + e = s + V.len; + p = s; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, + "zmauth: examining cookie value:(AMB)%V",&V); + + while (p < e) { + n.data = p; + while (p < e && *p != '=') { + ++p; + } + if (p == e) { + break; + } + n.len = p - n.data; + ++p; // consume = + v.data = p; + while (p < e && *p != ';') { + ++p; + } + v.len = p - v.data; + if (n.len == name->len && ngx_memcmp(n.data, name->data, n.len) + == 0) { + *value = v; + f = 1; + break; + } + if (p == e) { + break; + } + ++p; // consume ; + while (p < e && (*p == ' ' || *p == '\t')) { + ++p; + } + } + break; // AMB : change done as part of cookie handling as per new type "ngx_table_elt_t" + } + } + else + { + ngx_log_debug0 (NGX_LOG_DEBUG_HTTP, log, 0, + "zmauth: exiting get cookie without value:(AMB)"); + } return f; } @@ -1648,12 +1664,13 @@ zmauth_check_authtoken(ngx_http_request_t *r, ngx_str_t* field, log = r->connection->log; ngx_log_debug0 (NGX_LOG_DEBUG_HTTP, log, 0, - "zmauth: search for ZM_AUTH_TOKEN"); + "zmauth: search for ZM_AUTH_TOKEN(AMB)"); - /* look for auth token in the request cookie(s) */ + /* look for auth token in the request cookie(s) + AMB - There appears that in version 1.24.0 request member "cookie" has changed. This change occurs from upgrading the nginx library from version 1.20.0 to 1.24.0 (Previously it was ngx_array_t and now its changed to ngx_table_elt_t which don't have members elts & nelts).*/ f = ngx_get_cookie_value(log, - (ngx_table_elt_t **) r->headers_in.cookies.elts, - r->headers_in.cookies.nelts, &NGX_ZMAUTHTOKEN, &token); + (ngx_table_elt_t *) r->headers_in.cookie, + (ngx_uint_t)r->headers_in.cookie, &NGX_ZMAUTHTOKEN, &token); if (!f) { /* if not found, then look in the zauthtoken= query string arg */ @@ -1662,13 +1679,13 @@ zmauth_check_authtoken(ngx_http_request_t *r, ngx_str_t* field, if (f) { ngx_log_debug1 (NGX_LOG_DEBUG_HTTP, log, 0, - "zmauth: found ZM_AUTH_TOKEN:%V", + "zmauth: found ZM_AUTH_TOKEN(AMB):%V", &token); f = ngx_field_from_zmauthtoken(log, pool, &token, field, &value); if (f) { ngx_log_debug2 (NGX_LOG_DEBUG_HTTP, log, 0, - "zmauth: got %V:%V from ZM_AUTH_TOKEN", + "zmauth: got %V:%V from ZM_AUTH_TOKEN(AMB)", field, &value); if (value.len > 0) { pvalue = ngx_palloc(pool, sizeof(ngx_str_t)); @@ -1688,12 +1705,12 @@ zmauth_check_authtoken(ngx_http_request_t *r, ngx_str_t* field, } } else { ngx_log_debug1 (NGX_LOG_DEBUG_HTTP, log, 0, - "zmauth: no %V in ZM_AUTH_TOKEN", field); + "zmauth: no %V in ZM_AUTH_TOKEN(AMB)", field); } } else { ngx_log_debug1 (NGX_LOG_DEBUG_HTTP, log, 0, - "zmauth: no ZM_AUTH_TOKEN", + "zmauth: no ZM_AUTH_TOKEN(AMB)", &token); } @@ -1781,22 +1798,23 @@ zmauth_check_admin_authtoken(ngx_http_request_t *r, ngx_str_t* field, log = r->connection->log; ngx_log_debug0 (NGX_LOG_DEBUG_HTTP, log, 0, - "zmauth: search for ZM_ADMIN_AUTH_TOKEN"); + "zmauth: search for ZM_ADMIN_AUTH_TOKEN(AMB)"); - /* look for auth token in the request cookie(s) */ + /* look for auth token in the request cookie(s) + AMB - There appears that in version 1.24.0 request member "cookie" has changed. This change occurs from upgrading the nginx library from version 1.20.0 to 1.24.0 (Previously it was ngx_array_t and now its changed to ngx_table_elt_t which don't have members elts & nelts).*/ f = ngx_get_cookie_value(log, - (ngx_table_elt_t **) r->headers_in.cookies.elts, - r->headers_in.cookies.nelts, &NGX_ZMAUTHTOKEN_ADMIN, &token); + (ngx_table_elt_t *) r->headers_in.cookie, + (ngx_uint_t)r->headers_in.cookie, &NGX_ZMAUTHTOKEN_ADMIN, &token); if (f) { ngx_log_debug1 (NGX_LOG_DEBUG_HTTP, log, 0, - "zmauth: found ZM_AUTH_TOKEN:%V", + "zmauth: found ZM_AUTH_TOKEN:(AMB)%V", &token); f = ngx_field_from_zmauthtoken(log, pool, &token, field, &value); if (f) { ngx_log_debug2 (NGX_LOG_DEBUG_HTTP, log, 0, - "zmauth: got %V:%V from ZM_AUTH_TOKEN", + "zmauth: got %V:%V from ZM_AUTH_TOKEN(AMB)", field, &value); if (value.len > 0) { @@ -1817,12 +1835,12 @@ zmauth_check_admin_authtoken(ngx_http_request_t *r, ngx_str_t* field, } } else { ngx_log_debug1 (NGX_LOG_DEBUG_HTTP, log, 0, - "zmauth: no %V in ZM_ADMIN_AUTH_TOKEN", field); + "zmauth: no %V in ZM_ADMIN_AUTH_TOKEN(AMB)", field); } } else { ngx_log_debug1 (NGX_LOG_DEBUG_HTTP, log, 0, - "zmauth: no ZM_ADMIN_AUTH_TOKEN", + "zmauth: no ZM_ADMIN_AUTH_TOKEN(AMB)", &token); } diff --git a/versions.def b/versions.def index 2c80743a9..8bf338c0f 100644 --- a/versions.def +++ b/versions.def @@ -98,7 +98,7 @@ MARIADB_VERSION := 10.4.13 MAVEN_VERSION := 3.3.9 -NGINX_VERSION := 1.20.0 +NGINX_VERSION := 1.24.0 NGINX_HTTP_KEEPALIVE := 19755fc0e22aa7d7ac2198f78b297951d31ea1dc OPENSSL_VERSION := 3.0.9 diff --git a/zimbra/proxy-components/zimbra-proxy-components/debian/changelog b/zimbra/proxy-components/zimbra-proxy-components/debian/changelog index 0d32e6035..bf6565630 100644 --- a/zimbra/proxy-components/zimbra-proxy-components/debian/changelog +++ b/zimbra/proxy-components/zimbra-proxy-components/debian/changelog @@ -1,3 +1,9 @@ +zimbra-proxy-components (1.0.12-1zimbra8.8b1ZAPPEND) unstable; urgency=low + + * Upgraded zimbra-nginx to 1.24.0 + + -- Zimbra Packaging Services Mon, 05 Feb 2024 05:12:55 +0000 + zimbra-proxy-components (1.0.11-1zimbra8.8b1ZAPPEND) unstable; urgency=low * Updated zimbra-nginx diff --git a/zimbra/proxy-components/zimbra-proxy-components/debian/control b/zimbra/proxy-components/zimbra-proxy-components/debian/control index 892598175..c8fee5dcf 100644 --- a/zimbra/proxy-components/zimbra-proxy-components/debian/control +++ b/zimbra/proxy-components/zimbra-proxy-components/debian/control @@ -8,7 +8,7 @@ Standards-Version: 3.9.5 Package: zimbra-proxy-components Architecture: all Depends: ${misc:Depends}, ${perl:Depends}, ${shlibs:Depends}, - zimbra-proxy-base, zimbra-nginx (>= 1.20.0-1zimbra8.8b4ZAPPEND) + zimbra-proxy-base, zimbra-nginx (>= 1.24.0-1zimbra8.8b4ZAPPEND) Description: Zimbra components for proxy package Zimbra proxy components pulls in all the packages used by zimbra-proxy diff --git a/zimbra/proxy-components/zimbra-proxy-components/rpm/SPECS/proxy-components.spec b/zimbra/proxy-components/zimbra-proxy-components/rpm/SPECS/proxy-components.spec index b29c1c9b6..4d55b8772 100644 --- a/zimbra/proxy-components/zimbra-proxy-components/rpm/SPECS/proxy-components.spec +++ b/zimbra/proxy-components/zimbra-proxy-components/rpm/SPECS/proxy-components.spec @@ -1,9 +1,9 @@ Summary: Zimbra components for proxy package Name: zimbra-proxy-components -Version: 1.0.11 +Version: 1.0.12 Release: 1zimbra8.8b1ZAPPEND License: GPL-2 -Requires: zimbra-proxy-base, zimbra-nginx >= 1.20.0-1zimbra8.8b4ZAPPEND +Requires: zimbra-proxy-base, zimbra-nginx >= 1.24.0-1zimbra8.8b4ZAPPEND Packager: Zimbra Packaging Services Group: Development/Languages AutoReqProv: no @@ -11,6 +11,8 @@ AutoReqProv: no %define debug_package %{nil} %changelog +* Mon Feb 05 2024 Zimbra Packaging Services - 1.0.12 +- Upgraded zimbra-nginx to 1.24.0 * Tue Jun 13 2023 Zimbra Packaging Services - 1.0.11 - Updated zimbra-nginx * Fri May 06 2022 Zimbra Packaging Services - 1.0.10