summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornginx <nginx@nginx.org>2015-04-28 15:42:09 +0000
committerJon Kolb <kolbyjack@gmail.com>2015-04-28 15:42:09 +0000
commitcf6aa562bf80ee87c803ec940cb23234487f1444 (patch)
tree264d9212b7eaa078be7cb179d4e170c01dee387a
parenta3055d1ce9febb3fa0c8d25ba406c7c473efba43 (diff)
downloadnginx-cf6aa562bf80ee87c803ec940cb23234487f1444.tar.gz
Changes with nginx 1.9.0 28 Apr 2015v1.9.0
-rw-r--r--CHANGES22
-rw-r--r--CHANGES.ru24
-rw-r--r--auto/make59
-rw-r--r--auto/modules49
-rw-r--r--auto/options35
-rw-r--r--auto/os/freebsd20
-rw-r--r--auto/os/linux12
-rw-r--r--auto/os/win321
-rw-r--r--auto/sources51
-rw-r--r--src/core/nginx.c39
-rw-r--r--src/core/nginx.h4
-rw-r--r--src/core/ngx_connection.c7
-rw-r--r--src/core/ngx_connection.h11
-rw-r--r--src/core/ngx_core.h1
-rw-r--r--src/core/ngx_cycle.c31
-rw-r--r--src/core/ngx_cycle.h20
-rw-r--r--src/core/ngx_log.c2
-rw-r--r--src/core/ngx_log.h3
-rw-r--r--src/core/ngx_regex.c37
-rw-r--r--src/core/ngx_resolver.c24
-rw-r--r--src/core/ngx_rwlock.c120
-rw-r--r--src/core/ngx_rwlock.h21
-rw-r--r--src/event/modules/ngx_aio_module.c171
-rw-r--r--src/event/modules/ngx_poll_module.c10
-rw-r--r--src/event/modules/ngx_rtsig_module.c735
-rw-r--r--src/event/modules/ngx_select_module.c10
-rw-r--r--src/event/ngx_event.c64
-rw-r--r--src/event/ngx_event.h20
-rw-r--r--src/event/ngx_event_accept.c46
-rw-r--r--src/event/ngx_event_connect.c4
-rw-r--r--src/http/modules/ngx_http_limit_req_module.c7
-rw-r--r--src/http/modules/ngx_http_memcached_module.c1
-rw-r--r--src/http/modules/ngx_http_upstream_hash_module.c99
-rw-r--r--src/http/modules/ngx_http_upstream_ip_hash_module.c46
-rw-r--r--src/http/modules/ngx_http_upstream_least_conn_module.c167
-rw-r--r--src/http/modules/ngx_http_upstream_zone_module.c227
-rw-r--r--src/http/ngx_http.c5
-rw-r--r--src/http/ngx_http_core_module.c2
-rw-r--r--src/http/ngx_http_request.c8
-rw-r--r--src/http/ngx_http_request.h11
-rw-r--r--src/http/ngx_http_upstream.c2
-rw-r--r--src/http/ngx_http_upstream.h4
-rw-r--r--src/http/ngx_http_upstream_round_robin.c270
-rw-r--r--src/http/ngx_http_upstream_round_robin.h70
-rw-r--r--src/mail/ngx_mail.c16
-rw-r--r--src/mail/ngx_mail.h1
-rw-r--r--src/mail/ngx_mail_core_module.c27
-rw-r--r--src/mail/ngx_mail_handler.c29
-rw-r--r--src/os/unix/ngx_aio_read.c109
-rw-r--r--src/os/unix/ngx_aio_read_chain.c78
-rw-r--r--src/os/unix/ngx_aio_write.c109
-rw-r--r--src/os/unix/ngx_aio_write_chain.c100
-rw-r--r--src/os/unix/ngx_freebsd_config.h2
-rw-r--r--src/os/unix/ngx_linux.h2
-rw-r--r--src/os/unix/ngx_linux_config.h6
-rw-r--r--src/os/unix/ngx_linux_init.c33
-rw-r--r--src/os/unix/ngx_os.h8
-rw-r--r--src/os/unix/ngx_process_cycle.c13
-rw-r--r--src/stream/ngx_stream.c557
-rw-r--r--src/stream/ngx_stream.h205
-rw-r--r--src/stream/ngx_stream_core_module.c495
-rw-r--r--src/stream/ngx_stream_handler.c296
-rw-r--r--src/stream/ngx_stream_proxy_module.c1291
-rw-r--r--src/stream/ngx_stream_ssl_module.c456
-rw-r--r--src/stream/ngx_stream_ssl_module.h49
-rw-r--r--src/stream/ngx_stream_upstream.c462
-rw-r--r--src/stream/ngx_stream_upstream.h103
-rw-r--r--src/stream/ngx_stream_upstream_hash_module.c643
-rw-r--r--src/stream/ngx_stream_upstream_least_conn_module.c305
-rw-r--r--src/stream/ngx_stream_upstream_round_robin.c697
-rw-r--r--src/stream/ngx_stream_upstream_round_robin.h138
-rw-r--r--src/stream/ngx_stream_upstream_zone_module.c221
72 files changed, 7030 insertions, 1993 deletions
diff --git a/CHANGES b/CHANGES
index f2f87a163..938418f0e 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,7 +1,25 @@
-Changes with nginx 1.8.0 21 Apr 2015
+Changes with nginx 1.9.0 28 Apr 2015
- *) 1.8.x stable branch.
+ *) Change: obsolete aio and rtsig event methods have been removed.
+
+ *) Feature: the "zone" directive inside the "upstream" block.
+
+ *) Feature: the stream module.
+
+ *) Feature: byte ranges support in the ngx_http_memcached_module.
+ Thanks to Martin Mlynář.
+
+ *) Feature: shared memory can now be used on Windows versions with
+ address space layout randomization.
+ Thanks to Sergey Brester.
+
+ *) Feature: the "error_log" directive can now be used on mail and server
+ levels in mail proxy.
+
+ *) Bugfix: the "proxy_protocol" parameter of the "listen" directive did
+ not work if not specified in the first "listen" directive for a
+ listen socket.
Changes with nginx 1.7.12 07 Apr 2015
diff --git a/CHANGES.ru b/CHANGES.ru
index b301f4b67..f91a0d7ce 100644
--- a/CHANGES.ru
+++ b/CHANGES.ru
@@ -1,7 +1,27 @@
-Изменения в nginx 1.8.0 21.04.2015
+Изменения в nginx 1.9.0 28.04.2015
- *) Стабильная ветка 1.8.x.
+ *) Изменение: устаревшие методы обработки соединений aio и rtsig больше
+ не поддерживаются.
+
+ *) Добавление: директива zone в блоке upstream.
+
+ *) Добавление: модуль stream.
+
+ *) Добавление: поддержка byte ranges для ответов модуля
+ ngx_http_memcached_module.
+ Спасибо Martin Mlynář.
+
+ *) Добавление: разделяемую память теперь можно использовать на версиях
+ Windows с рандомизацией адресного пространства.
+ Спасибо Сергею Брестеру.
+
+ *) Добавление: директиву error_log теперь можно использовать на уровнях
+ mail и server в почтовом прокси-сервере.
+
+ *) Исправление: параметр proxy_protocol директивы listen не работал,
+ если не был указан в первой директиве listen для данного
+ listen-сокета.
Изменения в nginx 1.7.12 07.04.2015
diff --git a/auto/make b/auto/make
index ed94e8f62..98c2e3b58 100644
--- a/auto/make
+++ b/auto/make
@@ -10,6 +10,7 @@ mkdir -p $NGX_OBJS/src/core $NGX_OBJS/src/event $NGX_OBJS/src/event/modules \
$NGX_OBJS/src/http $NGX_OBJS/src/http/modules \
$NGX_OBJS/src/http/modules/perl \
$NGX_OBJS/src/mail \
+ $NGX_OBJS/src/stream \
$NGX_OBJS/src/misc
@@ -35,7 +36,7 @@ fi
# ALL_INCS, required by the addons and by OpenWatcom C precompiled headers
-ngx_incs=`echo $CORE_INCS $NGX_OBJS $HTTP_INCS $MAIL_INCS\
+ngx_incs=`echo $CORE_INCS $NGX_OBJS $HTTP_INCS $MAIL_INCS $STREAM_INCS\
| sed -e "s/ *\([^ ][^ ]*\)/$ngx_regex_cont$ngx_include_opt\1/g" \
-e "s/\//$ngx_regex_dirsep/g"`
@@ -121,6 +122,32 @@ END
fi
+# the stream dependences and include paths
+
+if [ $STREAM = YES ]; then
+
+ ngx_all_srcs="$ngx_all_srcs $STREAM_SRCS"
+
+ ngx_deps=`echo $STREAM_DEPS \
+ | sed -e "s/ *\([^ ][^ ]*\)/$ngx_regex_cont\1/g" \
+ -e "s/\//$ngx_regex_dirsep/g"`
+
+ ngx_incs=`echo $STREAM_INCS \
+ | sed -e "s/ *\([^ ][^ ]*\)/$ngx_regex_cont$ngx_include_opt\1/g" \
+ -e "s/\//$ngx_regex_dirsep/g"`
+
+ cat << END >> $NGX_MAKEFILE
+
+STREAM_DEPS = $ngx_deps
+
+
+STREAM_INCS = $ngx_include_opt$ngx_incs
+
+END
+
+fi
+
+
ngx_all_srcs="$ngx_all_srcs $NGX_MISC_SRCS"
@@ -306,6 +333,36 @@ END
fi
+# the stream sources
+
+if [ $STREAM = YES ]; then
+
+ if test -n "$NGX_PCH"; then
+ ngx_cc="\$(CC) $ngx_compile_opt \$(CFLAGS) $ngx_use_pch \$(ALL_INCS)"
+ else
+ ngx_cc="\$(CC) $ngx_compile_opt \$(CFLAGS) \$(CORE_INCS) \$(STREAM_INCS)"
+ fi
+
+ for ngx_src in $STREAM_SRCS
+ do
+ ngx_src=`echo $ngx_src | sed -e "s/\//$ngx_regex_dirsep/g"`
+ ngx_obj=`echo $ngx_src \
+ | sed -e "s#^\(.*\.\)cpp\\$#$ngx_objs_dir\1$ngx_objext#g" \
+ -e "s#^\(.*\.\)cc\\$#$ngx_objs_dir\1$ngx_objext#g" \
+ -e "s#^\(.*\.\)c\\$#$ngx_objs_dir\1$ngx_objext#g" \
+ -e "s#^\(.*\.\)S\\$#$ngx_objs_dir\1$ngx_objext#g"`
+
+ cat << END >> $NGX_MAKEFILE
+
+$ngx_obj: \$(CORE_DEPS) \$(STREAM_DEPS)$ngx_cont$ngx_src
+ $ngx_cc$ngx_tab$ngx_objout$ngx_obj$ngx_tab$ngx_src$NGX_AUX
+
+END
+ done
+
+fi
+
+
# the misc sources
if test -n "$NGX_MISC_SRCS"; then
diff --git a/auto/modules b/auto/modules
index 5a56957b8..de3dc4a64 100644
--- a/auto/modules
+++ b/auto/modules
@@ -49,13 +49,6 @@ if [ $NGX_TEST_BUILD_EPOLL = YES ]; then
CORE_SRCS="$CORE_SRCS $EPOLL_SRCS"
fi
-if [ $NGX_TEST_BUILD_RTSIG = YES ]; then
- have=NGX_HAVE_RTSIG . auto/have
- have=NGX_TEST_BUILD_RTSIG . auto/have
- EVENT_MODULES="$EVENT_MODULES $RTSIG_MODULE"
- CORE_SRCS="$CORE_SRCS $RTSIG_SRCS"
-fi
-
if [ $NGX_TEST_BUILD_SOLARIS_SENDFILEV = YES ]; then
have=NGX_TEST_BUILD_SOLARIS_SENDFILEV . auto/have
CORE_SRCS="$CORE_SRCS $SOLARIS_SENDFILEV_SRCS"
@@ -391,6 +384,12 @@ if [ $HTTP_UPSTREAM_KEEPALIVE = YES ]; then
HTTP_SRCS="$HTTP_SRCS $HTTP_UPSTREAM_KEEPALIVE_SRCS"
fi
+if [ $HTTP_UPSTREAM_ZONE = YES ]; then
+ have=NGX_HTTP_UPSTREAM_ZONE . auto/have
+ HTTP_MODULES="$HTTP_MODULES $HTTP_UPSTREAM_ZONE_MODULE"
+ HTTP_SRCS="$HTTP_SRCS $HTTP_UPSTREAM_ZONE_SRCS"
+fi
+
if [ $HTTP_STUB_STATUS = YES ]; then
have=NGX_STAT_STUB . auto/have
HTTP_MODULES="$HTTP_MODULES ngx_http_stub_status_module"
@@ -429,6 +428,12 @@ if [ $MAIL_SSL = YES ]; then
fi
+if [ $STREAM_SSL = YES ]; then
+ have=NGX_STREAM_SSL . auto/have
+ USE_OPENSSL=YES
+fi
+
+
modules="$CORE_MODULES $EVENT_MODULES"
@@ -499,6 +504,36 @@ if [ $MAIL = YES ]; then
fi
+if [ $STREAM = YES ]; then
+ have=NGX_STREAM . auto/have
+ modules="$modules $STREAM_MODULES"
+
+ if [ $STREAM_SSL = YES ]; then
+ modules="$modules $STREAM_SSL_MODULE"
+ STREAM_DEPS="$STREAM_DEPS $STREAM_SSL_DEPS"
+ STREAM_SRCS="$STREAM_SRCS $STREAM_SSL_SRCS"
+ fi
+
+ if [ $STREAM_UPSTREAM_HASH = YES ]; then
+ modules="$modules $STREAM_UPSTREAM_HASH_MODULE"
+ STREAM_SRCS="$STREAM_SRCS $STREAM_UPSTREAM_HASH_SRCS"
+ fi
+
+ if [ $STREAM_UPSTREAM_LEAST_CONN = YES ]; then
+ modules="$modules $STREAM_UPSTREAM_LEAST_CONN_MODULE"
+ STREAM_SRCS="$STREAM_SRCS $STREAM_UPSTREAM_LEAST_CONN_SRCS"
+ fi
+
+ if [ $STREAM_UPSTREAM_ZONE = YES ]; then
+ have=NGX_STREAM_UPSTREAM_ZONE . auto/have
+ modules="$modules $STREAM_UPSTREAM_ZONE_MODULE"
+ STREAM_SRCS="$STREAM_SRCS $STREAM_UPSTREAM_ZONE_SRCS"
+ fi
+
+ NGX_ADDON_DEPS="$NGX_ADDON_DEPS \$(STREAM_DEPS)"
+fi
+
+
if [ $NGX_GOOGLE_PERFTOOLS = YES ]; then
modules="$modules $NGX_GOOGLE_PERFTOOLS_MODULE"
NGX_MISC_SRCS="$NGX_MISC_SRCS $NGX_GOOGLE_PERFTOOLS_SRCS"
diff --git a/auto/options b/auto/options
index 763871f7b..5ecb84bff 100644
--- a/auto/options
+++ b/auto/options
@@ -30,7 +30,6 @@ NGX_RPATH=NO
NGX_TEST_BUILD_DEVPOLL=NO
NGX_TEST_BUILD_EVENTPORT=NO
NGX_TEST_BUILD_EPOLL=NO
-NGX_TEST_BUILD_RTSIG=NO
NGX_TEST_BUILD_SOLARIS_SENDFILEV=NO
NGX_PLATFORM=
@@ -38,10 +37,8 @@ NGX_WINE=
EVENT_FOUND=NO
-EVENT_RTSIG=NO
EVENT_SELECT=NO
EVENT_POLL=NO
-EVENT_AIO=NO
USE_THREADS=NO
@@ -103,6 +100,7 @@ HTTP_UPSTREAM_HASH=YES
HTTP_UPSTREAM_IP_HASH=YES
HTTP_UPSTREAM_LEAST_CONN=YES
HTTP_UPSTREAM_KEEPALIVE=YES
+HTTP_UPSTREAM_ZONE=YES
# STUB
HTTP_STUB_STATUS=NO
@@ -113,6 +111,12 @@ MAIL_POP3=YES
MAIL_IMAP=YES
MAIL_SMTP=YES
+STREAM=NO
+STREAM_SSL=NO
+STREAM_UPSTREAM_HASH=YES
+STREAM_UPSTREAM_LEAST_CONN=YES
+STREAM_UPSTREAM_ZONE=YES
+
NGX_ADDONS=
USE_PCRE=NO
@@ -183,12 +187,10 @@ do
--build=*) NGX_BUILD="$value" ;;
--builddir=*) NGX_OBJS="$value" ;;
- --with-rtsig_module) EVENT_RTSIG=YES ;;
--with-select_module) EVENT_SELECT=YES ;;
--without-select_module) EVENT_SELECT=NONE ;;
--with-poll_module) EVENT_POLL=YES ;;
--without-poll_module) EVENT_POLL=NONE ;;
- --with-aio_module) EVENT_AIO=YES ;;
--with-threads) USE_THREADS=YES ;;
@@ -256,6 +258,7 @@ use the \"--without-http_limit_conn_module\" option instead"
--without-http_upstream_least_conn_module)
HTTP_UPSTREAM_LEAST_CONN=NO ;;
--without-http_upstream_keepalive_module) HTTP_UPSTREAM_KEEPALIVE=NO ;;
+ --without-http_upstream_zone_module) HTTP_UPSTREAM_ZONE=NO ;;
--with-http_perl_module) HTTP_PERL=YES ;;
--with-perl_modules_path=*) NGX_PERL_MODULES="$value" ;;
@@ -273,6 +276,15 @@ use the \"--without-http_limit_conn_module\" option instead"
--without-mail_imap_module) MAIL_IMAP=NO ;;
--without-mail_smtp_module) MAIL_SMTP=NO ;;
+ --with-stream) STREAM=YES ;;
+ --with-stream_ssl_module) STREAM_SSL=YES ;;
+ --without-stream_upstream_hash_module)
+ STREAM_UPSTREAM_HASH=NO ;;
+ --without-stream_upstream_least_conn_module)
+ STREAM_UPSTREAM_LEAST_CONN=NO ;;
+ --without-stream_upstream_zone_module)
+ STREAM_UPSTREAM_ZONE=NO ;;
+
--with-google_perftools_module) NGX_GOOGLE_PERFTOOLS=YES ;;
--with-cpp_test_module) NGX_CPP_TEST=YES ;;
@@ -312,7 +324,6 @@ use the \"--without-http_limit_conn_module\" option instead"
--test-build-devpoll) NGX_TEST_BUILD_DEVPOLL=YES ;;
--test-build-eventport) NGX_TEST_BUILD_EVENTPORT=YES ;;
--test-build-epoll) NGX_TEST_BUILD_EPOLL=YES ;;
- --test-build-rtsig) NGX_TEST_BUILD_RTSIG=YES ;;
--test-build-solaris-sendfilev) NGX_TEST_BUILD_SOLARIS_SENDFILEV=YES ;;
*)
@@ -347,7 +358,6 @@ cat << END
--build=NAME set build name
--builddir=DIR set build directory
- --with-rtsig_module enable rtsig module
--with-select_module enable select module
--without-select_module disable select module
--with-poll_module enable poll module
@@ -406,6 +416,8 @@ cat << END
disable ngx_http_upstream_least_conn_module
--without-http_upstream_keepalive_module
disable ngx_http_upstream_keepalive_module
+ --without-http_upstream_zone_module
+ disable ngx_http_upstream_zone_module
--with-http_perl_module enable ngx_http_perl_module
--with-perl_modules_path=PATH set Perl modules path
@@ -432,6 +444,15 @@ cat << END
--without-mail_imap_module disable ngx_mail_imap_module
--without-mail_smtp_module disable ngx_mail_smtp_module
+ --with-stream enable TCP proxy module
+ --with-stream_ssl_module enable ngx_stream_ssl_module
+ --without-stream_upstream_hash_module
+ disable ngx_stream_upstream_hash_module
+ --without-stream_upstream_least_conn_module
+ disable ngx_stream_upstream_least_conn_module
+ --without-stream_upstream_zone_module
+ disable ngx_stream_upstream_zone_module
+
--with-google_perftools_module enable ngx_google_perftools_module
--with-cpp_test_module enable ngx_cpp_test_module
diff --git a/auto/os/freebsd b/auto/os/freebsd
index 6c696326b..937ca204e 100644
--- a/auto/os/freebsd
+++ b/auto/os/freebsd
@@ -99,26 +99,6 @@ then
fi
-if [ $EVENT_AIO = YES ]; then
- if [ \( $version -lt 500000 -a $version -ge 430000 \) \
- -o $version -ge 500014 ]
- then
- have=NGX_HAVE_AIO . auto/have
- EVENT_MODULES="$EVENT_MODULES $AIO_MODULE"
- CORE_SRCS="$CORE_SRCS $AIO_SRCS"
- else
-
-cat << END
-
-$0: error: the kqueue does not support AIO on this FreeBSD version
-
-END
-
- exit 1
- fi
-fi
-
-
# cpuset_setaffinity()
if [ $version -ge 701000 ]; then
diff --git a/auto/os/linux b/auto/os/linux
index 19bf832ce..c93226757 100644
--- a/auto/os/linux
+++ b/auto/os/linux
@@ -26,18 +26,6 @@ version=$((`uname -r \
version=${version:-0}
-# enable the rt signals on Linux between 2.2.19 and 2.6.17
-
-if [ \( $version -ge 131603 -a $version -lt 132626 \) -o $EVENT_RTSIG = YES ]
-then
- echo " + rt signals found"
- have=NGX_HAVE_RTSIG . auto/have
- EVENT_MODULES="$EVENT_MODULES $RTSIG_MODULE"
- CORE_SRCS="$CORE_SRCS $RTSIG_SRCS"
- EVENT_FOUND=YES
-fi
-
-
# posix_fadvise64() had been implemented in 2.5.60
if [ $version -lt 132412 ]; then
diff --git a/auto/os/win32 b/auto/os/win32
index 0b9b46187..82fc212f8 100644
--- a/auto/os/win32
+++ b/auto/os/win32
@@ -36,5 +36,4 @@ if [ $NGX_IPV6 = YES ]; then
have=NGX_HAVE_INET6 . auto/have
fi
-have=NGX_HAVE_AIO . auto/have
have=NGX_HAVE_IOCP . auto/have
diff --git a/auto/sources b/auto/sources
index 90037894f..156db56fa 100644
--- a/auto/sources
+++ b/auto/sources
@@ -28,6 +28,7 @@ CORE_DEPS="src/core/nginx.h \
src/core/ngx_sha1.h \
src/core/ngx_rbtree.h \
src/core/ngx_radix_tree.h \
+ src/core/ngx_rwlock.h \
src/core/ngx_slab.h \
src/core/ngx_times.h \
src/core/ngx_shmtx.h \
@@ -65,6 +66,7 @@ CORE_SRCS="src/core/nginx.c \
src/core/ngx_connection.c \
src/core/ngx_cycle.c \
src/core/ngx_spinlock.c \
+ src/core/ngx_rwlock.c \
src/core/ngx_cpuinfo.c \
src/core/ngx_conf_file.c \
src/core/ngx_resolver.c \
@@ -122,19 +124,9 @@ EVENTPORT_SRCS=src/event/modules/ngx_eventport_module.c
EPOLL_MODULE=ngx_epoll_module
EPOLL_SRCS=src/event/modules/ngx_epoll_module.c
-RTSIG_MODULE=ngx_rtsig_module
-RTSIG_SRCS=src/event/modules/ngx_rtsig_module.c
-
IOCP_MODULE=ngx_iocp_module
IOCP_SRCS=src/event/modules/ngx_iocp_module.c
-AIO_MODULE=ngx_aio_module
-AIO_SRCS="src/event/modules/ngx_aio_module.c \
- src/os/unix/ngx_aio_read.c \
- src/os/unix/ngx_aio_write.c \
- src/os/unix/ngx_aio_read_chain.c \
- src/os/unix/ngx_aio_write_chain.c"
-
FILE_AIO_SRCS="src/os/unix/ngx_file_aio_read.c"
LINUX_AIO_SRCS="src/os/unix/ngx_linux_aio_read.c"
@@ -511,6 +503,11 @@ HTTP_UPSTREAM_KEEPALIVE_SRCS=" \
src/http/modules/ngx_http_upstream_keepalive_module.c"
+HTTP_UPSTREAM_ZONE_MODULE=ngx_http_upstream_zone_module
+HTTP_UPSTREAM_ZONE_SRCS=" \
+ src/http/modules/ngx_http_upstream_zone_module.c"
+
+
MAIL_INCS="src/mail"
MAIL_DEPS="src/mail/ngx_mail.h"
@@ -547,6 +544,40 @@ MAIL_AUTH_HTTP_SRCS="src/mail/ngx_mail_auth_http_module.c"
MAIL_PROXY_MODULE="ngx_mail_proxy_module"
MAIL_PROXY_SRCS="src/mail/ngx_mail_proxy_module.c"
+
+STREAM_INCS="src/stream"
+
+STREAM_DEPS="src/stream/ngx_stream.h \
+ src/stream/ngx_stream_upstream.h \
+ src/stream/ngx_stream_upstream_round_robin.h"
+
+STREAM_MODULES="ngx_stream_module \
+ ngx_stream_core_module \
+ ngx_stream_proxy_module \
+ ngx_stream_upstream_module"
+
+STREAM_SRCS="src/stream/ngx_stream.c \
+ src/stream/ngx_stream_handler.c \
+ src/stream/ngx_stream_core_module.c \
+ src/stream/ngx_stream_proxy_module.c \
+ src/stream/ngx_stream_upstream.c \
+ src/stream/ngx_stream_upstream_round_robin.c"
+
+STREAM_SSL_MODULE="ngx_stream_ssl_module"
+STREAM_SSL_DEPS="src/stream/ngx_stream_ssl_module.h"
+STREAM_SSL_SRCS="src/stream/ngx_stream_ssl_module.c"
+
+STREAM_UPSTREAM_HASH_MODULE=ngx_stream_upstream_hash_module
+STREAM_UPSTREAM_HASH_SRCS=src/stream/ngx_stream_upstream_hash_module.c
+
+STREAM_UPSTREAM_LEAST_CONN_MODULE=ngx_stream_upstream_least_conn_module
+STREAM_UPSTREAM_LEAST_CONN_SRCS=" \
+ src/stream/ngx_stream_upstream_least_conn_module.c"
+
+STREAM_UPSTREAM_ZONE_MODULE=ngx_stream_upstream_zone_module
+STREAM_UPSTREAM_ZONE_SRCS=src/stream/ngx_stream_upstream_zone_module.c
+
+
NGX_GOOGLE_PERFTOOLS_MODULE=ngx_google_perftools_module
NGX_GOOGLE_PERFTOOLS_SRCS=src/misc/ngx_google_perftools_module.c
diff --git a/src/core/nginx.c b/src/core/nginx.c
index 4702d2881..231a3daf5 100644
--- a/src/core/nginx.c
+++ b/src/core/nginx.c
@@ -118,13 +118,6 @@ static ngx_command_t ngx_core_commands[] = {
offsetof(ngx_core_conf_t, rlimit_core),
NULL },
- { ngx_string("worker_rlimit_sigpending"),
- NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
- ngx_conf_set_num_slot,
- 0,
- offsetof(ngx_core_conf_t, rlimit_sigpending),
- NULL },
-
{ ngx_string("working_directory"),
NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_slot,
@@ -139,24 +132,6 @@ static ngx_command_t ngx_core_commands[] = {
0,
NULL },
-#if (NGX_OLD_THREADS)
-
- { ngx_string("worker_threads"),
- NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
- ngx_conf_set_num_slot,
- 0,
- offsetof(ngx_core_conf_t, worker_threads),
- NULL },
-
- { ngx_string("thread_stack_size"),
- NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
- ngx_conf_set_size_slot,
- 0,
- offsetof(ngx_core_conf_t, thread_stack_size),
- NULL },
-
-#endif
-
ngx_null_command
};
@@ -966,16 +941,10 @@ ngx_core_module_create_conf(ngx_cycle_t *cycle)
ccf->rlimit_nofile = NGX_CONF_UNSET;
ccf->rlimit_core = NGX_CONF_UNSET;
- ccf->rlimit_sigpending = NGX_CONF_UNSET;
ccf->user = (ngx_uid_t) NGX_CONF_UNSET_UINT;
ccf->group = (ngx_gid_t) NGX_CONF_UNSET_UINT;
-#if (NGX_OLD_THREADS)
- ccf->worker_threads = NGX_CONF_UNSET;
- ccf->thread_stack_size = NGX_CONF_UNSET_SIZE;
-#endif
-
if (ngx_array_init(&ccf->env, cycle->pool, 1, sizeof(ngx_str_t))
!= NGX_OK)
{
@@ -1012,14 +981,6 @@ ngx_core_module_init_conf(ngx_cycle_t *cycle, void *conf)
#endif
-#if (NGX_OLD_THREADS)
-
- ngx_conf_init_value(ccf->worker_threads, 0);
- ngx_threads_n = ccf->worker_threads;
- ngx_conf_init_size_value(ccf->thread_stack_size, 2 * 1024 * 1024);
-
-#endif
-
if (ccf->pid.len == 0) {
ngx_str_set(&ccf->pid, NGX_PID_PATH);
diff --git a/src/core/nginx.h b/src/core/nginx.h
index 9f3a65631..347c25e82 100644
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -9,8 +9,8 @@
#define _NGINX_H_INCLUDED_
-#define nginx_version 1008000
-#define NGINX_VERSION "1.8.0"
+#define nginx_version 1009000
+#define NGINX_VERSION "1.9.0"
#define NGINX_VER "nginx/" NGINX_VERSION
#ifdef NGX_BUILD
diff --git a/src/core/ngx_connection.c b/src/core/ngx_connection.c
index 9f8fbc363..52b97ce89 100644
--- a/src/core/ngx_connection.c
+++ b/src/core/ngx_connection.c
@@ -389,7 +389,7 @@ ngx_open_listening_sockets(ngx_cycle_t *cycle)
#endif
/* TODO: close on exit */
- if (!(ngx_event_flags & NGX_USE_AIO_EVENT)) {
+ if (!(ngx_event_flags & NGX_USE_IOCP_EVENT)) {
if (ngx_nonblocking(s) == -1) {
ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
ngx_nonblocking_n " %V failed",
@@ -764,10 +764,7 @@ ngx_close_listening_sockets(ngx_cycle_t *cycle)
if (c) {
if (c->read->active) {
- if (ngx_event_flags & NGX_USE_RTSIG_EVENT) {
- ngx_del_conn(c, NGX_CLOSE_EVENT);
-
- } else if (ngx_event_flags & NGX_USE_EPOLL_EVENT) {
+ if (ngx_event_flags & NGX_USE_EPOLL_EVENT) {
/*
* it seems that Linux-2.6.x OpenVZ sends events
diff --git a/src/core/ngx_connection.h b/src/core/ngx_connection.h
index f1ca9619b..27bb8a99c 100644
--- a/src/core/ngx_connection.h
+++ b/src/core/ngx_connection.h
@@ -190,6 +190,17 @@ struct ngx_connection_s {
};
+#define ngx_set_connection_log(c, l) \
+ \
+ c->log->file = l->file; \
+ c->log->next = l->next; \
+ c->log->writer = l->writer; \
+ c->log->wdata = l->wdata; \
+ if (!(c->log->log_level & NGX_LOG_DEBUG_CONNECTION)) { \
+ c->log->log_level = l->log_level; \
+ }
+
+
ngx_listening_t *ngx_create_listening(ngx_conf_t *cf, void *sockaddr,
socklen_t socklen);
ngx_int_t ngx_set_inherited_sockets(ngx_cycle_t *cycle);
diff --git a/src/core/ngx_core.h b/src/core/ngx_core.h
index 97100f6d0..a279c81d6 100644
--- a/src/core/ngx_core.h
+++ b/src/core/ngx_core.h
@@ -71,6 +71,7 @@ typedef void (*ngx_connection_handler_pt)(ngx_connection_t *c);
#endif
#include <ngx_radix_tree.h>
#include <ngx_times.h>
+#include <ngx_rwlock.h>
#include <ngx_shmtx.h>
#include <ngx_slab.h>
#include <ngx_inet.h>
diff --git a/src/core/ngx_cycle.c b/src/core/ngx_cycle.c
index 11e413f4e..4852e3bbe 100644
--- a/src/core/ngx_cycle.c
+++ b/src/core/ngx_cycle.c
@@ -26,10 +26,6 @@ static ngx_event_t ngx_cleaner_event;
ngx_uint_t ngx_test_config;
ngx_uint_t ngx_quiet_mode;
-#if (NGX_OLD_THREADS)
-ngx_tls_key_t ngx_core_tls_key;
-#endif
-
/* STUB NAME */
static ngx_connection_t dumb;
@@ -441,9 +437,13 @@ ngx_init_cycle(ngx_cycle_t *old_cycle)
}
if (shm_zone[i].tag == oshm_zone[n].tag
- && shm_zone[i].shm.size == oshm_zone[n].shm.size)
+ && shm_zone[i].shm.size == oshm_zone[n].shm.size
+ && !shm_zone[i].noreuse)
{
shm_zone[i].shm.addr = oshm_zone[n].shm.addr;
+#if (NGX_WIN32)
+ shm_zone[i].shm.handle = oshm_zone[n].shm.handle;
+#endif
if (shm_zone[i].init(&shm_zone[i], oshm_zone[n].data)
!= NGX_OK)
@@ -863,6 +863,22 @@ ngx_init_zone_pool(ngx_cycle_t *cycle, ngx_shm_zone_t *zn)
return NGX_OK;
}
+#if (NGX_WIN32)
+
+ /* remap at the required address */
+
+ if (ngx_shm_remap(&zn->shm, sp->addr) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ sp = (ngx_slab_pool_t *) zn->shm.addr;
+
+ if (sp == sp->addr) {
+ return NGX_OK;
+ }
+
+#endif
+
ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
"shared zone \"%V\" has no equal addresses: %p vs %p",
&zn->shm.name, sp->addr, sp);
@@ -1210,6 +1226,10 @@ ngx_shared_memory_add(ngx_conf_t *cf, ngx_str_t *name, size_t size, void *tag)
return NULL;
}
+ if (shm_zone[i].shm.size == 0) {
+ shm_zone[i].shm.size = size;
+ }
+
if (size && size != shm_zone[i].shm.size) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"the size %uz of shared memory zone \"%V\" "
@@ -1234,6 +1254,7 @@ ngx_shared_memory_add(ngx_conf_t *cf, ngx_str_t *name, size_t size, void *tag)
shm_zone->shm.exists = 0;
shm_zone->init = NULL;
shm_zone->tag = tag;
+ shm_zone->noreuse = 0;
return shm_zone;
}
diff --git a/src/core/ngx_cycle.h b/src/core/ngx_cycle.h
index be90a7281..c601ea133 100644
--- a/src/core/ngx_cycle.h
+++ b/src/core/ngx_cycle.h
@@ -31,6 +31,7 @@ struct ngx_shm_zone_s {
ngx_shm_t shm;
ngx_shm_zone_init_pt init;
void *tag;
+ ngx_uint_t noreuse; /* unsigned noreuse:1; */
};
@@ -82,7 +83,6 @@ typedef struct {
ngx_int_t debug_points;
ngx_int_t rlimit_nofile;
- ngx_int_t rlimit_sigpending;
off_t rlimit_core;
int priority;
@@ -102,24 +102,9 @@ typedef struct {
ngx_array_t env;
char **environment;
-
-#if (NGX_OLD_THREADS)
- ngx_int_t worker_threads;
- size_t thread_stack_size;
-#endif
-
} ngx_core_conf_t;
-#if (NGX_OLD_THREADS)
-
-typedef struct {
- ngx_pool_t *pool; /* pcre's malloc() pool */
-} ngx_core_tls_t;
-
-#endif
-
-
#define ngx_is_init_cycle(cycle) (cycle->conf_ctx == NULL)
@@ -140,9 +125,6 @@ extern ngx_array_t ngx_old_cycles;
extern ngx_module_t ngx_core_module;
extern ngx_uint_t ngx_test_config;
extern ngx_uint_t ngx_quiet_mode;
-#if (NGX_OLD_THREADS)
-extern ngx_tls_key_t ngx_core_tls_key;
-#endif
#endif /* _NGX_CYCLE_H_INCLUDED_ */
diff --git a/src/core/ngx_log.c b/src/core/ngx_log.c
index bf0050885..2aea37440 100644
--- a/src/core/ngx_log.c
+++ b/src/core/ngx_log.c
@@ -86,7 +86,7 @@ static ngx_str_t err_levels[] = {
static const char *debug_levels[] = {
"debug_core", "debug_alloc", "debug_mutex", "debug_event",
- "debug_http", "debug_mail", "debug_mysql"
+ "debug_http", "debug_mail", "debug_mysql", "debug_stream"
};
diff --git a/src/core/ngx_log.h b/src/core/ngx_log.h
index 6b04b7876..cb80b5f83 100644
--- a/src/core/ngx_log.h
+++ b/src/core/ngx_log.h
@@ -30,6 +30,7 @@
#define NGX_LOG_DEBUG_HTTP 0x100
#define NGX_LOG_DEBUG_MAIL 0x200
#define NGX_LOG_DEBUG_MYSQL 0x400
+#define NGX_LOG_DEBUG_STREAM 0x800
/*
* do not forget to update debug_levels[] in src/core/ngx_log.c
@@ -37,7 +38,7 @@
*/
#define NGX_LOG_DEBUG_FIRST NGX_LOG_DEBUG_CORE
-#define NGX_LOG_DEBUG_LAST NGX_LOG_DEBUG_MYSQL
+#define NGX_LOG_DEBUG_LAST NGX_LOG_DEBUG_STREAM
#define NGX_LOG_DEBUG_CONNECTION 0x80000000
#define NGX_LOG_DEBUG_ALL 0x7ffffff0
diff --git a/src/core/ngx_regex.c b/src/core/ngx_regex.c
index 77c5947de..416622dcc 100644
--- a/src/core/ngx_regex.c
+++ b/src/core/ngx_regex.c
@@ -80,17 +80,6 @@ ngx_regex_init(void)
static ngx_inline void
ngx_regex_malloc_init(ngx_pool_t *pool)
{
-#if (NGX_OLD_THREADS)
- ngx_core_tls_t *tls;
-
- if (ngx_threaded) {
- tls = ngx_thread_get_tls(ngx_core_tls_key);
- tls->pool = pool;
- return;
- }
-
-#endif
-
ngx_pcre_pool = pool;
}
@@ -98,17 +87,6 @@ ngx_regex_malloc_init(ngx_pool_t *pool)
static ngx_inline void
ngx_regex_malloc_done(void)
{
-#if (NGX_OLD_THREADS)
- ngx_core_tls_t *tls;
-
- if (ngx_threaded) {
- tls = ngx_thread_get_tls(ngx_core_tls_key);
- tls->pool = NULL;
- return;
- }
-
-#endif
-
ngx_pcre_pool = NULL;
}
@@ -253,23 +231,8 @@ static void * ngx_libc_cdecl
ngx_regex_malloc(size_t size)
{
ngx_pool_t *pool;
-#if (NGX_OLD_THREADS)
- ngx_core_tls_t *tls;
-
- if (ngx_threaded) {
- tls = ngx_thread_get_tls(ngx_core_tls_key);
- pool = tls->pool;
-
- } else {
- pool = ngx_pcre_pool;
- }
-
-#else
-
pool = ngx_pcre_pool;
-#endif
-
if (pool) {
return ngx_palloc(pool, size);
}
diff --git a/src/core/ngx_resolver.c b/src/core/ngx_resolver.c
index 4e9c85da2..caa2b51c0 100644
--- a/src/core/ngx_resolver.c
+++ b/src/core/ngx_resolver.c
@@ -3092,7 +3092,7 @@ ngx_udp_connect(ngx_udp_connection_t *uc)
rc = connect(s, uc->sockaddr, uc->socklen);
- /* TODO: aio, iocp */
+ /* TODO: iocp */
if (rc == -1) {
ngx_log_error(NGX_LOG_CRIT, &uc->log, ngx_socket_errno,
@@ -3104,23 +3104,13 @@ ngx_udp_connect(ngx_udp_connection_t *uc)
/* UDP sockets are always ready to write */
wev->ready = 1;
- if (ngx_add_event) {
+ event = (ngx_event_flags & NGX_USE_CLEAR_EVENT) ?
+ /* kqueue, epoll */ NGX_CLEAR_EVENT:
+ /* select, poll, /dev/poll */ NGX_LEVEL_EVENT;
+ /* eventport event type has no meaning: oneshot only */
- event = (ngx_event_flags & NGX_USE_CLEAR_EVENT) ?
- /* kqueue, epoll */ NGX_CLEAR_EVENT:
- /* select, poll, /dev/poll */ NGX_LEVEL_EVENT;
- /* eventport event type has no meaning: oneshot only */
-
- if (ngx_add_event(rev, NGX_READ_EVENT, event) != NGX_OK) {
- goto failed;
- }
-
- } else {
- /* rtsig */
-
- if (ngx_add_conn(c) == NGX_ERROR) {
- goto failed;
- }
+ if (ngx_add_event(rev, NGX_READ_EVENT, event) != NGX_OK) {
+ goto failed;
}
return NGX_OK;
diff --git a/src/core/ngx_rwlock.c b/src/core/ngx_rwlock.c
new file mode 100644
index 000000000..1404c6edf
--- /dev/null
+++ b/src/core/ngx_rwlock.c
@@ -0,0 +1,120 @@
+
+/*
+ * Copyright (C) Ruslan Ermilov
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+#if (NGX_HAVE_ATOMIC_OPS)
+
+
+#define NGX_RWLOCK_SPIN 2048
+#define NGX_RWLOCK_WLOCK ((ngx_atomic_uint_t) -1)
+
+
+void
+ngx_rwlock_wlock(ngx_atomic_t *lock)
+{
+ ngx_uint_t i, n;
+
+ for ( ;; ) {
+
+ if (*lock == 0 && ngx_atomic_cmp_set(lock, 0, NGX_RWLOCK_WLOCK)) {
+ return;
+ }
+
+ if (ngx_ncpu > 1) {
+
+ for (n = 1; n < NGX_RWLOCK_SPIN; n <<= 1) {
+
+ for (i = 0; i < n; i++) {
+ ngx_cpu_pause();
+ }
+
+ if (*lock == 0
+ && ngx_atomic_cmp_set(lock, 0, NGX_RWLOCK_WLOCK))
+ {
+ return;
+ }
+ }
+ }
+
+ ngx_sched_yield();
+ }
+}
+
+
+void
+ngx_rwlock_rlock(ngx_atomic_t *lock)
+{
+ ngx_uint_t i, n;
+ ngx_atomic_uint_t readers;
+
+ for ( ;; ) {
+ readers = *lock;
+
+ if (readers != NGX_RWLOCK_WLOCK
+ && ngx_atomic_cmp_set(lock, readers, readers + 1))
+ {
+ return;
+ }
+
+ if (ngx_ncpu > 1) {
+
+ for (n = 1; n < NGX_RWLOCK_SPIN; n <<= 1) {
+
+ for (i = 0; i < n; i++) {
+ ngx_cpu_pause();
+ }
+
+ readers = *lock;
+
+ if (readers != NGX_RWLOCK_WLOCK
+ && ngx_atomic_cmp_set(lock, readers, readers + 1))
+ {
+ return;
+ }
+ }
+ }
+
+ ngx_sched_yield();
+ }
+}
+
+
+void
+ngx_rwlock_unlock(ngx_atomic_t *lock)
+{
+ ngx_atomic_uint_t readers;
+
+ readers = *lock;
+
+ if (readers == NGX_RWLOCK_WLOCK) {
+ *lock = 0;
+ return;
+ }
+
+ for ( ;; ) {
+
+ if (ngx_atomic_cmp_set(lock, readers, readers - 1)) {
+ return;
+ }
+
+ readers = *lock;
+ }
+}
+
+
+#else
+
+#if (NGX_HTTP_UPSTREAM_ZONE)
+
+#error ngx_atomic_cmp_set() is not defined!
+
+#endif
+
+#endif
diff --git a/src/core/ngx_rwlock.h b/src/core/ngx_rwlock.h
new file mode 100644
index 000000000..8b16eca20
--- /dev/null
+++ b/src/core/ngx_rwlock.h
@@ -0,0 +1,21 @@
+
+/*
+ * Copyright (C) Ruslan Ermilov
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_RWLOCK_H_INCLUDED_
+#define _NGX_RWLOCK_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+void ngx_rwlock_wlock(ngx_atomic_t *lock);
+void ngx_rwlock_rlock(ngx_atomic_t *lock);
+void ngx_rwlock_unlock(ngx_atomic_t *lock);
+
+
+#endif /* _NGX_RWLOCK_H_INCLUDED_ */
diff --git a/src/event/modules/ngx_aio_module.c b/src/event/modules/ngx_aio_module.c
deleted file mode 100644
index fd03fecb4..000000000
--- a/src/event/modules/ngx_aio_module.c
+++ /dev/null
@@ -1,171 +0,0 @@
-
-/*
- * Copyright (C) Igor Sysoev
- * Copyright (C) Nginx, Inc.
- */
-
-
-#include <ngx_config.h>
-#include <ngx_core.h>
-#include <ngx_event.h>
-
-
-extern ngx_event_module_t ngx_kqueue_module_ctx;
-
-
-static ngx_int_t ngx_aio_init(ngx_cycle_t *cycle, ngx_msec_t timer);
-static void ngx_aio_done(ngx_cycle_t *cycle);
-static ngx_int_t ngx_aio_add_event(ngx_event_t *ev, ngx_int_t event,
- ngx_uint_t flags);
-static ngx_int_t ngx_aio_del_event(ngx_event_t *ev, ngx_int_t event,
- ngx_uint_t flags);
-static ngx_int_t ngx_aio_del_connection(ngx_connection_t *c, ngx_uint_t flags);
-static ngx_int_t ngx_aio_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,
- ngx_uint_t flags);
-
-
-ngx_os_io_t ngx_os_aio = {
- ngx_aio_read,
- ngx_aio_read_chain,
- NULL,
- ngx_aio_write,
- ngx_aio_write_chain,
- 0
-};
-
-
-static ngx_str_t aio_name = ngx_string("aio");
-
-ngx_event_module_t ngx_aio_module_ctx = {
- &aio_name,
- NULL, /* create configuration */
- NULL, /* init configuration */
-
- {
- ngx_aio_add_event, /* add an event */
- ngx_aio_del_event, /* delete an event */
- NULL, /* enable an event */
- NULL, /* disable an event */
- NULL, /* add an connection */
- ngx_aio_del_connection, /* delete an connection */
- NULL, /* trigger a notify */
- ngx_aio_process_events, /* process the events */
- ngx_aio_init, /* init the events */
- ngx_aio_done /* done the events */
- }
-
-};
-
-ngx_module_t ngx_aio_module = {
- NGX_MODULE_V1,
- &ngx_aio_module_ctx, /* module context */
- NULL, /* module directives */
- NGX_EVENT_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
-};
-
-
-#if (NGX_HAVE_KQUEUE)
-
-static ngx_int_t
-ngx_aio_init(ngx_cycle_t *cycle, ngx_msec_t timer)
-{
- if (ngx_kqueue_module_ctx.actions.init(cycle, timer) == NGX_ERROR) {
- return NGX_ERROR;
- }
-
- ngx_io = ngx_os_aio;
-
- ngx_event_flags = NGX_USE_AIO_EVENT;
- ngx_event_actions = ngx_aio_module_ctx.actions;
-
-
- return NGX_OK;
-}
-
-
-static void
-ngx_aio_done(ngx_cycle_t *cycle)
-{
- ngx_kqueue_module_ctx.actions.done(cycle);
-}
-
-
-/* the event adding and deleting are needed for the listening sockets */
-
-static ngx_int_t
-ngx_aio_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
-{
- return ngx_kqueue_module_ctx.actions.add(ev, event, flags);
-}
-
-
-static ngx_int_t
-ngx_aio_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
-{
- return ngx_kqueue_module_ctx.actions.del(ev, event, flags);
-}
-
-
-static ngx_int_t
-ngx_aio_del_connection(ngx_connection_t *c, ngx_uint_t flags)
-{
- int rc;
-
- if (c->read->active == 0 && c->write->active == 0) {
- return NGX_OK;
- }
-
- if (flags & NGX_CLOSE_EVENT) {
- return NGX_OK;
- }
-
- rc = aio_cancel(c->fd, NULL);
-
- ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "aio_cancel: %d", rc);
-
- if (rc == AIO_CANCELED) {
- c->read->active = 0;
- c->write->active = 0;
- return NGX_OK;
- }
-
- if (rc == AIO_ALLDONE) {
- c->read->active = 0;
- c->write->active = 0;
- ngx_log_error(NGX_LOG_ALERT, c->log, 0,
- "aio_cancel() returned AIO_ALLDONE");
- return NGX_OK;
- }
-
- if (rc == -1) {
- ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
- "aio_cancel() failed");
- return NGX_ERROR;
- }
-
- if (rc == AIO_NOTCANCELED) {
- ngx_log_error(NGX_LOG_ALERT, c->log, 0,
- "aio_cancel() returned AIO_NOTCANCELED");
-
- return NGX_ERROR;
- }
-
- return NGX_OK;
-}
-
-
-static ngx_int_t
-ngx_aio_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags)
-{
- return ngx_kqueue_module_ctx.actions.process_events(cycle, timer, flags);
-}
-
-#endif /* NGX_HAVE_KQUEUE */
diff --git a/src/event/modules/ngx_poll_module.c b/src/event/modules/ngx_poll_module.c
index e48a8227a..4370950c0 100644
--- a/src/event/modules/ngx_poll_module.c
+++ b/src/event/modules/ngx_poll_module.c
@@ -413,15 +413,5 @@ ngx_poll_init_conf(ngx_cycle_t *cycle, void *conf)
return NGX_CONF_OK;
}
-#if (NGX_OLD_THREADS)
-
- ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
- "poll() is not supported in the threaded mode");
- return NGX_CONF_ERROR;
-
-#else
-
return NGX_CONF_OK;
-
-#endif
}
diff --git a/src/event/modules/ngx_rtsig_module.c b/src/event/modules/ngx_rtsig_module.c
deleted file mode 100644
index b120fecde..000000000
--- a/src/event/modules/ngx_rtsig_module.c
+++ /dev/null
@@ -1,735 +0,0 @@
-
-/*
- * Copyright (C) Igor Sysoev
- * Copyright (C) Nginx, Inc.
- */
-
-
-#include <ngx_config.h>
-#include <ngx_core.h>
-#include <ngx_event.h>
-
-
-#if (NGX_TEST_BUILD_RTSIG)
-
-#if (NGX_DARWIN)
-
-#define SIGRTMIN 33
-#define si_fd __pad[0]
-
-#else
-
-#ifdef SIGRTMIN
-#define si_fd _reason.__spare__.__spare2__[0]
-#else
-#define SIGRTMIN 33
-#define si_fd __spare__[0]
-#endif
-
-#endif
-
-#define F_SETSIG 10
-#define KERN_RTSIGNR 30
-#define KERN_RTSIGMAX 31
-
-int sigtimedwait(const sigset_t *set, siginfo_t *info,
- const struct timespec *timeout);
-
-int sigtimedwait(const sigset_t *set, siginfo_t *info,
- const struct timespec *timeout)
-{
- return -1;
-}
-
-int ngx_linux_rtsig_max;
-
-#endif
-
-
-typedef struct {
- ngx_uint_t signo;
- ngx_uint_t overflow_events;
- ngx_uint_t overflow_test;
- ngx_uint_t overflow_threshold;
-} ngx_rtsig_conf_t;
-
-
-extern ngx_event_module_t ngx_poll_module_ctx;
-
-static ngx_int_t ngx_rtsig_init(ngx_cycle_t *cycle, ngx_msec_t timer);
-static void ngx_rtsig_done(ngx_cycle_t *cycle);
-static ngx_int_t ngx_rtsig_add_connection(ngx_connection_t *c);
-static ngx_int_t ngx_rtsig_del_connection(ngx_connection_t *c,
- ngx_uint_t flags);
-static ngx_int_t ngx_rtsig_process_events(ngx_cycle_t *cycle,
- ngx_msec_t timer, ngx_uint_t flags);
-static ngx_int_t ngx_rtsig_process_overflow(ngx_cycle_t *cycle,
- ngx_msec_t timer, ngx_uint_t flags);
-
-static void *ngx_rtsig_create_conf(ngx_cycle_t *cycle);
-static char *ngx_rtsig_init_conf(ngx_cycle_t *cycle, void *conf);
-static char *ngx_check_ngx_overflow_threshold_bounds(ngx_conf_t *cf,
- void *post, void *data);
-
-
-static sigset_t set;
-static ngx_uint_t overflow, overflow_current;
-static struct pollfd *overflow_list;
-
-
-static ngx_str_t rtsig_name = ngx_string("rtsig");
-
-static ngx_conf_num_bounds_t ngx_overflow_threshold_bounds = {
- ngx_check_ngx_overflow_threshold_bounds, 2, 10
-};
-
-
-static ngx_command_t ngx_rtsig_commands[] = {
-
- { ngx_string("rtsig_signo"),
- NGX_EVENT_CONF|NGX_CONF_TAKE1,
- ngx_conf_set_num_slot,
- 0,
- offsetof(ngx_rtsig_conf_t, signo),
- NULL },
-
- { ngx_string("rtsig_overflow_events"),
- NGX_EVENT_CONF|NGX_CONF_TAKE1,
- ngx_conf_set_num_slot,
- 0,
- offsetof(ngx_rtsig_conf_t, overflow_events),
- NULL },
-
- { ngx_string("rtsig_overflow_test"),
- NGX_EVENT_CONF|NGX_CONF_TAKE1,
- ngx_conf_set_num_slot,
- 0,
- offsetof(ngx_rtsig_conf_t, overflow_test),
- NULL },
-
- { ngx_string("rtsig_overflow_threshold"),
- NGX_EVENT_CONF|NGX_CONF_TAKE1,
- ngx_conf_set_num_slot,
- 0,
- offsetof(ngx_rtsig_conf_t, overflow_threshold),
- &ngx_overflow_threshold_bounds },
-
- ngx_null_command
-};
-
-
-ngx_event_module_t ngx_rtsig_module_ctx = {
- &rtsig_name,
- ngx_rtsig_create_conf, /* create configuration */
- ngx_rtsig_init_conf, /* init configuration */
-
- {
- NULL, /* add an event */
- NULL, /* delete an event */
- NULL, /* enable an event */
- NULL, /* disable an event */
- ngx_rtsig_add_connection, /* add an connection */
- ngx_rtsig_del_connection, /* delete an connection */
- NULL, /* trigger a notify */
- ngx_rtsig_process_events, /* process the events */
- ngx_rtsig_init, /* init the events */
- ngx_rtsig_done, /* done the events */
- }
-
-};
-
-ngx_module_t ngx_rtsig_module = {
- NGX_MODULE_V1,
- &ngx_rtsig_module_ctx, /* module context */
- ngx_rtsig_commands, /* module directives */
- NGX_EVENT_MODULE, /* module type */
- NULL, /* init master */
- NULL, /* init module */
- NULL, /* init process */
- NULL, /* init thread */
- NULL, /* exit thread */
- NULL, /* exit process */
- NULL, /* exit master */
- NGX_MODULE_V1_PADDING
-};
-
-
-static ngx_int_t
-ngx_rtsig_init(ngx_cycle_t *cycle, ngx_msec_t timer)
-{
- ngx_rtsig_conf_t *rtscf;
-
- rtscf = ngx_event_get_conf(cycle->conf_ctx, ngx_rtsig_module);
-
- sigemptyset(&set);
- sigaddset(&set, (int) rtscf->signo);
- sigaddset(&set, (int) rtscf->signo + 1);
- sigaddset(&set, SIGIO);
- sigaddset(&set, SIGALRM);
-
- if (sigprocmask(SIG_BLOCK, &set, NULL) == -1) {
- ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
- "sigprocmask() failed");
- return NGX_ERROR;
- }
-
- if (overflow_list) {
- ngx_free(overflow_list);
- }
-
- overflow_list = ngx_alloc(sizeof(struct pollfd) * rtscf->overflow_events,
- cycle->log);
- if (overflow_list == NULL) {
- return NGX_ERROR;
- }
-
- ngx_io = ngx_os_io;
-
- ngx_event_actions = ngx_rtsig_module_ctx.actions;
-
- ngx_event_flags = NGX_USE_RTSIG_EVENT
- |NGX_USE_GREEDY_EVENT
- |NGX_USE_FD_EVENT;
-
- return NGX_OK;
-}
-
-
-static void
-ngx_rtsig_done(ngx_cycle_t *cycle)
-{
- ngx_free(overflow_list);
-
- overflow_list = NULL;
-}
-
-
-static ngx_int_t
-ngx_rtsig_add_connection(ngx_connection_t *c)
-{
- ngx_uint_t signo;
- ngx_rtsig_conf_t *rtscf;
-
- if (c->read->accept && c->read->disabled) {
-
- ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "rtsig enable connection: fd:%d", c->fd);
-
- if (fcntl(c->fd, F_SETOWN, ngx_pid) == -1) {
- ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
- "fcntl(F_SETOWN) failed");
- return NGX_ERROR;
- }
-
- c->read->active = 1;
- c->read->disabled = 0;
- }
-
- rtscf = ngx_event_get_conf(ngx_cycle->conf_ctx, ngx_rtsig_module);
-
- signo = rtscf->signo + c->read->instance;
-
- ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "rtsig add connection: fd:%d signo:%ui", c->fd, signo);
-
- if (fcntl(c->fd, F_SETFL, O_RDWR|O_NONBLOCK|O_ASYNC) == -1) {
- ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
- "fcntl(O_RDWR|O_NONBLOCK|O_ASYNC) failed");
- return NGX_ERROR;
- }
-
- if (fcntl(c->fd, F_SETSIG, (int) signo) == -1) {
- ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
- "fcntl(F_SETSIG) failed");
- return NGX_ERROR;
- }
-
- if (fcntl(c->fd, F_SETOWN, ngx_pid) == -1) {
- ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
- "fcntl(F_SETOWN) failed");
- return NGX_ERROR;
- }
-
-#if (NGX_HAVE_ONESIGFD)
- if (fcntl(c->fd, F_SETAUXFL, O_ONESIGFD) == -1) {
- ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
- "fcntl(F_SETAUXFL) failed");
- return NGX_ERROR;
- }
-#endif
-
- c->read->active = 1;
- c->write->active = 1;
-
- return NGX_OK;
-}
-
-
-static ngx_int_t
-ngx_rtsig_del_connection(ngx_connection_t *c, ngx_uint_t flags)
-{
- ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "rtsig del connection: fd:%d", c->fd);
-
- if ((flags & NGX_DISABLE_EVENT) && c->read->accept) {
-
- ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "rtsig disable connection: fd:%d", c->fd);
-
- c->read->active = 0;
- c->read->disabled = 1;
- return NGX_OK;
- }
-
- if (flags & NGX_CLOSE_EVENT) {
- c->read->active = 0;
- c->write->active = 0;
- return NGX_OK;
- }
-
- if (fcntl(c->fd, F_SETFL, O_RDWR|O_NONBLOCK) == -1) {
- ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
- "fcntl(O_RDWR|O_NONBLOCK) failed");
- return NGX_ERROR;
- }
-
- c->read->active = 0;
- c->write->active = 0;
-
- return NGX_OK;
-}
-
-
-static ngx_int_t
-ngx_rtsig_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags)
-{
- int signo;
- ngx_int_t instance;
- ngx_err_t err;
- siginfo_t si;
- ngx_event_t *rev, *wev;
- ngx_queue_t *queue;
- struct timespec ts, *tp;
- struct sigaction sa;
- ngx_connection_t *c;
- ngx_rtsig_conf_t *rtscf;
-
- if (timer == NGX_TIMER_INFINITE) {
- tp = NULL;
-
- } else {
- ts.tv_sec = timer / 1000;
- ts.tv_nsec = (timer % 1000) * 1000000;
- tp = &ts;
- }
-
- ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
- "rtsig timer: %M", timer);
-
- /* Linux's sigwaitinfo() is sigtimedwait() with the NULL timeout pointer */
-
- signo = sigtimedwait(&set, &si, tp);
-
- if (signo == -1) {
- err = ngx_errno;
-
- ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, err,
- "rtsig signo:%d", signo);
-
- if (flags & NGX_UPDATE_TIME) {
- ngx_time_update();
- }
-
- if (err == NGX_EAGAIN) {
-
- /* timeout */
-
- if (timer != NGX_TIMER_INFINITE) {
- return NGX_AGAIN;
- }
-
- ngx_log_error(NGX_LOG_ALERT, cycle->log, err,
- "sigtimedwait() returned EAGAIN without timeout");
- return NGX_ERROR;
- }
-
- ngx_log_error((err == NGX_EINTR) ? NGX_LOG_INFO : NGX_LOG_ALERT,
- cycle->log, err, "sigtimedwait() failed");
- return NGX_ERROR;
- }
-
- ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
- "rtsig signo:%d fd:%d band:%04Xd",
- signo, si.si_fd, si.si_band);
-
- if (flags & NGX_UPDATE_TIME) {
- ngx_time_update();
- }
-
- rtscf = ngx_event_get_conf(ngx_cycle->conf_ctx, ngx_rtsig_module);
-
- if (signo == (int) rtscf->signo || signo == (int) rtscf->signo + 1) {
-
- if (overflow && (ngx_uint_t) si.si_fd > overflow_current) {
- return NGX_OK;
- }
-
- c = ngx_cycle->files[si.si_fd];
-
- if (c == NULL) {
-
- /* the stale event */
-
- return NGX_OK;
- }
-
- instance = signo - (int) rtscf->signo;
-
- rev = c->read;
-
- if (rev->instance != instance) {
-
- /*
- * the stale event from a file descriptor
- * that was just closed in this iteration
- */
-
- ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
- "rtsig: stale event %p", c);
-
- return NGX_OK;
- }
-
- if ((si.si_band & (POLLIN|POLLHUP|POLLERR)) && rev->active) {
-
- rev->ready = 1;
-
- if (flags & NGX_POST_EVENTS) {
- queue = rev->accept ? &ngx_posted_accept_events
- : &ngx_posted_events;
-
- ngx_post_event(rev, queue);
-
- } else {
- rev->handler(rev);
- }
- }
-
- wev = c->write;
-
- if ((si.si_band & (POLLOUT|POLLHUP|POLLERR)) && wev->active) {
-
- wev->ready = 1;
-
- if (flags & NGX_POST_EVENTS) {
- ngx_post_event(wev, &ngx_posted_events);
-
- } else {
- wev->handler(wev);
- }
- }
-
- return NGX_OK;
-
- } else if (signo == SIGALRM) {
-
- ngx_time_update();
-
- return NGX_OK;
-
- } else if (signo == SIGIO) {
-
- ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
- "rt signal queue overflowed");
-
- /* flush the RT signal queue */
-
- ngx_memzero(&sa, sizeof(struct sigaction));
- sa.sa_handler = SIG_DFL;
- sigemptyset(&sa.sa_mask);
-
- if (sigaction(rtscf->signo, &sa, NULL) == -1) {
- ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
- "sigaction(%d, SIG_DFL) failed", rtscf->signo);
- }
-
- if (sigaction(rtscf->signo + 1, &sa, NULL) == -1) {
- ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
- "sigaction(%d, SIG_DFL) failed", rtscf->signo + 1);
- }
-
- overflow = 1;
- overflow_current = 0;
- ngx_event_actions.process_events = ngx_rtsig_process_overflow;
-
- return NGX_ERROR;
-
- }
-
- ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
- "sigtimedwait() returned unexpected signal: %d", signo);
-
- return NGX_ERROR;
-}
-
-
-static ngx_int_t
-ngx_rtsig_process_overflow(ngx_cycle_t *cycle, ngx_msec_t timer,
- ngx_uint_t flags)
-{
- int name[2], rtsig_max, rtsig_nr, events, ready;
- size_t len;
- ngx_err_t err;
- ngx_uint_t tested, n, i;
- ngx_event_t *rev, *wev;
- ngx_queue_t *queue;
- ngx_connection_t *c;
- ngx_rtsig_conf_t *rtscf;
-
- ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
- "rtsig process overflow");
-
- rtscf = ngx_event_get_conf(ngx_cycle->conf_ctx, ngx_rtsig_module);
-
- tested = 0;
-
- for ( ;; ) {
-
- n = 0;
- while (n < rtscf->overflow_events) {
-
- if (overflow_current == cycle->connection_n) {
- break;
- }
-
- c = cycle->files[overflow_current++];
-
- if (c == NULL || c->fd == -1) {
- continue;
- }
-
- events = 0;
-
- if (c->read->active && c->read->handler) {
- events |= POLLIN;
- }
-
- if (c->write->active && c->write->handler) {
- events |= POLLOUT;
- }
-
- if (events == 0) {
- continue;
- }
-
- overflow_list[n].fd = c->fd;
- overflow_list[n].events = events;
- overflow_list[n].revents = 0;
- n++;
- }
-
- if (n == 0) {
- break;
- }
-
- for ( ;; ) {
- ready = poll(overflow_list, n, 0);
-
- ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
- "rtsig overflow poll:%d", ready);
-
- if (ready == -1) {
- err = ngx_errno;
- ngx_log_error((err == NGX_EINTR) ? NGX_LOG_INFO : NGX_LOG_ALERT,
- cycle->log, 0,
- "poll() failed while the overflow recover");
-
- if (err == NGX_EINTR) {
- continue;
- }
- }
-
- break;
- }
-
- if (ready <= 0) {
- continue;
- }
-
- for (i = 0; i < n; i++) {
- c = cycle->files[overflow_list[i].fd];
-
- if (c == NULL) {
- continue;
- }
-
- rev = c->read;
-
- if (rev->active
- && !rev->closed
- && rev->handler
- && (overflow_list[i].revents
- & (POLLIN|POLLERR|POLLHUP|POLLNVAL)))
- {
- tested++;
-
- rev->ready = 1;
-
- if (flags & NGX_POST_EVENTS) {
- queue = rev->accept ? &ngx_posted_accept_events
- : &ngx_posted_events;
-
- ngx_post_event(rev, queue);
-
- } else {
- rev->handler(rev);
- }
- }
-
- wev = c->write;
-
- if (wev->active
- && !wev->closed
- && wev->handler
- && (overflow_list[i].revents
- & (POLLOUT|POLLERR|POLLHUP|POLLNVAL)))
- {
- tested++;
-
- wev->ready = 1;
-
- if (flags & NGX_POST_EVENTS) {
- ngx_post_event(wev, &ngx_posted_events);
-
- } else {
- wev->handler(wev);
- }
- }
- }
-
- if (tested >= rtscf->overflow_test) {
-
- if (ngx_linux_rtsig_max) {
-
- /*
- * Check the current rt queue length to prevent
- * the new overflow.
- *
- * learn the "/proc/sys/kernel/rtsig-max" value because
- * it can be changed since the last checking
- */
-
- name[0] = CTL_KERN;
- name[1] = KERN_RTSIGMAX;
- len = sizeof(rtsig_max);
-
- if (sysctl(name, 2, &rtsig_max, &len, NULL, 0) == -1) {
- ngx_log_error(NGX_LOG_ALERT, cycle->log, errno,
- "sysctl(KERN_RTSIGMAX) failed");
- return NGX_ERROR;
- }
-
- /* name[0] = CTL_KERN; */
- name[1] = KERN_RTSIGNR;
- len = sizeof(rtsig_nr);
-
- if (sysctl(name, 2, &rtsig_nr, &len, NULL, 0) == -1) {
- ngx_log_error(NGX_LOG_ALERT, cycle->log, errno,
- "sysctl(KERN_RTSIGNR) failed");
- return NGX_ERROR;
- }
-
- /*
- * drain the rt signal queue if the /"proc/sys/kernel/rtsig-nr"
- * is bigger than
- * "/proc/sys/kernel/rtsig-max" / "rtsig_overflow_threshold"
- */
-
- if (rtsig_max / (int) rtscf->overflow_threshold < rtsig_nr) {
- ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
- "rtsig queue state: %d/%d",
- rtsig_nr, rtsig_max);
- while (ngx_rtsig_process_events(cycle, 0, flags) == NGX_OK)
- {
- /* void */
- }
- }
-
- } else {
-
- /*
- * Linux has not KERN_RTSIGMAX since 2.6.6-mm2
- * so drain the rt signal queue unconditionally
- */
-
- while (ngx_rtsig_process_events(cycle, 0, flags) == NGX_OK) {
- /* void */
- }
- }
-
- tested = 0;
- }
- }
-
- if (flags & NGX_UPDATE_TIME) {
- ngx_time_update();
- }
-
- ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
- "rt signal queue overflow recovered");
-
- overflow = 0;
- ngx_event_actions.process_events = ngx_rtsig_process_events;
-
- return NGX_OK;
-}
-
-
-static void *
-ngx_rtsig_create_conf(ngx_cycle_t *cycle)
-{
- ngx_rtsig_conf_t *rtscf;
-
- rtscf = ngx_palloc(cycle->pool, sizeof(ngx_rtsig_conf_t));
- if (rtscf == NULL) {
- return NULL;
- }
-
- rtscf->signo = NGX_CONF_UNSET;
- rtscf->overflow_events = NGX_CONF_UNSET;
- rtscf->overflow_test = NGX_CONF_UNSET;
- rtscf->overflow_threshold = NGX_CONF_UNSET;
-
- return rtscf;
-}
-
-
-static char *
-ngx_rtsig_init_conf(ngx_cycle_t *cycle, void *conf)
-{
- ngx_rtsig_conf_t *rtscf = conf;
-
- /* LinuxThreads use the first 3 RT signals */
- ngx_conf_init_uint_value(rtscf->signo, SIGRTMIN + 10);
-
- ngx_conf_init_uint_value(rtscf->overflow_events, 16);
- ngx_conf_init_uint_value(rtscf->overflow_test, 32);
- ngx_conf_init_uint_value(rtscf->overflow_threshold, 10);
-
- return NGX_CONF_OK;
-}
-
-
-static char *
-ngx_check_ngx_overflow_threshold_bounds(ngx_conf_t *cf, void *post, void *data)
-{
- if (ngx_linux_rtsig_max) {
- return ngx_conf_check_num_bounds(cf, post, data);
- }
-
- ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
- "\"rtsig_overflow_threshold\" is not supported "
- "since Linux 2.6.6-mm2, ignored");
-
- return NGX_CONF_OK;
-}
diff --git a/src/event/modules/ngx_select_module.c b/src/event/modules/ngx_select_module.c
index 46004e5fd..5a976bd92 100644
--- a/src/event/modules/ngx_select_module.c
+++ b/src/event/modules/ngx_select_module.c
@@ -419,15 +419,5 @@ ngx_select_init_conf(ngx_cycle_t *cycle, void *conf)
return NGX_CONF_ERROR;
}
-#if (NGX_OLD_THREADS)
-
- ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
- "select() is not supported in the threaded mode");
- return NGX_CONF_ERROR;
-
-#else
-
return NGX_CONF_OK;
-
-#endif
}
diff --git a/src/event/ngx_event.c b/src/event/ngx_event.c
index 31514c44b..5ca6e826a 100644
--- a/src/event/ngx_event.c
+++ b/src/event/ngx_event.c
@@ -17,7 +17,6 @@ extern ngx_module_t ngx_kqueue_module;
extern ngx_module_t ngx_eventport_module;
extern ngx_module_t ngx_devpoll_module;
extern ngx_module_t ngx_epoll_module;
-extern ngx_module_t ngx_rtsig_module;
extern ngx_module_t ngx_select_module;
@@ -212,7 +211,9 @@ ngx_process_events_and_timers(ngx_cycle_t *cycle)
timer = ngx_event_find_timer();
flags = NGX_UPDATE_TIME;
-#if (NGX_OLD_THREADS)
+#if (NGX_WIN32)
+
+ /* handle signals from master in case of network inactivity */
if (timer == NGX_TIMER_INFINITE || timer > 500) {
timer = 500;
@@ -328,7 +329,7 @@ ngx_handle_read_event(ngx_event_t *rev, ngx_uint_t flags)
}
}
- /* aio, iocp, rtsig */
+ /* iocp */
return NGX_OK;
}
@@ -407,7 +408,7 @@ ngx_handle_write_event(ngx_event_t *wev, size_t lowat)
}
}
- /* aio, iocp, rtsig */
+ /* iocp */
return NGX_OK;
}
@@ -506,7 +507,7 @@ ngx_event_module_init(ngx_cycle_t *cycle)
#endif
shm.size = size;
- shm.name.len = sizeof("nginx_shared_zone");
+ shm.name.len = sizeof("nginx_shared_zone") - 1;
shm.name.data = (u_char *) "nginx_shared_zone";
shm.log = cycle->log;
@@ -815,15 +816,8 @@ ngx_event_process_init(ngx_cycle_t *cycle)
continue;
}
- if (ngx_event_flags & NGX_USE_RTSIG_EVENT) {
- if (ngx_add_conn(c) == NGX_ERROR) {
- return NGX_ERROR;
- }
-
- } else {
- if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
- return NGX_ERROR;
- }
+ if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
+ return NGX_ERROR;
}
#endif
@@ -1189,10 +1183,6 @@ ngx_event_core_init_conf(ngx_cycle_t *cycle, void *conf)
#if (NGX_HAVE_EPOLL) && !(NGX_TEST_BUILD_EPOLL)
int fd;
#endif
-#if (NGX_HAVE_RTSIG)
- ngx_uint_t rtsig;
- ngx_core_conf_t *ccf;
-#endif
ngx_int_t i;
ngx_module_t *module;
ngx_event_module_t *event_module;
@@ -1213,18 +1203,6 @@ ngx_event_core_init_conf(ngx_cycle_t *cycle, void *conf)
#endif
-#if (NGX_HAVE_RTSIG)
-
- if (module == NULL) {
- module = &ngx_rtsig_module;
- rtsig = 1;
-
- } else {
- rtsig = 0;
- }
-
-#endif
-
#if (NGX_HAVE_DEVPOLL)
module = &ngx_devpoll_module;
@@ -1281,31 +1259,5 @@ ngx_event_core_init_conf(ngx_cycle_t *cycle, void *conf)
ngx_conf_init_value(ecf->accept_mutex, 1);
ngx_conf_init_msec_value(ecf->accept_mutex_delay, 500);
-
-#if (NGX_HAVE_RTSIG)
-
- if (!rtsig) {
- return NGX_CONF_OK;
- }
-
- if (ecf->accept_mutex) {
- return NGX_CONF_OK;
- }
-
- ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
-
- if (ccf->worker_processes == 0) {
- return NGX_CONF_OK;
- }
-
- ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
- "the \"rtsig\" method requires \"accept_mutex\" to be on");
-
- return NGX_CONF_ERROR;
-
-#else
-
return NGX_CONF_OK;
-
-#endif
}
diff --git a/src/event/ngx_event.h b/src/event/ngx_event.h
index b7009a3d3..21819dc4d 100644
--- a/src/event/ngx_event.h
+++ b/src/event/ngx_event.h
@@ -34,7 +34,7 @@ struct ngx_event_s {
unsigned accept:1;
- /* used to detect the stale events in kqueue, rtsig, and epoll */
+ /* used to detect the stale events in kqueue and epoll */
unsigned instance:1;
/*
@@ -103,14 +103,8 @@ struct ngx_event_s {
ngx_event_handler_pt handler;
-#if (NGX_HAVE_AIO)
-
#if (NGX_HAVE_IOCP)
ngx_event_ovlp_t ovlp;
-#else
- struct aiocb aiocb;
-#endif
-
#endif
ngx_uint_t index;
@@ -236,7 +230,7 @@ extern ngx_event_actions_t ngx_event_actions;
#define NGX_USE_LOWAT_EVENT 0x00000010
/*
- * The event filter requires to do i/o operation until EAGAIN: epoll, rtsig.
+ * The event filter requires to do i/o operation until EAGAIN: epoll.
*/
#define NGX_USE_GREEDY_EVENT 0x00000020
@@ -246,25 +240,23 @@ extern ngx_event_actions_t ngx_event_actions;
#define NGX_USE_EPOLL_EVENT 0x00000040
/*
- * No need to add or delete the event filters: rtsig.
+ * Obsolete.
*/
#define NGX_USE_RTSIG_EVENT 0x00000080
/*
- * No need to add or delete the event filters: overlapped, aio_read,
- * aioread, io_submit.
+ * Obsolete.
*/
#define NGX_USE_AIO_EVENT 0x00000100
/*
* Need to add socket or handle only once: i/o completion port.
- * It also requires NGX_HAVE_AIO and NGX_USE_AIO_EVENT to be set.
*/
#define NGX_USE_IOCP_EVENT 0x00000200
/*
* The event filter has no opaque data and requires file descriptors table:
- * poll, /dev/poll, rtsig.
+ * poll, /dev/poll.
*/
#define NGX_USE_FD_EVENT 0x00000400
@@ -289,7 +281,7 @@ extern ngx_event_actions_t ngx_event_actions;
/*
* The event filter is deleted just before the closing file.
* Has no meaning for select and poll.
- * kqueue, epoll, rtsig, eventport: allows to avoid explicit delete,
+ * kqueue, epoll, eventport: allows to avoid explicit delete,
* because filter automatically is deleted
* on file close,
*
diff --git a/src/event/ngx_event_accept.c b/src/event/ngx_event_accept.c
index 6285baafb..3f1c0b164 100644
--- a/src/event/ngx_event_accept.c
+++ b/src/event/ngx_event_accept.c
@@ -42,10 +42,7 @@ ngx_event_accept(ngx_event_t *ev)
ecf = ngx_event_get_conf(ngx_cycle->conf_ctx, ngx_event_core_module);
- if (ngx_event_flags & NGX_USE_RTSIG_EVENT) {
- ev->available = 1;
-
- } else if (!(ngx_event_flags & NGX_USE_KQUEUE_EVENT)) {
+ if (!(ngx_event_flags & NGX_USE_KQUEUE_EVENT)) {
ev->available = ecf->multi_accept;
}
@@ -176,10 +173,10 @@ ngx_event_accept(ngx_event_t *ev)
return;
}
- /* set a blocking mode for aio and non-blocking mode for others */
+ /* set a blocking mode for iocp and non-blocking mode for others */
if (ngx_inherited_nonblocking) {
- if (ngx_event_flags & NGX_USE_AIO_EVENT) {
+ if (ngx_event_flags & NGX_USE_IOCP_EVENT) {
if (ngx_blocking(s) == -1) {
ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_socket_errno,
ngx_blocking_n " failed");
@@ -189,7 +186,7 @@ ngx_event_accept(ngx_event_t *ev)
}
} else {
- if (!(ngx_event_flags & (NGX_USE_AIO_EVENT|NGX_USE_RTSIG_EVENT))) {
+ if (!(ngx_event_flags & NGX_USE_IOCP_EVENT)) {
if (ngx_nonblocking(s) == -1) {
ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_socket_errno,
ngx_nonblocking_n " failed");
@@ -232,8 +229,7 @@ ngx_event_accept(ngx_event_t *ev)
wev->ready = 1;
- if (ngx_event_flags & (NGX_USE_AIO_EVENT|NGX_USE_RTSIG_EVENT)) {
- /* rtsig, aio, iocp */
+ if (ngx_event_flags & NGX_USE_IOCP_EVENT) {
rev->ready = 1;
}
@@ -375,10 +371,7 @@ ngx_trylock_accept_mutex(ngx_cycle_t *cycle)
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"accept mutex locked");
- if (ngx_accept_mutex_held
- && ngx_accept_events == 0
- && !(ngx_event_flags & NGX_USE_RTSIG_EVENT))
- {
+ if (ngx_accept_mutex_held && ngx_accept_events == 0) {
return NGX_OK;
}
@@ -424,16 +417,8 @@ ngx_enable_accept_events(ngx_cycle_t *cycle)
continue;
}
- if (ngx_event_flags & NGX_USE_RTSIG_EVENT) {
-
- if (ngx_add_conn(c) == NGX_ERROR) {
- return NGX_ERROR;
- }
-
- } else {
- if (ngx_add_event(c->read, NGX_READ_EVENT, 0) == NGX_ERROR) {
- return NGX_ERROR;
- }
+ if (ngx_add_event(c->read, NGX_READ_EVENT, 0) == NGX_ERROR) {
+ return NGX_ERROR;
}
}
@@ -457,17 +442,10 @@ ngx_disable_accept_events(ngx_cycle_t *cycle)
continue;
}
- if (ngx_event_flags & NGX_USE_RTSIG_EVENT) {
- if (ngx_del_conn(c, NGX_DISABLE_EVENT) == NGX_ERROR) {
- return NGX_ERROR;
- }
-
- } else {
- if (ngx_del_event(c->read, NGX_READ_EVENT, NGX_DISABLE_EVENT)
- == NGX_ERROR)
- {
- return NGX_ERROR;
- }
+ if (ngx_del_event(c->read, NGX_READ_EVENT, NGX_DISABLE_EVENT)
+ == NGX_ERROR)
+ {
+ return NGX_ERROR;
}
}
diff --git a/src/event/ngx_event_connect.c b/src/event/ngx_event_connect.c
index a09d2e5d7..118695879 100644
--- a/src/event/ngx_event_connect.c
+++ b/src/event/ngx_event_connect.c
@@ -171,13 +171,11 @@ ngx_event_connect_peer(ngx_peer_connection_t *pc)
return NGX_OK;
}
- if (ngx_event_flags & NGX_USE_AIO_EVENT) {
+ if (ngx_event_flags & NGX_USE_IOCP_EVENT) {
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, pc->log, ngx_socket_errno,
"connect(): %d", rc);
- /* aio, iocp */
-
if (ngx_blocking(s) == -1) {
ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,
ngx_blocking_n " failed");
diff --git a/src/http/modules/ngx_http_limit_req_module.c b/src/http/modules/ngx_http_limit_req_module.c
index 5d5cbe944..9059ac376 100644
--- a/src/http/modules/ngx_http_limit_req_module.c
+++ b/src/http/modules/ngx_http_limit_req_module.c
@@ -919,13 +919,6 @@ ngx_http_limit_req(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
return NGX_CONF_ERROR;
}
- if (shm_zone->data == NULL) {
- ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
- "unknown limit_req_zone \"%V\"",
- &shm_zone->shm.name);
- return NGX_CONF_ERROR;
- }
-
limits = lrcf->limits.elts;
if (limits == NULL) {
diff --git a/src/http/modules/ngx_http_memcached_module.c b/src/http/modules/ngx_http_memcached_module.c
index 17f1d8e10..8341b92d3 100644
--- a/src/http/modules/ngx_http_memcached_module.c
+++ b/src/http/modules/ngx_http_memcached_module.c
@@ -618,6 +618,7 @@ ngx_http_memcached_create_loc_conf(ngx_conf_t *cf)
conf->upstream.intercept_404 = 1;
conf->upstream.pass_request_headers = 0;
conf->upstream.pass_request_body = 0;
+ conf->upstream.force_ranges = 1;
conf->index = NGX_CONF_UNSET;
conf->gzip_flag = NGX_CONF_UNSET_UINT;
diff --git a/src/http/modules/ngx_http_upstream_hash_module.c b/src/http/modules/ngx_http_upstream_hash_module.c
index a1f0ff3c9..e3dd0b6a5 100644
--- a/src/http/modules/ngx_http_upstream_hash_module.c
+++ b/src/http/modules/ngx_http_upstream_hash_module.c
@@ -170,13 +170,16 @@ ngx_http_upstream_get_hash_peer(ngx_peer_connection_t *pc, void *data)
uint32_t hash;
ngx_int_t w;
uintptr_t m;
- ngx_uint_t i, n, p;
+ ngx_uint_t n, p;
ngx_http_upstream_rr_peer_t *peer;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
"get hash peer, try: %ui", pc->tries);
+ ngx_http_upstream_rr_peers_wlock(hp->rrp.peers);
+
if (hp->tries > 20 || hp->rrp.peers->single) {
+ ngx_http_upstream_rr_peers_unlock(hp->rrp.peers);
return hp->get_rr_peer(pc, &hp->rrp);
}
@@ -208,20 +211,14 @@ ngx_http_upstream_get_hash_peer(ngx_peer_connection_t *pc, void *data)
hp->hash += hash;
hp->rehash++;
- if (!hp->rrp.peers->weighted) {
- p = hp->hash % hp->rrp.peers->number;
-
- } else {
- w = hp->hash % hp->rrp.peers->total_weight;
-
- for (i = 0; i < hp->rrp.peers->number; i++) {
- w -= hp->rrp.peers->peer[i].weight;
- if (w < 0) {
- break;
- }
- }
+ w = hp->hash % hp->rrp.peers->total_weight;
+ peer = hp->rrp.peers->peer;
+ p = 0;
- p = i;
+ while (w >= peer->weight) {
+ w -= peer->weight;
+ peer = peer->next;
+ p++;
}
n = p / (8 * sizeof(uintptr_t));
@@ -234,8 +231,6 @@ ngx_http_upstream_get_hash_peer(ngx_peer_connection_t *pc, void *data)
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
"get hash peer, value:%uD, peer:%ui", hp->hash, p);
- peer = &hp->rrp.peers->peer[p];
-
if (peer->down) {
goto next;
}
@@ -252,20 +247,25 @@ ngx_http_upstream_get_hash_peer(ngx_peer_connection_t *pc, void *data)
next:
if (++hp->tries > 20) {
+ ngx_http_upstream_rr_peers_unlock(hp->rrp.peers);
return hp->get_rr_peer(pc, &hp->rrp);
}
}
- hp->rrp.current = p;
+ hp->rrp.current = peer;
pc->sockaddr = peer->sockaddr;
pc->socklen = peer->socklen;
pc->name = &peer->name;
+ peer->conns++;
+
if (now - peer->checked > peer->fail_timeout) {
peer->checked = now;
}
+ ngx_http_upstream_rr_peers_unlock(hp->rrp.peers);
+
hp->rrp.tried[n] |= m;
return NGX_OK;
@@ -304,8 +304,7 @@ ngx_http_upstream_init_chash(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us)
points->number = 0;
- for (i = 0; i < peers->number; i++) {
- peer = &peers->peer[i];
+ for (peer = peers->peer; peer; peer = peer->next) {
server = &peer->server;
/*
@@ -458,8 +457,13 @@ ngx_http_upstream_init_chash_peer(ngx_http_request_t *r,
hcf = ngx_http_conf_upstream_srv_conf(us, ngx_http_upstream_hash_module);
hash = ngx_crc32_long(hp->key.data, hp->key.len);
+
+ ngx_http_upstream_rr_peers_rlock(hp->rrp.peers);
+
hp->hash = ngx_http_upstream_find_chash_point(hcf->points, hash);
+ ngx_http_upstream_rr_peers_unlock(hp->rrp.peers);
+
return NGX_OK;
}
@@ -473,7 +477,7 @@ ngx_http_upstream_get_chash_peer(ngx_peer_connection_t *pc, void *data)
intptr_t m;
ngx_str_t *server;
ngx_int_t total;
- ngx_uint_t i, n;
+ ngx_uint_t i, n, best_i;
ngx_http_upstream_rr_peer_t *peer, *best;
ngx_http_upstream_chash_point_t *point;
ngx_http_upstream_chash_points_t *points;
@@ -482,6 +486,8 @@ ngx_http_upstream_get_chash_peer(ngx_peer_connection_t *pc, void *data)
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
"get consistent hash peer, try: %ui", pc->tries);
+ ngx_http_upstream_rr_peers_wlock(hp->rrp.peers);
+
pc->cached = 0;
pc->connection = NULL;
@@ -499,9 +505,13 @@ ngx_http_upstream_get_chash_peer(ngx_peer_connection_t *pc, void *data)
hp->hash, server);
best = NULL;
+ best_i = 0;
total = 0;
- for (i = 0; i < hp->rrp.peers->number; i++) {
+ for (peer = hp->rrp.peers->peer, i = 0;
+ peer;
+ peer = peer->next, i++)
+ {
n = i / (8 * sizeof(uintptr_t));
m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));
@@ -510,8 +520,6 @@ ngx_http_upstream_get_chash_peer(ngx_peer_connection_t *pc, void *data)
continue;
}
- peer = &hp->rrp.peers->peer[i];
-
if (peer->down) {
continue;
}
@@ -539,39 +547,46 @@ ngx_http_upstream_get_chash_peer(ngx_peer_connection_t *pc, void *data)
if (best == NULL || peer->current_weight > best->current_weight) {
best = peer;
+ best_i = i;
}
}
if (best) {
best->current_weight -= total;
-
- i = best - &hp->rrp.peers->peer[0];
-
- hp->rrp.current = i;
-
- n = i / (8 * sizeof(uintptr_t));
- m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));
-
- hp->rrp.tried[n] |= m;
-
- if (now - best->checked > best->fail_timeout) {
- best->checked = now;
- }
-
- pc->sockaddr = best->sockaddr;
- pc->socklen = best->socklen;
- pc->name = &best->name;
-
- return NGX_OK;
+ goto found;
}
hp->hash++;
hp->tries++;
if (hp->tries >= points->number) {
+ ngx_http_upstream_rr_peers_unlock(hp->rrp.peers);
return NGX_BUSY;
}
}
+
+found:
+
+ hp->rrp.current = best;
+
+ pc->sockaddr = best->sockaddr;
+ pc->socklen = best->socklen;
+ pc->name = &best->name;
+
+ best->conns++;
+
+ if (now - best->checked > best->fail_timeout) {
+ best->checked = now;
+ }
+
+ ngx_http_upstream_rr_peers_unlock(hp->rrp.peers);
+
+ n = best_i / (8 * sizeof(uintptr_t));
+ m = (uintptr_t) 1 << best_i % (8 * sizeof(uintptr_t));
+
+ hp->rrp.tried[n] |= m;
+
+ return NGX_OK;
}
diff --git a/src/http/modules/ngx_http_upstream_ip_hash_module.c b/src/http/modules/ngx_http_upstream_ip_hash_module.c
index 148d73a84..401b58e5c 100644
--- a/src/http/modules/ngx_http_upstream_ip_hash_module.c
+++ b/src/http/modules/ngx_http_upstream_ip_hash_module.c
@@ -161,7 +161,10 @@ ngx_http_upstream_get_ip_hash_peer(ngx_peer_connection_t *pc, void *data)
/* TODO: cached */
+ ngx_http_upstream_rr_peers_wlock(iphp->rrp.peers);
+
if (iphp->tries > 20 || iphp->rrp.peers->single) {
+ ngx_http_upstream_rr_peers_unlock(iphp->rrp.peers);
return iphp->get_rr_peer(pc, &iphp->rrp);
}
@@ -178,20 +181,14 @@ ngx_http_upstream_get_ip_hash_peer(ngx_peer_connection_t *pc, void *data)
hash = (hash * 113 + iphp->addr[i]) % 6271;
}
- if (!iphp->rrp.peers->weighted) {
- p = hash % iphp->rrp.peers->number;
-
- } else {
- w = hash % iphp->rrp.peers->total_weight;
-
- for (i = 0; i < iphp->rrp.peers->number; i++) {
- w -= iphp->rrp.peers->peer[i].weight;
- if (w < 0) {
- break;
- }
- }
+ w = hash % iphp->rrp.peers->total_weight;
+ peer = iphp->rrp.peers->peer;
+ p = 0;
- p = i;
+ while (w >= peer->weight) {
+ w -= peer->weight;
+ peer = peer->next;
+ p++;
}
n = p / (8 * sizeof(uintptr_t));
@@ -204,49 +201,40 @@ ngx_http_upstream_get_ip_hash_peer(ngx_peer_connection_t *pc, void *data)
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
"get ip hash peer, hash: %ui %04XA", p, m);
- peer = &iphp->rrp.peers->peer[p];
-
- /* ngx_lock_mutex(iphp->rrp.peers->mutex); */
-
if (peer->down) {
- goto next_try;
+ goto next;
}
if (peer->max_fails
&& peer->fails >= peer->max_fails
&& now - peer->checked <= peer->fail_timeout)
{
- goto next_try;
+ goto next;
}
break;
- next_try:
-
- iphp->rrp.tried[n] |= m;
-
- /* ngx_unlock_mutex(iphp->rrp.peers->mutex); */
-
- pc->tries--;
-
next:
if (++iphp->tries > 20) {
+ ngx_http_upstream_rr_peers_unlock(iphp->rrp.peers);
return iphp->get_rr_peer(pc, &iphp->rrp);
}
}
- iphp->rrp.current = p;
+ iphp->rrp.current = peer;
pc->sockaddr = peer->sockaddr;
pc->socklen = peer->socklen;
pc->name = &peer->name;
+ peer->conns++;
+
if (now - peer->checked > peer->fail_timeout) {
peer->checked = now;
}
- /* ngx_unlock_mutex(iphp->rrp.peers->mutex); */
+ ngx_http_upstream_rr_peers_unlock(iphp->rrp.peers);
iphp->rrp.tried[n] |= m;
iphp->hash = hash;
diff --git a/src/http/modules/ngx_http_upstream_least_conn_module.c b/src/http/modules/ngx_http_upstream_least_conn_module.c
index 623bc9b10..92951bd20 100644
--- a/src/http/modules/ngx_http_upstream_least_conn_module.c
+++ b/src/http/modules/ngx_http_upstream_least_conn_module.c
@@ -10,29 +10,10 @@
#include <ngx_http.h>
-typedef struct {
- ngx_uint_t *conns;
-} ngx_http_upstream_least_conn_conf_t;
-
-
-typedef struct {
- /* the round robin data must be first */
- ngx_http_upstream_rr_peer_data_t rrp;
-
- ngx_uint_t *conns;
-
- ngx_event_get_peer_pt get_rr_peer;
- ngx_event_free_peer_pt free_rr_peer;
-} ngx_http_upstream_lc_peer_data_t;
-
-
static ngx_int_t ngx_http_upstream_init_least_conn_peer(ngx_http_request_t *r,
ngx_http_upstream_srv_conf_t *us);
static ngx_int_t ngx_http_upstream_get_least_conn_peer(
ngx_peer_connection_t *pc, void *data);
-static void ngx_http_upstream_free_least_conn_peer(ngx_peer_connection_t *pc,
- void *data, ngx_uint_t state);
-static void *ngx_http_upstream_least_conn_create_conf(ngx_conf_t *cf);
static char *ngx_http_upstream_least_conn(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
@@ -57,7 +38,7 @@ static ngx_http_module_t ngx_http_upstream_least_conn_module_ctx = {
NULL, /* create main configuration */
NULL, /* init main configuration */
- ngx_http_upstream_least_conn_create_conf, /* create server configuration */
+ NULL, /* create server configuration */
NULL, /* merge server configuration */
NULL, /* create location configuration */
@@ -85,10 +66,6 @@ static ngx_int_t
ngx_http_upstream_init_least_conn(ngx_conf_t *cf,
ngx_http_upstream_srv_conf_t *us)
{
- ngx_uint_t n;
- ngx_http_upstream_rr_peers_t *peers;
- ngx_http_upstream_least_conn_conf_t *lcf;
-
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cf->log, 0,
"init least conn");
@@ -96,22 +73,6 @@ ngx_http_upstream_init_least_conn(ngx_conf_t *cf,
return NGX_ERROR;
}
- peers = us->peer.data;
-
- n = peers->number;
-
- if (peers->next) {
- n += peers->next->number;
- }
-
- lcf = ngx_http_conf_upstream_srv_conf(us,
- ngx_http_upstream_least_conn_module);
-
- lcf->conns = ngx_pcalloc(cf->pool, sizeof(ngx_uint_t) * n);
- if (lcf->conns == NULL) {
- return NGX_ERROR;
- }
-
us->peer.init = ngx_http_upstream_init_least_conn_peer;
return NGX_OK;
@@ -122,33 +83,14 @@ static ngx_int_t
ngx_http_upstream_init_least_conn_peer(ngx_http_request_t *r,
ngx_http_upstream_srv_conf_t *us)
{
- ngx_http_upstream_lc_peer_data_t *lcp;
- ngx_http_upstream_least_conn_conf_t *lcf;
-
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"init least conn peer");
- lcf = ngx_http_conf_upstream_srv_conf(us,
- ngx_http_upstream_least_conn_module);
-
- lcp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_lc_peer_data_t));
- if (lcp == NULL) {
- return NGX_ERROR;
- }
-
- lcp->conns = lcf->conns;
-
- r->upstream->peer.data = &lcp->rrp;
-
if (ngx_http_upstream_init_round_robin_peer(r, us) != NGX_OK) {
return NGX_ERROR;
}
r->upstream->peer.get = ngx_http_upstream_get_least_conn_peer;
- r->upstream->peer.free = ngx_http_upstream_free_least_conn_peer;
-
- lcp->get_rr_peer = ngx_http_upstream_get_round_robin_peer;
- lcp->free_rr_peer = ngx_http_upstream_free_round_robin_peer;
return NGX_OK;
}
@@ -157,7 +99,7 @@ ngx_http_upstream_init_least_conn_peer(ngx_http_request_t *r,
static ngx_int_t
ngx_http_upstream_get_least_conn_peer(ngx_peer_connection_t *pc, void *data)
{
- ngx_http_upstream_lc_peer_data_t *lcp = data;
+ ngx_http_upstream_rr_peer_data_t *rrp = data;
time_t now;
uintptr_t m;
@@ -169,8 +111,8 @@ ngx_http_upstream_get_least_conn_peer(ngx_peer_connection_t *pc, void *data)
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
"get least conn peer, try: %ui", pc->tries);
- if (lcp->rrp.peers->single) {
- return lcp->get_rr_peer(pc, &lcp->rrp);
+ if (rrp->peers->single) {
+ return ngx_http_upstream_get_round_robin_peer(pc, rrp);
}
pc->cached = 0;
@@ -178,7 +120,9 @@ ngx_http_upstream_get_least_conn_peer(ngx_peer_connection_t *pc, void *data)
now = ngx_time();
- peers = lcp->rrp.peers;
+ peers = rrp->peers;
+
+ ngx_http_upstream_rr_peers_wlock(peers);
best = NULL;
total = 0;
@@ -188,17 +132,18 @@ ngx_http_upstream_get_least_conn_peer(ngx_peer_connection_t *pc, void *data)
p = 0;
#endif
- for (i = 0; i < peers->number; i++) {
+ for (peer = peers->peer, i = 0;
+ peer;
+ peer = peer->next, i++)
+ {
n = i / (8 * sizeof(uintptr_t));
m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));
- if (lcp->rrp.tried[n] & m) {
+ if (rrp->tried[n] & m) {
continue;
}
- peer = &peers->peer[i];
-
if (peer->down) {
continue;
}
@@ -217,15 +162,13 @@ ngx_http_upstream_get_least_conn_peer(ngx_peer_connection_t *pc, void *data)
*/
if (best == NULL
- || lcp->conns[i] * best->weight < lcp->conns[p] * peer->weight)
+ || peer->conns * best->weight < best->conns * peer->weight)
{
best = peer;
many = 0;
p = i;
- } else if (lcp->conns[i] * best->weight
- == lcp->conns[p] * peer->weight)
- {
+ } else if (peer->conns * best->weight == best->conns * peer->weight) {
many = 1;
}
}
@@ -241,22 +184,22 @@ ngx_http_upstream_get_least_conn_peer(ngx_peer_connection_t *pc, void *data)
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,
"get least conn peer, many");
- for (i = p; i < peers->number; i++) {
-
+ for (peer = best, i = p;
+ peer;
+ peer = peer->next, i++)
+ {
n = i / (8 * sizeof(uintptr_t));
m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));
- if (lcp->rrp.tried[n] & m) {
+ if (rrp->tried[n] & m) {
continue;
}
- peer = &peers->peer[i];
-
if (peer->down) {
continue;
}
- if (lcp->conns[i] * best->weight != lcp->conns[p] * peer->weight) {
+ if (peer->conns * best->weight != best->conns * peer->weight) {
continue;
}
@@ -291,13 +234,16 @@ ngx_http_upstream_get_least_conn_peer(ngx_peer_connection_t *pc, void *data)
pc->socklen = best->socklen;
pc->name = &best->name;
- lcp->rrp.current = p;
+ best->conns++;
+
+ rrp->current = best;
n = p / (8 * sizeof(uintptr_t));
m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t));
- lcp->rrp.tried[n] |= m;
- lcp->conns[p]++;
+ rrp->tried[n] |= m;
+
+ ngx_http_upstream_rr_peers_unlock(peers);
return NGX_OK;
@@ -307,77 +253,40 @@ failed:
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,
"get least conn peer, backup servers");
- lcp->conns += peers->number;
-
- lcp->rrp.peers = peers->next;
+ rrp->peers = peers->next;
- n = (lcp->rrp.peers->number + (8 * sizeof(uintptr_t) - 1))
+ n = (rrp->peers->number + (8 * sizeof(uintptr_t) - 1))
/ (8 * sizeof(uintptr_t));
for (i = 0; i < n; i++) {
- lcp->rrp.tried[i] = 0;
+ rrp->tried[i] = 0;
}
- rc = ngx_http_upstream_get_least_conn_peer(pc, lcp);
+ ngx_http_upstream_rr_peers_unlock(peers);
+
+ rc = ngx_http_upstream_get_least_conn_peer(pc, rrp);
if (rc != NGX_BUSY) {
return rc;
}
+
+ ngx_http_upstream_rr_peers_wlock(peers);
}
/* all peers failed, mark them as live for quick recovery */
- for (i = 0; i < peers->number; i++) {
- peers->peer[i].fails = 0;
+ for (peer = peers->peer; peer; peer = peer->next) {
+ peer->fails = 0;
}
+ ngx_http_upstream_rr_peers_unlock(peers);
+
pc->name = peers->name;
return NGX_BUSY;
}
-static void
-ngx_http_upstream_free_least_conn_peer(ngx_peer_connection_t *pc,
- void *data, ngx_uint_t state)
-{
- ngx_http_upstream_lc_peer_data_t *lcp = data;
-
- ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
- "free least conn peer %ui %ui", pc->tries, state);
-
- if (lcp->rrp.peers->single) {
- lcp->free_rr_peer(pc, &lcp->rrp, state);
- return;
- }
-
- lcp->conns[lcp->rrp.current]--;
-
- lcp->free_rr_peer(pc, &lcp->rrp, state);
-}
-
-
-static void *
-ngx_http_upstream_least_conn_create_conf(ngx_conf_t *cf)
-{
- ngx_http_upstream_least_conn_conf_t *conf;
-
- conf = ngx_pcalloc(cf->pool,
- sizeof(ngx_http_upstream_least_conn_conf_t));
- if (conf == NULL) {
- return NULL;
- }
-
- /*
- * set by ngx_pcalloc():
- *
- * conf->conns = NULL;
- */
-
- return conf;
-}
-
-
static char *
ngx_http_upstream_least_conn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
diff --git a/src/http/modules/ngx_http_upstream_zone_module.c b/src/http/modules/ngx_http_upstream_zone_module.c
new file mode 100644
index 000000000..ea7c43b84
--- /dev/null
+++ b/src/http/modules/ngx_http_upstream_zone_module.c
@@ -0,0 +1,227 @@
+
+/*
+ * Copyright (C) Ruslan Ermilov
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+static char *ngx_http_upstream_zone(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static ngx_int_t ngx_http_upstream_init_zone(ngx_shm_zone_t *shm_zone,
+ void *data);
+static ngx_int_t ngx_http_upstream_zone_copy_peers(ngx_slab_pool_t *shpool,
+ ngx_http_upstream_srv_conf_t *uscf);
+
+
+static ngx_command_t ngx_http_upstream_zone_commands[] = {
+
+ { ngx_string("zone"),
+ NGX_HTTP_UPS_CONF|NGX_CONF_TAKE12,
+ ngx_http_upstream_zone,
+ 0,
+ 0,
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_upstream_zone_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 */
+};
+
+
+ngx_module_t ngx_http_upstream_zone_module = {
+ NGX_MODULE_V1,
+ &ngx_http_upstream_zone_module_ctx, /* module context */
+ ngx_http_upstream_zone_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static char *
+ngx_http_upstream_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ssize_t size;
+ ngx_str_t *value;
+ ngx_http_upstream_srv_conf_t *uscf;
+ ngx_http_upstream_main_conf_t *umcf;
+
+ uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);
+ umcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_upstream_module);
+
+ value = cf->args->elts;
+
+ if (!value[1].len) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid zone name \"%V\"", &value[1]);
+ return NGX_CONF_ERROR;
+ }
+
+ if (cf->args->nelts == 3) {
+ size = ngx_parse_size(&value[2]);
+
+ if (size == NGX_ERROR) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid zone size \"%V\"", &value[2]);
+ return NGX_CONF_ERROR;
+ }
+
+ if (size < (ssize_t) (8 * ngx_pagesize)) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "zone \"%V\" is too small", &value[1]);
+ return NGX_CONF_ERROR;
+ }
+
+ } else {
+ size = 0;
+ }
+
+ uscf->shm_zone = ngx_shared_memory_add(cf, &value[1], size,
+ &ngx_http_upstream_module);
+ if (uscf->shm_zone == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ uscf->shm_zone->init = ngx_http_upstream_init_zone;
+ uscf->shm_zone->data = umcf;
+
+ uscf->shm_zone->noreuse = 1;
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_init_zone(ngx_shm_zone_t *shm_zone, void *data)
+{
+ size_t len;
+ ngx_uint_t i;
+ ngx_slab_pool_t *shpool;
+ ngx_http_upstream_srv_conf_t *uscf, **uscfp;
+ ngx_http_upstream_main_conf_t *umcf;
+
+ shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
+
+ if (shm_zone->shm.exists) {
+ return NGX_ERROR;
+ }
+
+ len = sizeof(" in upstream zone \"\"") + shm_zone->shm.name.len;
+
+ shpool->log_ctx = ngx_slab_alloc(shpool, len);
+ if (shpool->log_ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_sprintf(shpool->log_ctx, " in upstream zone \"%V\"%Z",
+ &shm_zone->shm.name);
+
+
+ /* copy peers to shared memory */
+
+ umcf = shm_zone->data;
+ uscfp = umcf->upstreams.elts;
+
+ for (i = 0; i < umcf->upstreams.nelts; i++) {
+ uscf = uscfp[i];
+
+ if (uscf->shm_zone != shm_zone) {
+ continue;
+ }
+
+ if (ngx_http_upstream_zone_copy_peers(shpool, uscf) != NGX_OK) {
+ return NGX_ERROR;
+ }
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_zone_copy_peers(ngx_slab_pool_t *shpool,
+ ngx_http_upstream_srv_conf_t *uscf)
+{
+ ngx_http_upstream_rr_peer_t *peer, **peerp;
+ ngx_http_upstream_rr_peers_t *peers, *backup;
+
+ peers = ngx_slab_alloc(shpool, sizeof(ngx_http_upstream_rr_peers_t));
+ if (peers == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(peers, uscf->peer.data, sizeof(ngx_http_upstream_rr_peers_t));
+
+ peers->shpool = shpool;
+
+ for (peerp = &peers->peer; *peerp; peerp = &peer->next) {
+ /* pool is unlocked */
+ peer = ngx_slab_calloc_locked(shpool,
+ sizeof(ngx_http_upstream_rr_peer_t));
+ if (peer == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(peer, *peerp, sizeof(ngx_http_upstream_rr_peer_t));
+
+ *peerp = peer;
+ }
+
+ if (peers->next == NULL) {
+ goto done;
+ }
+
+ backup = ngx_slab_alloc(shpool, sizeof(ngx_http_upstream_rr_peers_t));
+ if (backup == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(backup, peers->next, sizeof(ngx_http_upstream_rr_peers_t));
+
+ backup->shpool = shpool;
+
+ for (peerp = &backup->peer; *peerp; peerp = &peer->next) {
+ /* pool is unlocked */
+ peer = ngx_slab_calloc_locked(shpool,
+ sizeof(ngx_http_upstream_rr_peer_t));
+ if (peer == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(peer, *peerp, sizeof(ngx_http_upstream_rr_peer_t));
+
+ *peerp = peer;
+ }
+
+ peers->next = backup;
+
+done:
+
+ uscf->peer.data = peers;
+
+ return NGX_OK;
+}
diff --git a/src/http/ngx_http.c b/src/http/ngx_http.c
index 72981c17b..924c4b548 100644
--- a/src/http/ngx_http.c
+++ b/src/http/ngx_http.c
@@ -1220,7 +1220,7 @@ ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
{
u_char *p;
size_t len, off;
- ngx_uint_t i, default_server;
+ ngx_uint_t i, default_server, proxy_protocol;
struct sockaddr *sa;
ngx_http_conf_addr_t *addr;
#if (NGX_HAVE_UNIX_DOMAIN)
@@ -1281,6 +1281,8 @@ ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
/* preserve default_server bit during listen options overwriting */
default_server = addr[i].opt.default_server;
+ proxy_protocol = lsopt->proxy_protocol || addr[i].opt.proxy_protocol;
+
#if (NGX_HTTP_SSL)
ssl = lsopt->ssl || addr[i].opt.ssl;
#endif
@@ -1314,6 +1316,7 @@ ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
}
addr[i].opt.default_server = default_server;
+ addr[i].opt.proxy_protocol = proxy_protocol;
#if (NGX_HTTP_SSL)
addr[i].opt.ssl = ssl;
#endif
diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c
index 096a561c4..81960414f 100644
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -1445,7 +1445,7 @@ ngx_http_update_location_config(ngx_http_request_t *r)
}
if (r == r->main) {
- ngx_http_set_connection_log(r->connection, clcf->error_log);
+ ngx_set_connection_log(r->connection, clcf->error_log);
}
if ((ngx_io.flags & NGX_IO_SENDFILE) && clcf->sendfile) {
diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c
index 9f98799a1..2669b522c 100644
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -349,7 +349,7 @@ ngx_http_init_connection(ngx_connection_t *c)
}
if (rev->ready) {
- /* the deferred accept(), rtsig, aio, iocp */
+ /* the deferred accept(), iocp */
if (ngx_use_accept_mutex) {
ngx_post_event(rev, &ngx_posted_events);
@@ -543,7 +543,7 @@ ngx_http_create_request(ngx_connection_t *c)
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
- ngx_http_set_connection_log(r->connection, clcf->error_log);
+ ngx_set_connection_log(r->connection, clcf->error_log);
r->header_in = hc->nbusy ? hc->busy[0] : c->buffer;
@@ -867,7 +867,7 @@ ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg)
clcf = ngx_http_get_module_loc_conf(hc->conf_ctx, ngx_http_core_module);
- ngx_http_set_connection_log(c, clcf->error_log);
+ ngx_set_connection_log(c, clcf->error_log);
sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module);
@@ -2073,7 +2073,7 @@ ngx_http_set_virtual_server(ngx_http_request_t *r, ngx_str_t *host)
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
- ngx_http_set_connection_log(r->connection, clcf->error_log);
+ ngx_set_connection_log(r->connection, clcf->error_log);
return NGX_OK;
}
diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h
index ead4d236f..3954de3f1 100644
--- a/src/http/ngx_http_request.h
+++ b/src/http/ngx_http_request.h
@@ -586,17 +586,6 @@ extern ngx_http_header_t ngx_http_headers_in[];
extern ngx_http_header_out_t ngx_http_headers_out[];
-#define ngx_http_set_connection_log(c, l) \
- \
- c->log->file = l->file; \
- c->log->next = l->next; \
- c->log->writer = l->writer; \
- c->log->wdata = l->wdata; \
- if (!(c->log->log_level & NGX_LOG_DEBUG_CONNECTION)) { \
- c->log->log_level = l->log_level; \
- }
-
-
#define ngx_http_set_log_request(log, r) \
((ngx_http_log_ctx_t *) log->data)->current_request = r
diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c
index 56091fa98..ba01d1ce5 100644
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -2911,7 +2911,7 @@ ngx_http_upstream_send_response(ngx_http_request_t *r, ngx_http_upstream_t *u)
p->buf_to_file->temporary = 1;
}
- if (ngx_event_flags & NGX_USE_AIO_EVENT) {
+ if (ngx_event_flags & NGX_USE_IOCP_EVENT) {
/* the posted aio operation may corrupt a shadow buffer */
p->single_buf = 1;
}
diff --git a/src/http/ngx_http_upstream.h b/src/http/ngx_http_upstream.h
index 895a55966..28e4142fc 100644
--- a/src/http/ngx_http_upstream.h
+++ b/src/http/ngx_http_upstream.h
@@ -123,6 +123,10 @@ struct ngx_http_upstream_srv_conf_s {
in_port_t port;
in_port_t default_port;
ngx_uint_t no_port; /* unsigned no_port:1 */
+
+#if (NGX_HTTP_UPSTREAM_ZONE)
+ ngx_shm_zone_t *shm_zone;
+#endif
};
diff --git a/src/http/ngx_http_upstream_round_robin.c b/src/http/ngx_http_upstream_round_robin.c
index 2d0649b7f..487b0e3cc 100644
--- a/src/http/ngx_http_upstream_round_robin.c
+++ b/src/http/ngx_http_upstream_round_robin.c
@@ -34,7 +34,7 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
ngx_url_t u;
ngx_uint_t i, j, n, w;
ngx_http_upstream_server_t *server;
- ngx_http_upstream_rr_peer_t *peer;
+ ngx_http_upstream_rr_peer_t *peer, **peerp;
ngx_http_upstream_rr_peers_t *peers, *backup;
us->peer.init = ngx_http_upstream_init_round_robin_peer;
@@ -61,12 +61,16 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
return NGX_ERROR;
}
- peers = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peers_t)
- + sizeof(ngx_http_upstream_rr_peer_t) * (n - 1));
+ peers = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peers_t));
if (peers == NULL) {
return NGX_ERROR;
}
+ peer = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peer_t) * n);
+ if (peer == NULL) {
+ return NGX_ERROR;
+ }
+
peers->single = (n == 1);
peers->number = n;
peers->weighted = (w != n);
@@ -74,7 +78,7 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
peers->name = &us->host;
n = 0;
- peer = peers->peer;
+ peerp = &peers->peer;
for (i = 0; i < us->servers->nelts; i++) {
if (server[i].backup) {
@@ -92,6 +96,9 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
peer[n].fail_timeout = server[i].fail_timeout;
peer[n].down = server[i].down;
peer[n].server = server[i].name;
+
+ *peerp = &peer[n];
+ peerp = &peer[n].next;
n++;
}
}
@@ -116,12 +123,16 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
return NGX_OK;
}
- backup = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peers_t)
- + sizeof(ngx_http_upstream_rr_peer_t) * (n - 1));
+ backup = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peers_t));
if (backup == NULL) {
return NGX_ERROR;
}
+ peer = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peer_t) * n);
+ if (peer == NULL) {
+ return NGX_ERROR;
+ }
+
peers->single = 0;
backup->single = 0;
backup->number = n;
@@ -130,7 +141,7 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
backup->name = &us->host;
n = 0;
- peer = backup->peer;
+ peerp = &backup->peer;
for (i = 0; i < us->servers->nelts; i++) {
if (!server[i].backup) {
@@ -148,6 +159,9 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
peer[n].fail_timeout = server[i].fail_timeout;
peer[n].down = server[i].down;
peer[n].server = server[i].name;
+
+ *peerp = &peer[n];
+ peerp = &peer[n].next;
n++;
}
}
@@ -184,19 +198,23 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
n = u.naddrs;
- peers = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peers_t)
- + sizeof(ngx_http_upstream_rr_peer_t) * (n - 1));
+ peers = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peers_t));
if (peers == NULL) {
return NGX_ERROR;
}
+ peer = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peer_t) * n);
+ if (peer == NULL) {
+ return NGX_ERROR;
+ }
+
peers->single = (n == 1);
peers->number = n;
peers->weighted = 0;
peers->total_weight = n;
peers->name = &us->host;
- peer = peers->peer;
+ peerp = &peers->peer;
for (i = 0; i < u.naddrs; i++) {
peer[i].sockaddr = u.addrs[i].sockaddr;
@@ -207,6 +225,8 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
peer[i].current_weight = 0;
peer[i].max_fails = 1;
peer[i].fail_timeout = 10;
+ *peerp = &peer[i];
+ peerp = &peer[i].next;
}
us->peer.data = peers;
@@ -236,7 +256,7 @@ ngx_http_upstream_init_round_robin_peer(ngx_http_request_t *r,
}
rrp->peers = us->peer.data;
- rrp->current = 0;
+ rrp->current = NULL;
n = rrp->peers->number;
@@ -280,7 +300,7 @@ ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r,
socklen_t socklen;
ngx_uint_t i, n;
struct sockaddr *sockaddr;
- ngx_http_upstream_rr_peer_t *peer;
+ ngx_http_upstream_rr_peer_t *peer, **peerp;
ngx_http_upstream_rr_peers_t *peers;
ngx_http_upstream_rr_peer_data_t *rrp;
@@ -295,18 +315,21 @@ ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r,
r->upstream->peer.data = rrp;
}
- peers = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_rr_peers_t)
- + sizeof(ngx_http_upstream_rr_peer_t) * (ur->naddrs - 1));
+ peers = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_rr_peers_t));
if (peers == NULL) {
return NGX_ERROR;
}
+ peer = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_rr_peer_t)
+ * ur->naddrs);
+ if (peer == NULL) {
+ return NGX_ERROR;
+ }
+
peers->single = (ur->naddrs == 1);
peers->number = ur->naddrs;
peers->name = &ur->host;
- peer = peers->peer;
-
if (ur->sockaddr) {
peer[0].sockaddr = ur->sockaddr;
peer[0].socklen = ur->socklen;
@@ -316,8 +339,10 @@ ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r,
peer[0].current_weight = 0;
peer[0].max_fails = 1;
peer[0].fail_timeout = 10;
+ peers->peer = peer;
} else {
+ peerp = &peers->peer;
for (i = 0; i < ur->naddrs; i++) {
@@ -356,11 +381,13 @@ ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r,
peer[i].current_weight = 0;
peer[i].max_fails = 1;
peer[i].fail_timeout = 10;
+ *peerp = &peer[i];
+ peerp = &peer[i].next;
}
}
rrp->peers = peers;
- rrp->current = 0;
+ rrp->current = NULL;
if (rrp->peers->number <= 8 * sizeof(uintptr_t)) {
rrp->tried = &rrp->data;
@@ -405,16 +432,17 @@ ngx_http_upstream_get_round_robin_peer(ngx_peer_connection_t *pc, void *data)
pc->connection = NULL;
peers = rrp->peers;
-
- /* ngx_lock_mutex(peers->mutex); */
+ ngx_http_upstream_rr_peers_wlock(peers);
if (peers->single) {
- peer = &peers->peer[0];
+ peer = peers->peer;
if (peer->down) {
goto failed;
}
+ rrp->current = peer;
+
} else {
/* there are several peers */
@@ -426,15 +454,17 @@ ngx_http_upstream_get_round_robin_peer(ngx_peer_connection_t *pc, void *data)
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
- "get rr peer, current: %ui %i",
- rrp->current, peer->current_weight);
+ "get rr peer, current: %p %i",
+ peer, peer->current_weight);
}
pc->sockaddr = peer->sockaddr;
pc->socklen = peer->socklen;
pc->name = &peer->name;
- /* ngx_unlock_mutex(peers->mutex); */
+ peer->conns++;
+
+ ngx_http_upstream_rr_peers_unlock(peers);
return NGX_OK;
@@ -442,8 +472,6 @@ failed:
if (peers->next) {
- /* ngx_unlock_mutex(peers->mutex); */
-
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0, "backup servers");
rrp->peers = peers->next;
@@ -455,22 +483,24 @@ failed:
rrp->tried[i] = 0;
}
+ ngx_http_upstream_rr_peers_unlock(peers);
+
rc = ngx_http_upstream_get_round_robin_peer(pc, rrp);
if (rc != NGX_BUSY) {
return rc;
}
- /* ngx_lock_mutex(peers->mutex); */
+ ngx_http_upstream_rr_peers_wlock(peers);
}
/* all peers failed, mark them as live for quick recovery */
- for (i = 0; i < peers->number; i++) {
- peers->peer[i].fails = 0;
+ for (peer = peers->peer; peer; peer = peer->next) {
+ peer->fails = 0;
}
- /* ngx_unlock_mutex(peers->mutex); */
+ ngx_http_upstream_rr_peers_unlock(peers);
pc->name = peers->name;
@@ -484,7 +514,7 @@ ngx_http_upstream_get_peer(ngx_http_upstream_rr_peer_data_t *rrp)
time_t now;
uintptr_t m;
ngx_int_t total;
- ngx_uint_t i, n;
+ ngx_uint_t i, n, p;
ngx_http_upstream_rr_peer_t *peer, *best;
now = ngx_time();
@@ -492,7 +522,14 @@ ngx_http_upstream_get_peer(ngx_http_upstream_rr_peer_data_t *rrp)
best = NULL;
total = 0;
- for (i = 0; i < rrp->peers->number; i++) {
+#if (NGX_SUPPRESS_WARN)
+ p = 0;
+#endif
+
+ for (peer = rrp->peers->peer, i = 0;
+ peer;
+ peer = peer->next, i++)
+ {
n = i / (8 * sizeof(uintptr_t));
m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));
@@ -501,8 +538,6 @@ ngx_http_upstream_get_peer(ngx_http_upstream_rr_peer_data_t *rrp)
continue;
}
- peer = &rrp->peers->peer[i];
-
if (peer->down) {
continue;
}
@@ -523,6 +558,7 @@ ngx_http_upstream_get_peer(ngx_http_upstream_rr_peer_data_t *rrp)
if (best == NULL || peer->current_weight > best->current_weight) {
best = peer;
+ p = i;
}
}
@@ -530,12 +566,10 @@ ngx_http_upstream_get_peer(ngx_http_upstream_rr_peer_data_t *rrp)
return NULL;
}
- i = best - &rrp->peers->peer[0];
-
- rrp->current = i;
+ rrp->current = best;
- n = i / (8 * sizeof(uintptr_t));
- m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));
+ n = p / (8 * sizeof(uintptr_t));
+ m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t));
rrp->tried[n] |= m;
@@ -563,18 +597,25 @@ ngx_http_upstream_free_round_robin_peer(ngx_peer_connection_t *pc, void *data,
/* TODO: NGX_PEER_KEEPALIVE */
+ peer = rrp->current;
+
+ ngx_http_upstream_rr_peers_rlock(rrp->peers);
+ ngx_http_upstream_rr_peer_lock(rrp->peers, peer);
+
if (rrp->peers->single) {
+
+ peer->conns--;
+
+ ngx_http_upstream_rr_peer_unlock(rrp->peers, peer);
+ ngx_http_upstream_rr_peers_unlock(rrp->peers);
+
pc->tries = 0;
return;
}
- peer = &rrp->peers->peer[rrp->current];
-
if (state & NGX_PEER_FAILED) {
now = ngx_time();
- /* ngx_lock_mutex(rrp->peers->mutex); */
-
peer->fails++;
peer->accessed = now;
peer->checked = now;
@@ -584,15 +625,13 @@ ngx_http_upstream_free_round_robin_peer(ngx_peer_connection_t *pc, void *data,
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
- "free rr peer failed: %ui %i",
- rrp->current, peer->effective_weight);
+ "free rr peer failed: %p %i",
+ peer, peer->effective_weight);
if (peer->effective_weight < 0) {
peer->effective_weight = 0;
}
- /* ngx_unlock_mutex(rrp->peers->mutex); */
-
} else {
/* mark peer live if check passed */
@@ -602,11 +641,14 @@ ngx_http_upstream_free_round_robin_peer(ngx_peer_connection_t *pc, void *data,
}
}
+ peer->conns--;
+
+ ngx_http_upstream_rr_peer_unlock(rrp->peers, peer);
+ ngx_http_upstream_rr_peers_unlock(rrp->peers);
+
if (pc->tries) {
pc->tries--;
}
-
- /* ngx_unlock_mutex(rrp->peers->mutex); */
}
@@ -618,14 +660,54 @@ ngx_http_upstream_set_round_robin_peer_session(ngx_peer_connection_t *pc,
{
ngx_http_upstream_rr_peer_data_t *rrp = data;
- ngx_int_t rc;
- ngx_ssl_session_t *ssl_session;
- ngx_http_upstream_rr_peer_t *peer;
+ ngx_int_t rc;
+ ngx_ssl_session_t *ssl_session;
+ ngx_http_upstream_rr_peer_t *peer;
+#if (NGX_HTTP_UPSTREAM_ZONE)
+ int len;
+#if OPENSSL_VERSION_NUMBER >= 0x0090707fL
+ const
+#endif
+ u_char *p;
+ ngx_http_upstream_rr_peers_t *peers;
+ u_char buf[NGX_SSL_MAX_SESSION_SIZE];
+#endif
+
+ peer = rrp->current;
+
+#if (NGX_HTTP_UPSTREAM_ZONE)
+ peers = rrp->peers;
+
+ if (peers->shpool) {
+ ngx_http_upstream_rr_peers_rlock(peers);
+ ngx_http_upstream_rr_peer_lock(peers, peer);
- peer = &rrp->peers->peer[rrp->current];
+ if (peer->ssl_session == NULL) {
+ ngx_http_upstream_rr_peer_unlock(peers, peer);
+ ngx_http_upstream_rr_peers_unlock(peers);
+ return NGX_OK;
+ }
+
+ len = peer->ssl_session_len;
+
+ ngx_memcpy(buf, peer->ssl_session, len);
- /* TODO: threads only mutex */
- /* ngx_lock_mutex(rrp->peers->mutex); */
+ ngx_http_upstream_rr_peer_unlock(peers, peer);
+ ngx_http_upstream_rr_peers_unlock(peers);
+
+ p = buf;
+ ssl_session = d2i_SSL_SESSION(NULL, &p, len);
+
+ 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_ssl_free_session(ssl_session);
+
+ return rc;
+ }
+#endif
ssl_session = peer->ssl_session;
@@ -634,8 +716,6 @@ ngx_http_upstream_set_round_robin_peer_session(ngx_peer_connection_t *pc,
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
"set session: %p", ssl_session);
- /* ngx_unlock_mutex(rrp->peers->mutex); */
-
return rc;
}
@@ -646,8 +726,75 @@ ngx_http_upstream_save_round_robin_peer_session(ngx_peer_connection_t *pc,
{
ngx_http_upstream_rr_peer_data_t *rrp = data;
- ngx_ssl_session_t *old_ssl_session, *ssl_session;
- ngx_http_upstream_rr_peer_t *peer;
+ ngx_ssl_session_t *old_ssl_session, *ssl_session;
+ ngx_http_upstream_rr_peer_t *peer;
+#if (NGX_HTTP_UPSTREAM_ZONE)
+ int len;
+ u_char *p;
+ ngx_http_upstream_rr_peers_t *peers;
+ u_char buf[NGX_SSL_MAX_SESSION_SIZE];
+#endif
+
+#if (NGX_HTTP_UPSTREAM_ZONE)
+ peers = rrp->peers;
+
+ if (peers->shpool) {
+
+ ssl_session = SSL_get0_session(pc->connection->ssl->connection);
+
+ if (ssl_session == NULL) {
+ return;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
+ "save session: %p", ssl_session);
+
+ len = i2d_SSL_SESSION(ssl_session, NULL);
+
+ /* do not cache too big session */
+
+ if (len > NGX_SSL_MAX_SESSION_SIZE) {
+ return;
+ }
+
+ p = buf;
+ (void) i2d_SSL_SESSION(ssl_session, &p);
+
+ peer = rrp->current;
+
+ ngx_http_upstream_rr_peers_rlock(peers);
+ ngx_http_upstream_rr_peer_lock(peers, peer);
+
+ if (len > peer->ssl_session_len) {
+ ngx_shmtx_lock(&peers->shpool->mutex);
+
+ if (peer->ssl_session) {
+ ngx_slab_free_locked(peers->shpool, peer->ssl_session);
+ }
+
+ peer->ssl_session = ngx_slab_alloc_locked(peers->shpool, len);
+
+ ngx_shmtx_unlock(&peers->shpool->mutex);
+
+ if (peer->ssl_session == NULL) {
+ peer->ssl_session_len = 0;
+
+ ngx_http_upstream_rr_peer_unlock(peers, peer);
+ ngx_http_upstream_rr_peers_unlock(peers);
+ return;
+ }
+
+ peer->ssl_session_len = len;
+ }
+
+ ngx_memcpy(peer->ssl_session, buf, len);
+
+ ngx_http_upstream_rr_peer_unlock(peers, peer);
+ ngx_http_upstream_rr_peers_unlock(peers);
+
+ return;
+ }
+#endif
ssl_session = ngx_ssl_get_session(pc->connection);
@@ -658,16 +805,11 @@ ngx_http_upstream_save_round_robin_peer_session(ngx_peer_connection_t *pc,
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
"save session: %p", ssl_session);
- peer = &rrp->peers->peer[rrp->current];
-
- /* TODO: threads only mutex */
- /* ngx_lock_mutex(rrp->peers->mutex); */
+ peer = rrp->current;
old_ssl_session = peer->ssl_session;
peer->ssl_session = ssl_session;
- /* ngx_unlock_mutex(rrp->peers->mutex); */
-
if (old_ssl_session) {
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
diff --git a/src/http/ngx_http_upstream_round_robin.h b/src/http/ngx_http_upstream_round_robin.h
index 3bbba0b37..454515cdd 100644
--- a/src/http/ngx_http_upstream_round_robin.h
+++ b/src/http/ngx_http_upstream_round_robin.h
@@ -14,7 +14,9 @@
#include <ngx_http.h>
-typedef struct {
+typedef struct ngx_http_upstream_rr_peer_s ngx_http_upstream_rr_peer_t;
+
+struct ngx_http_upstream_rr_peer_s {
struct sockaddr *sockaddr;
socklen_t socklen;
ngx_str_t name;
@@ -24,6 +26,8 @@ typedef struct {
ngx_int_t effective_weight;
ngx_int_t weight;
+ ngx_uint_t conns;
+
ngx_uint_t fails;
time_t accessed;
time_t checked;
@@ -34,9 +38,16 @@ typedef struct {
ngx_uint_t down; /* unsigned down:1; */
#if (NGX_HTTP_SSL)
- ngx_ssl_session_t *ssl_session; /* local to a process */
+ void *ssl_session;
+ int ssl_session_len;
#endif
-} ngx_http_upstream_rr_peer_t;
+
+ ngx_http_upstream_rr_peer_t *next;
+
+#if (NGX_HTTP_UPSTREAM_ZONE)
+ ngx_atomic_t lock;
+#endif
+};
typedef struct ngx_http_upstream_rr_peers_s ngx_http_upstream_rr_peers_t;
@@ -44,6 +55,11 @@ typedef struct ngx_http_upstream_rr_peers_s ngx_http_upstream_rr_peers_t;
struct ngx_http_upstream_rr_peers_s {
ngx_uint_t number;
+#if (NGX_HTTP_UPSTREAM_ZONE)
+ ngx_slab_pool_t *shpool;
+ ngx_atomic_t rwlock;
+#endif
+
ngx_uint_t total_weight;
unsigned single:1;
@@ -53,13 +69,57 @@ struct ngx_http_upstream_rr_peers_s {
ngx_http_upstream_rr_peers_t *next;
- ngx_http_upstream_rr_peer_t peer[1];
+ ngx_http_upstream_rr_peer_t *peer;
};
+#if (NGX_HTTP_UPSTREAM_ZONE)
+
+#define ngx_http_upstream_rr_peers_rlock(peers) \
+ \
+ if (peers->shpool) { \
+ ngx_rwlock_rlock(&peers->rwlock); \
+ }
+
+#define ngx_http_upstream_rr_peers_wlock(peers) \
+ \
+ if (peers->shpool) { \
+ ngx_rwlock_wlock(&peers->rwlock); \
+ }
+
+#define ngx_http_upstream_rr_peers_unlock(peers) \
+ \
+ if (peers->shpool) { \
+ ngx_rwlock_unlock(&peers->rwlock); \
+ }
+
+
+#define ngx_http_upstream_rr_peer_lock(peers, peer) \
+ \
+ if (peers->shpool) { \
+ ngx_rwlock_wlock(&peer->lock); \
+ }
+
+#define ngx_http_upstream_rr_peer_unlock(peers, peer) \
+ \
+ if (peers->shpool) { \
+ ngx_rwlock_unlock(&peer->lock); \
+ }
+
+#else
+
+#define ngx_http_upstream_rr_peers_rlock(peers)
+#define ngx_http_upstream_rr_peers_wlock(peers)
+#define ngx_http_upstream_rr_peers_unlock(peers)
+#define ngx_http_upstream_rr_peer_lock(peers, peer)
+#define ngx_http_upstream_rr_peer_unlock(peers, peer)
+
+#endif
+
+
typedef struct {
ngx_http_upstream_rr_peers_t *peers;
- ngx_uint_t current;
+ ngx_http_upstream_rr_peer_t *current;
uintptr_t *tried;
uintptr_t data;
} ngx_http_upstream_rr_peer_data_t;
diff --git a/src/mail/ngx_mail.c b/src/mail/ngx_mail.c
index bf1b858df..3e4b2cbf4 100644
--- a/src/mail/ngx_mail.c
+++ b/src/mail/ngx_mail.c
@@ -334,11 +334,12 @@ found:
static char *
ngx_mail_optimize_servers(ngx_conf_t *cf, ngx_array_t *ports)
{
- ngx_uint_t i, p, last, bind_wildcard;
- ngx_listening_t *ls;
- ngx_mail_port_t *mport;
- ngx_mail_conf_port_t *port;
- ngx_mail_conf_addr_t *addr;
+ ngx_uint_t i, p, last, bind_wildcard;
+ ngx_listening_t *ls;
+ ngx_mail_port_t *mport;
+ ngx_mail_conf_port_t *port;
+ ngx_mail_conf_addr_t *addr;
+ ngx_mail_core_srv_conf_t *cscf;
port = ports->elts;
for (p = 0; p < ports->nelts; p++) {
@@ -380,8 +381,9 @@ ngx_mail_optimize_servers(ngx_conf_t *cf, ngx_array_t *ports)
ls->handler = ngx_mail_init_connection;
ls->pool_size = 256;
- /* TODO: error_log directive */
- ls->logp = &cf->cycle->new_log;
+ cscf = addr->ctx->srv_conf[ngx_mail_core_module.ctx_index];
+
+ ls->logp = cscf->error_log;
ls->log.data = &ls->addr_text;
ls->log.handler = ngx_accept_log_error;
diff --git a/src/mail/ngx_mail.h b/src/mail/ngx_mail.h
index 02261390c..e15bc6342 100644
--- a/src/mail/ngx_mail.h
+++ b/src/mail/ngx_mail.h
@@ -139,6 +139,7 @@ typedef struct {
ngx_int_t line;
ngx_resolver_t *resolver;
+ ngx_log_t *error_log;
/* server ctx */
ngx_mail_conf_ctx_t *ctx;
diff --git a/src/mail/ngx_mail_core_module.c b/src/mail/ngx_mail_core_module.c
index 05a47f5e3..9f349fa5e 100644
--- a/src/mail/ngx_mail_core_module.c
+++ b/src/mail/ngx_mail_core_module.c
@@ -21,6 +21,8 @@ static char *ngx_mail_core_listen(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_mail_core_protocol(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
+static char *ngx_mail_core_error_log(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
static char *ngx_mail_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
@@ -75,6 +77,13 @@ static ngx_command_t ngx_mail_core_commands[] = {
offsetof(ngx_mail_core_srv_conf_t, server_name),
NULL },
+ { ngx_string("error_log"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,
+ ngx_mail_core_error_log,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ 0,
+ NULL },
+
{ ngx_string("resolver"),
NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,
ngx_mail_core_resolver,
@@ -161,6 +170,7 @@ ngx_mail_core_create_srv_conf(ngx_conf_t *cf)
* set by ngx_pcalloc():
*
* cscf->protocol = NULL;
+ * cscf->error_log = NULL;
*/
cscf->timeout = NGX_CONF_UNSET_MSEC;
@@ -202,6 +212,14 @@ ngx_mail_core_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
return NGX_CONF_ERROR;
}
+ if (conf->error_log == NULL) {
+ if (prev->error_log) {
+ conf->error_log = prev->error_log;
+ } else {
+ conf->error_log = &cf->cycle->new_log;
+ }
+ }
+
ngx_conf_merge_ptr_value(conf->resolver, prev->resolver, NULL);
return NGX_CONF_OK;
@@ -601,6 +619,15 @@ ngx_mail_core_protocol(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
static char *
+ngx_mail_core_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_mail_core_srv_conf_t *cscf = conf;
+
+ return ngx_log_set_log(cf, &cscf->error_log);
+}
+
+
+static char *
ngx_mail_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_mail_core_srv_conf_t *cscf = conf;
diff --git a/src/mail/ngx_mail_handler.c b/src/mail/ngx_mail_handler.c
index 870b5eeed..901bb8f15 100644
--- a/src/mail/ngx_mail_handler.c
+++ b/src/mail/ngx_mail_handler.c
@@ -24,19 +24,20 @@ static ngx_int_t ngx_mail_verify_cert(ngx_mail_session_t *s,
void
ngx_mail_init_connection(ngx_connection_t *c)
{
- size_t len;
- ngx_uint_t i;
- ngx_mail_port_t *port;
- struct sockaddr *sa;
- struct sockaddr_in *sin;
- ngx_mail_log_ctx_t *ctx;
- ngx_mail_in_addr_t *addr;
- ngx_mail_session_t *s;
- ngx_mail_addr_conf_t *addr_conf;
- u_char text[NGX_SOCKADDR_STRLEN];
+ size_t len;
+ ngx_uint_t i;
+ ngx_mail_port_t *port;
+ struct sockaddr *sa;
+ struct sockaddr_in *sin;
+ ngx_mail_log_ctx_t *ctx;
+ ngx_mail_in_addr_t *addr;
+ 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];
#if (NGX_HAVE_INET6)
- struct sockaddr_in6 *sin6;
- ngx_mail_in6_addr_t *addr6;
+ struct sockaddr_in6 *sin6;
+ ngx_mail_in6_addr_t *addr6;
#endif
@@ -133,6 +134,10 @@ ngx_mail_init_connection(ngx_connection_t *c)
c->data = s;
s->connection = c;
+ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+ ngx_set_connection_log(c, cscf->error_log);
+
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",
diff --git a/src/os/unix/ngx_aio_read.c b/src/os/unix/ngx_aio_read.c
deleted file mode 100644
index 784988173..000000000
--- a/src/os/unix/ngx_aio_read.c
+++ /dev/null
@@ -1,109 +0,0 @@
-
-/*
- * Copyright (C) Igor Sysoev
- * Copyright (C) Nginx, Inc.
- */
-
-
-#include <ngx_config.h>
-#include <ngx_core.h>
-#include <ngx_event.h>
-
-
-extern int ngx_kqueue;
-
-
-ssize_t
-ngx_aio_read(ngx_connection_t *c, u_char *buf, size_t size)
-{
- int n;
- ngx_event_t *rev;
-
- rev = c->read;
-
- if (!rev->ready) {
- ngx_log_error(NGX_LOG_ALERT, c->log, 0, "second aio post");
- return NGX_AGAIN;
- }
-
- ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "rev->complete: %d", rev->complete);
- ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "aio size: %d", size);
-
- if (!rev->complete) {
- ngx_memzero(&rev->aiocb, sizeof(struct aiocb));
-
- rev->aiocb.aio_fildes = c->fd;
- rev->aiocb.aio_buf = buf;
- rev->aiocb.aio_nbytes = size;
-
-#if (NGX_HAVE_KQUEUE)
- rev->aiocb.aio_sigevent.sigev_notify_kqueue = ngx_kqueue;
- rev->aiocb.aio_sigevent.sigev_notify = SIGEV_KEVENT;
- rev->aiocb.aio_sigevent.sigev_value.sigval_ptr = rev;
-#endif
-
- if (aio_read(&rev->aiocb) == -1) {
- ngx_log_error(NGX_LOG_CRIT, rev->log, ngx_errno,
- "aio_read() failed");
- rev->error = 1;
- return NGX_ERROR;
- }
-
- ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "aio_read: #%d OK", c->fd);
-
- rev->active = 1;
- rev->ready = 0;
- }
-
- rev->complete = 0;
-
- n = aio_error(&rev->aiocb);
- if (n == -1) {
- ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno, "aio_error() failed");
- rev->error = 1;
- return NGX_ERROR;
- }
-
- if (n != 0) {
- if (n == NGX_EINPROGRESS) {
- if (rev->ready) {
- ngx_log_error(NGX_LOG_ALERT, c->log, n,
- "aio_read() still in progress");
- rev->ready = 0;
- }
- return NGX_AGAIN;
- }
-
- ngx_log_error(NGX_LOG_CRIT, c->log, n, "aio_read() failed");
- rev->error = 1;
- rev->ready = 0;
- return NGX_ERROR;
- }
-
- n = aio_return(&rev->aiocb);
- if (n == -1) {
- ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
- "aio_return() failed");
-
- rev->error = 1;
- rev->ready = 0;
- return NGX_ERROR;
- }
-
- ngx_log_debug2(NGX_LOG_DEBUG_EVENT, rev->log, 0,
- "aio_read: #%d %d", c->fd, n);
-
- if (n == 0) {
- rev->eof = 1;
- rev->ready = 0;
- } else {
- rev->ready = 1;
- }
-
- rev->active = 0;
-
- return n;
-}
diff --git a/src/os/unix/ngx_aio_read_chain.c b/src/os/unix/ngx_aio_read_chain.c
deleted file mode 100644
index d8722b2c1..000000000
--- a/src/os/unix/ngx_aio_read_chain.c
+++ /dev/null
@@ -1,78 +0,0 @@
-
-/*
- * Copyright (C) Igor Sysoev
- * Copyright (C) Nginx, Inc.
- */
-
-
-#include <ngx_config.h>
-#include <ngx_core.h>
-#include <ngx_event.h>
-
-
-ssize_t
-ngx_aio_read_chain(ngx_connection_t *c, ngx_chain_t *cl, off_t limit)
-{
- int n;
- u_char *buf, *prev;
- size_t size;
- ssize_t total;
-
- if (c->read->pending_eof) {
- c->read->ready = 0;
- return 0;
- }
-
- total = 0;
-
- while (cl) {
-
- /* we can post the single aio operation only */
-
- if (!c->read->ready) {
- return total ? total : NGX_AGAIN;
- }
-
- buf = cl->buf->last;
- prev = cl->buf->last;
- size = 0;
-
- /* coalesce the neighbouring bufs */
-
- while (cl && prev == cl->buf->last) {
- size += cl->buf->end - cl->buf->last;
- prev = cl->buf->end;
- cl = cl->next;
- }
-
- n = ngx_aio_read(c, buf, size);
-
- ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "aio_read: %d", n);
-
- if (n == NGX_AGAIN) {
- return total ? total : NGX_AGAIN;
- }
-
- if (n == NGX_ERROR) {
- return NGX_ERROR;
- }
-
- if (n == 0) {
- c->read->pending_eof = 1;
- if (total) {
- c->read->eof = 0;
- c->read->ready = 1;
- }
- return total;
- }
-
- if (n > 0) {
- total += n;
- }
-
- ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "aio_read total: %d", total);
- }
-
- return total ? total : NGX_AGAIN;
-}
diff --git a/src/os/unix/ngx_aio_write.c b/src/os/unix/ngx_aio_write.c
deleted file mode 100644
index f0d93918e..000000000
--- a/src/os/unix/ngx_aio_write.c
+++ /dev/null
@@ -1,109 +0,0 @@
-
-/*
- * Copyright (C) Igor Sysoev
- * Copyright (C) Nginx, Inc.
- */
-
-
-#include <ngx_config.h>
-#include <ngx_core.h>
-#include <ngx_event.h>
-
-
-extern int ngx_kqueue;
-
-
-ssize_t
-ngx_aio_write(ngx_connection_t *c, u_char *buf, size_t size)
-{
- int n;
- ngx_event_t *wev;
-
- wev = c->write;
-
- if (!wev->ready) {
- return NGX_AGAIN;
- }
-
- ngx_log_debug1(NGX_LOG_DEBUG_EVENT, wev->log, 0,
- "aio: wev->complete: %d", wev->complete);
-
- if (!wev->complete) {
- ngx_memzero(&wev->aiocb, sizeof(struct aiocb));
-
- wev->aiocb.aio_fildes = c->fd;
- wev->aiocb.aio_buf = buf;
- wev->aiocb.aio_nbytes = size;
-
-#if (NGX_HAVE_KQUEUE)
- wev->aiocb.aio_sigevent.sigev_notify_kqueue = ngx_kqueue;
- wev->aiocb.aio_sigevent.sigev_notify = SIGEV_KEVENT;
- wev->aiocb.aio_sigevent.sigev_value.sigval_ptr = wev;
-#endif
-
- if (aio_write(&wev->aiocb) == -1) {
- ngx_log_error(NGX_LOG_CRIT, wev->log, ngx_errno,
- "aio_write() failed");
- return NGX_ERROR;
- }
-
- ngx_log_debug0(NGX_LOG_DEBUG_EVENT, wev->log, 0, "aio_write: OK");
-
- wev->active = 1;
- wev->ready = 0;
- }
-
- wev->complete = 0;
-
- n = aio_error(&wev->aiocb);
- if (n == -1) {
- ngx_log_error(NGX_LOG_CRIT, wev->log, ngx_errno, "aio_error() failed");
- wev->error = 1;
- return NGX_ERROR;
- }
-
- if (n != 0) {
- if (n == NGX_EINPROGRESS) {
- if (wev->ready) {
- ngx_log_error(NGX_LOG_ALERT, wev->log, n,
- "aio_write() still in progress");
- wev->ready = 0;
- }
- return NGX_AGAIN;
- }
-
- ngx_log_error(NGX_LOG_CRIT, wev->log, n, "aio_write() failed");
- wev->error = 1;
- wev->ready = 0;
-
-#if 1
- n = aio_return(&wev->aiocb);
- if (n == -1) {
- ngx_log_error(NGX_LOG_ALERT, wev->log, ngx_errno,
- "aio_return() failed");
- }
-
- ngx_log_error(NGX_LOG_CRIT, wev->log, n, "aio_return() %d", n);
-#endif
-
- return NGX_ERROR;
- }
-
- n = aio_return(&wev->aiocb);
- if (n == -1) {
- ngx_log_error(NGX_LOG_ALERT, wev->log, ngx_errno,
- "aio_return() failed");
-
- wev->error = 1;
- wev->ready = 0;
- return NGX_ERROR;
- }
-
-
- ngx_log_debug1(NGX_LOG_DEBUG_EVENT, wev->log, 0, "aio_write: %d", n);
-
- wev->active = 0;
- wev->ready = 1;
-
- return n;
-}
diff --git a/src/os/unix/ngx_aio_write_chain.c b/src/os/unix/ngx_aio_write_chain.c
deleted file mode 100644
index b0c25085d..000000000
--- a/src/os/unix/ngx_aio_write_chain.c
+++ /dev/null
@@ -1,100 +0,0 @@
-
-/*
- * Copyright (C) Igor Sysoev
- * Copyright (C) Nginx, Inc.
- */
-
-
-#include <ngx_config.h>
-#include <ngx_core.h>
-#include <ngx_event.h>
-
-
-ngx_chain_t *
-ngx_aio_write_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
-{
- u_char *buf, *prev;
- off_t send, sent;
- size_t len;
- ssize_t n, size;
- ngx_chain_t *cl;
-
- /* the maximum limit size is the maximum size_t value - the page size */
-
- if (limit == 0 || limit > (off_t) (NGX_MAX_SIZE_T_VALUE - ngx_pagesize)) {
- limit = NGX_MAX_SIZE_T_VALUE - ngx_pagesize;
- }
-
- send = 0;
- sent = 0;
- cl = in;
-
- while (cl) {
-
- if (cl->buf->pos == cl->buf->last) {
- cl = cl->next;
- continue;
- }
-
- /* we can post the single aio operation only */
-
- if (!c->write->ready) {
- return cl;
- }
-
- buf = cl->buf->pos;
- prev = buf;
- len = 0;
-
- /* coalesce the neighbouring bufs */
-
- while (cl && prev == cl->buf->pos && send < limit) {
- if (ngx_buf_special(cl->buf)) {
- continue;
- }
-
- size = cl->buf->last - cl->buf->pos;
-
- if (send + size > limit) {
- size = limit - send;
- }
-
- len += size;
- prev = cl->buf->pos + size;
- send += size;
- cl = cl->next;
- }
-
- n = ngx_aio_write(c, buf, len);
-
- ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "aio_write: %z", n);
-
- if (n == NGX_ERROR) {
- return NGX_CHAIN_ERROR;
- }
-
- if (n > 0) {
- sent += n;
- c->sent += n;
- }
-
- ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "aio_write sent: %O", c->sent);
-
- for (cl = in; cl; cl = cl->next) {
-
- if (sent >= cl->buf->last - cl->buf->pos) {
- sent -= cl->buf->last - cl->buf->pos;
- cl->buf->pos = cl->buf->last;
-
- continue;
- }
-
- cl->buf->pos += sent;
-
- break;
- }
- }
-
- return cl;
-}
diff --git a/src/os/unix/ngx_freebsd_config.h b/src/os/unix/ngx_freebsd_config.h
index 8f060514d..9a2578875 100644
--- a/src/os/unix/ngx_freebsd_config.h
+++ b/src/os/unix/ngx_freebsd_config.h
@@ -86,7 +86,7 @@
#endif
-#if (NGX_HAVE_FILE_AIO || NGX_HAVE_AIO)
+#if (NGX_HAVE_FILE_AIO)
#include <aio.h>
typedef struct aiocb ngx_aiocb_t;
#endif
diff --git a/src/os/unix/ngx_linux.h b/src/os/unix/ngx_linux.h
index 1b8bdac51..13d654e39 100644
--- a/src/os/unix/ngx_linux.h
+++ b/src/os/unix/ngx_linux.h
@@ -12,7 +12,5 @@
ngx_chain_t *ngx_linux_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in,
off_t limit);
-extern int ngx_linux_rtsig_max;
-
#endif /* _NGX_LINUX_H_INCLUDED_ */
diff --git a/src/os/unix/ngx_linux_config.h b/src/os/unix/ngx_linux_config.h
index 0c0b168d7..162a992c9 100644
--- a/src/os/unix/ngx_linux_config.h
+++ b/src/os/unix/ngx_linux_config.h
@@ -82,12 +82,6 @@ extern ssize_t sendfile(int s, int fd, int32_t *offset, size_t size);
#endif
-#if (NGX_HAVE_RTSIG)
-#include <poll.h>
-#include <sys/sysctl.h>
-#endif
-
-
#if (NGX_HAVE_EPOLL)
#include <sys/epoll.h>
#endif
diff --git a/src/os/unix/ngx_linux_init.c b/src/os/unix/ngx_linux_init.c
index b910380d7..b306cda7c 100644
--- a/src/os/unix/ngx_linux_init.c
+++ b/src/os/unix/ngx_linux_init.c
@@ -12,8 +12,6 @@
u_char ngx_linux_kern_ostype[50];
u_char ngx_linux_kern_osrelease[50];
-int ngx_linux_rtsig_max;
-
static ngx_os_io_t ngx_linux_io = {
ngx_unix_recv,
@@ -46,32 +44,6 @@ ngx_os_specific_init(ngx_log_t *log)
(void) ngx_cpystrn(ngx_linux_kern_osrelease, (u_char *) u.release,
sizeof(ngx_linux_kern_osrelease));
-#if (NGX_HAVE_RTSIG)
- {
- int name[2];
- size_t len;
- ngx_err_t err;
-
- name[0] = CTL_KERN;
- name[1] = KERN_RTSIGMAX;
- len = sizeof(ngx_linux_rtsig_max);
-
- if (sysctl(name, 2, &ngx_linux_rtsig_max, &len, NULL, 0) == -1) {
- err = ngx_errno;
-
- if (err != NGX_ENOTDIR && err != NGX_ENOSYS) {
- ngx_log_error(NGX_LOG_ALERT, log, err,
- "sysctl(KERN_RTSIGMAX) failed");
-
- return NGX_ERROR;
- }
-
- ngx_linux_rtsig_max = 0;
- }
-
- }
-#endif
-
ngx_os_io = ngx_linux_io;
return NGX_OK;
@@ -83,9 +55,4 @@ ngx_os_specific_status(ngx_log_t *log)
{
ngx_log_error(NGX_LOG_NOTICE, log, 0, "OS: %s %s",
ngx_linux_kern_ostype, ngx_linux_kern_osrelease);
-
-#if (NGX_HAVE_RTSIG)
- ngx_log_error(NGX_LOG_NOTICE, log, 0, "sysctl(KERN_RTSIGMAX): %d",
- ngx_linux_rtsig_max);
-#endif
}
diff --git a/src/os/unix/ngx_os.h b/src/os/unix/ngx_os.h
index 09f79175e..d8bcb0140 100644
--- a/src/os/unix/ngx_os.h
+++ b/src/os/unix/ngx_os.h
@@ -48,14 +48,6 @@ ssize_t ngx_unix_send(ngx_connection_t *c, u_char *buf, size_t size);
ngx_chain_t *ngx_writev_chain(ngx_connection_t *c, ngx_chain_t *in,
off_t limit);
-#if (NGX_HAVE_AIO)
-ssize_t ngx_aio_read(ngx_connection_t *c, u_char *buf, size_t size);
-ssize_t ngx_aio_read_chain(ngx_connection_t *c, ngx_chain_t *cl, off_t limit);
-ssize_t ngx_aio_write(ngx_connection_t *c, u_char *buf, size_t size);
-ngx_chain_t *ngx_aio_write_chain(ngx_connection_t *c, ngx_chain_t *in,
- off_t limit);
-#endif
-
#if (IOV_MAX > 64)
#define NGX_IOVS_PREALLOCATE 64
diff --git a/src/os/unix/ngx_process_cycle.c b/src/os/unix/ngx_process_cycle.c
index 1d5e700a8..50676326f 100644
--- a/src/os/unix/ngx_process_cycle.c
+++ b/src/os/unix/ngx_process_cycle.c
@@ -840,19 +840,6 @@ ngx_worker_process_init(ngx_cycle_t *cycle, ngx_int_t worker)
}
}
-#ifdef RLIMIT_SIGPENDING
- if (ccf->rlimit_sigpending != NGX_CONF_UNSET) {
- rlmt.rlim_cur = (rlim_t) ccf->rlimit_sigpending;
- rlmt.rlim_max = (rlim_t) ccf->rlimit_sigpending;
-
- if (setrlimit(RLIMIT_SIGPENDING, &rlmt) == -1) {
- ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
- "setrlimit(RLIMIT_SIGPENDING, %i) failed",
- ccf->rlimit_sigpending);
- }
- }
-#endif
-
if (geteuid() == 0) {
if (setgid(ccf->group) == -1) {
ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
diff --git a/src/stream/ngx_stream.c b/src/stream/ngx_stream.c
new file mode 100644
index 000000000..e5ffcf9bc
--- /dev/null
+++ b/src/stream/ngx_stream.c
@@ -0,0 +1,557 @@
+
+/*
+ * Copyright (C) Roman Arutyunyan
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_stream.h>
+
+
+static char *ngx_stream_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+static ngx_int_t ngx_stream_add_ports(ngx_conf_t *cf, ngx_array_t *ports,
+ ngx_stream_listen_t *listen);
+static char *ngx_stream_optimize_servers(ngx_conf_t *cf, ngx_array_t *ports);
+static ngx_int_t ngx_stream_add_addrs(ngx_conf_t *cf, ngx_stream_port_t *stport,
+ ngx_stream_conf_addr_t *addr);
+#if (NGX_HAVE_INET6)
+static ngx_int_t ngx_stream_add_addrs6(ngx_conf_t *cf,
+ ngx_stream_port_t *stport, ngx_stream_conf_addr_t *addr);
+#endif
+static ngx_int_t ngx_stream_cmp_conf_addrs(const void *one, const void *two);
+
+
+ngx_uint_t ngx_stream_max_module;
+
+
+static ngx_command_t ngx_stream_commands[] = {
+
+ { ngx_string("stream"),
+ NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
+ ngx_stream_block,
+ 0,
+ 0,
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_core_module_t ngx_stream_module_ctx = {
+ ngx_string("stream"),
+ NULL,
+ NULL
+};
+
+
+ngx_module_t ngx_stream_module = {
+ NGX_MODULE_V1,
+ &ngx_stream_module_ctx, /* module context */
+ ngx_stream_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_stream_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ char *rv;
+ ngx_uint_t i, m, mi, s;
+ ngx_conf_t pcf;
+ ngx_array_t ports;
+ ngx_stream_listen_t *listen;
+ ngx_stream_module_t *module;
+ ngx_stream_conf_ctx_t *ctx;
+ ngx_stream_core_srv_conf_t **cscfp;
+ ngx_stream_core_main_conf_t *cmcf;
+
+ /* the main stream context */
+
+ ctx = ngx_pcalloc(cf->pool, sizeof(ngx_stream_conf_ctx_t));
+ if (ctx == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *(ngx_stream_conf_ctx_t **) conf = ctx;
+
+ /* count the number of the stream modules and set up their indices */
+
+ ngx_stream_max_module = 0;
+ for (m = 0; ngx_modules[m]; m++) {
+ if (ngx_modules[m]->type != NGX_STREAM_MODULE) {
+ continue;
+ }
+
+ ngx_modules[m]->ctx_index = ngx_stream_max_module++;
+ }
+
+
+ /* the stream main_conf context, it's the same in the all stream contexts */
+
+ ctx->main_conf = ngx_pcalloc(cf->pool,
+ sizeof(void *) * ngx_stream_max_module);
+ if (ctx->main_conf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+
+ /*
+ * the stream null srv_conf context, it is used to merge
+ * the server{}s' srv_conf's
+ */
+
+ ctx->srv_conf = ngx_pcalloc(cf->pool,
+ sizeof(void *) * ngx_stream_max_module);
+ if (ctx->srv_conf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+
+ /*
+ * create the main_conf's and the null srv_conf's of the all stream modules
+ */
+
+ for (m = 0; ngx_modules[m]; m++) {
+ if (ngx_modules[m]->type != NGX_STREAM_MODULE) {
+ continue;
+ }
+
+ module = ngx_modules[m]->ctx;
+ mi = ngx_modules[m]->ctx_index;
+
+ if (module->create_main_conf) {
+ ctx->main_conf[mi] = module->create_main_conf(cf);
+ if (ctx->main_conf[mi] == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ if (module->create_srv_conf) {
+ ctx->srv_conf[mi] = module->create_srv_conf(cf);
+ if (ctx->srv_conf[mi] == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+ }
+
+
+ /* parse inside the stream{} block */
+
+ pcf = *cf;
+ cf->ctx = ctx;
+
+ cf->module_type = NGX_STREAM_MODULE;
+ cf->cmd_type = NGX_STREAM_MAIN_CONF;
+ rv = ngx_conf_parse(cf, NULL);
+
+ if (rv != NGX_CONF_OK) {
+ *cf = pcf;
+ return rv;
+ }
+
+
+ /* init stream{} main_conf's, merge the server{}s' srv_conf's */
+
+ cmcf = ctx->main_conf[ngx_stream_core_module.ctx_index];
+ cscfp = cmcf->servers.elts;
+
+ for (m = 0; ngx_modules[m]; m++) {
+ if (ngx_modules[m]->type != NGX_STREAM_MODULE) {
+ continue;
+ }
+
+ module = ngx_modules[m]->ctx;
+ mi = ngx_modules[m]->ctx_index;
+
+ /* init stream{} main_conf's */
+
+ cf->ctx = ctx;
+
+ if (module->init_main_conf) {
+ rv = module->init_main_conf(cf, ctx->main_conf[mi]);
+ if (rv != NGX_CONF_OK) {
+ *cf = pcf;
+ return rv;
+ }
+ }
+
+ for (s = 0; s < cmcf->servers.nelts; s++) {
+
+ /* merge the server{}s' srv_conf's */
+
+ cf->ctx = cscfp[s]->ctx;
+
+ if (module->merge_srv_conf) {
+ rv = module->merge_srv_conf(cf,
+ ctx->srv_conf[mi],
+ cscfp[s]->ctx->srv_conf[mi]);
+ if (rv != NGX_CONF_OK) {
+ *cf = pcf;
+ return rv;
+ }
+ }
+ }
+ }
+
+ *cf = pcf;
+
+
+ if (ngx_array_init(&ports, cf->temp_pool, 4, sizeof(ngx_stream_conf_port_t))
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ listen = cmcf->listen.elts;
+
+ for (i = 0; i < cmcf->listen.nelts; i++) {
+ if (ngx_stream_add_ports(cf, &ports, &listen[i]) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ return ngx_stream_optimize_servers(cf, &ports);
+}
+
+
+static ngx_int_t
+ngx_stream_add_ports(ngx_conf_t *cf, ngx_array_t *ports,
+ ngx_stream_listen_t *listen)
+{
+ in_port_t p;
+ ngx_uint_t i;
+ struct sockaddr *sa;
+ struct sockaddr_in *sin;
+ ngx_stream_conf_port_t *port;
+ ngx_stream_conf_addr_t *addr;
+#if (NGX_HAVE_INET6)
+ struct sockaddr_in6 *sin6;
+#endif
+
+ sa = (struct sockaddr *) &listen->sockaddr;
+
+ switch (sa->sa_family) {
+
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *) sa;
+ p = sin6->sin6_port;
+ break;
+#endif
+
+#if (NGX_HAVE_UNIX_DOMAIN)
+ case AF_UNIX:
+ p = 0;
+ break;
+#endif
+
+ default: /* AF_INET */
+ sin = (struct sockaddr_in *) sa;
+ p = sin->sin_port;
+ break;
+ }
+
+ port = ports->elts;
+ for (i = 0; i < ports->nelts; i++) {
+ if (p == port[i].port && sa->sa_family == port[i].family) {
+
+ /* a port is already in the port list */
+
+ port = &port[i];
+ goto found;
+ }
+ }
+
+ /* add a port to the port list */
+
+ port = ngx_array_push(ports);
+ if (port == NULL) {
+ return NGX_ERROR;
+ }
+
+ port->family = sa->sa_family;
+ port->port = p;
+
+ if (ngx_array_init(&port->addrs, cf->temp_pool, 2,
+ sizeof(ngx_stream_conf_addr_t))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+found:
+
+ addr = ngx_array_push(&port->addrs);
+ if (addr == NULL) {
+ return NGX_ERROR;
+ }
+
+ addr->sockaddr = (struct sockaddr *) &listen->sockaddr;
+ addr->socklen = listen->socklen;
+ addr->ctx = listen->ctx;
+ addr->bind = listen->bind;
+ addr->wildcard = listen->wildcard;
+ addr->so_keepalive = listen->so_keepalive;
+#if (NGX_HAVE_KEEPALIVE_TUNABLE)
+ addr->tcp_keepidle = listen->tcp_keepidle;
+ addr->tcp_keepintvl = listen->tcp_keepintvl;
+ addr->tcp_keepcnt = listen->tcp_keepcnt;
+#endif
+#if (NGX_STREAM_SSL)
+ addr->ssl = listen->ssl;
+#endif
+#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
+ addr->ipv6only = listen->ipv6only;
+#endif
+
+ return NGX_OK;
+}
+
+
+static char *
+ngx_stream_optimize_servers(ngx_conf_t *cf, ngx_array_t *ports)
+{
+ ngx_uint_t i, p, last, bind_wildcard;
+ ngx_listening_t *ls;
+ ngx_stream_port_t *stport;
+ ngx_stream_conf_port_t *port;
+ ngx_stream_conf_addr_t *addr;
+ ngx_stream_core_srv_conf_t *cscf;
+
+ port = ports->elts;
+ for (p = 0; p < ports->nelts; p++) {
+
+ ngx_sort(port[p].addrs.elts, (size_t) port[p].addrs.nelts,
+ sizeof(ngx_stream_conf_addr_t), ngx_stream_cmp_conf_addrs);
+
+ addr = port[p].addrs.elts;
+ last = port[p].addrs.nelts;
+
+ /*
+ * if there is the binding to the "*:port" then we need to bind()
+ * to the "*:port" only and ignore the other bindings
+ */
+
+ if (addr[last - 1].wildcard) {
+ addr[last - 1].bind = 1;
+ bind_wildcard = 1;
+
+ } else {
+ bind_wildcard = 0;
+ }
+
+ i = 0;
+
+ while (i < last) {
+
+ if (bind_wildcard && !addr[i].bind) {
+ i++;
+ continue;
+ }
+
+ ls = ngx_create_listening(cf, addr[i].sockaddr, addr[i].socklen);
+ if (ls == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ls->addr_ntop = 1;
+ ls->handler = ngx_stream_init_connection;
+ ls->pool_size = 256;
+
+ cscf = addr->ctx->srv_conf[ngx_stream_core_module.ctx_index];
+
+ ls->logp = cscf->error_log;
+ ls->log.data = &ls->addr_text;
+ ls->log.handler = ngx_accept_log_error;
+
+ ls->keepalive = addr[i].so_keepalive;
+#if (NGX_HAVE_KEEPALIVE_TUNABLE)
+ ls->keepidle = addr[i].tcp_keepidle;
+ ls->keepintvl = addr[i].tcp_keepintvl;
+ ls->keepcnt = addr[i].tcp_keepcnt;
+#endif
+
+#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
+ ls->ipv6only = addr[i].ipv6only;
+#endif
+
+ stport = ngx_palloc(cf->pool, sizeof(ngx_stream_port_t));
+ if (stport == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ls->servers = stport;
+
+ if (i == last - 1) {
+ stport->naddrs = last;
+
+ } else {
+ stport->naddrs = 1;
+ i = 0;
+ }
+
+ switch (ls->sockaddr->sa_family) {
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ if (ngx_stream_add_addrs6(cf, stport, addr) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+ break;
+#endif
+ default: /* AF_INET */
+ if (ngx_stream_add_addrs(cf, stport, addr) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+ break;
+ }
+
+ addr++;
+ last--;
+ }
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_stream_add_addrs(ngx_conf_t *cf, ngx_stream_port_t *stport,
+ ngx_stream_conf_addr_t *addr)
+{
+ u_char *p;
+ size_t len;
+ ngx_uint_t i;
+ struct sockaddr_in *sin;
+ ngx_stream_in_addr_t *addrs;
+ u_char buf[NGX_SOCKADDR_STRLEN];
+
+ stport->addrs = ngx_pcalloc(cf->pool,
+ stport->naddrs * sizeof(ngx_stream_in_addr_t));
+ if (stport->addrs == NULL) {
+ return NGX_ERROR;
+ }
+
+ addrs = stport->addrs;
+
+ for (i = 0; i < stport->naddrs; i++) {
+
+ sin = (struct sockaddr_in *) addr[i].sockaddr;
+ addrs[i].addr = sin->sin_addr.s_addr;
+
+ addrs[i].conf.ctx = addr[i].ctx;
+#if (NGX_STREAM_SSL)
+ addrs[i].conf.ssl = addr[i].ssl;
+#endif
+
+ len = ngx_sock_ntop(addr[i].sockaddr, addr[i].socklen, buf,
+ NGX_SOCKADDR_STRLEN, 1);
+
+ p = ngx_pnalloc(cf->pool, len);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(p, buf, len);
+
+ addrs[i].conf.addr_text.len = len;
+ addrs[i].conf.addr_text.data = p;
+ }
+
+ return NGX_OK;
+}
+
+
+#if (NGX_HAVE_INET6)
+
+static ngx_int_t
+ngx_stream_add_addrs6(ngx_conf_t *cf, ngx_stream_port_t *stport,
+ ngx_stream_conf_addr_t *addr)
+{
+ u_char *p;
+ size_t len;
+ ngx_uint_t i;
+ struct sockaddr_in6 *sin6;
+ ngx_stream_in6_addr_t *addrs6;
+ u_char buf[NGX_SOCKADDR_STRLEN];
+
+ stport->addrs = ngx_pcalloc(cf->pool,
+ stport->naddrs * sizeof(ngx_stream_in6_addr_t));
+ if (stport->addrs == NULL) {
+ return NGX_ERROR;
+ }
+
+ addrs6 = stport->addrs;
+
+ for (i = 0; i < stport->naddrs; i++) {
+
+ sin6 = (struct sockaddr_in6 *) addr[i].sockaddr;
+ addrs6[i].addr6 = sin6->sin6_addr;
+
+ addrs6[i].conf.ctx = addr[i].ctx;
+#if (NGX_STREAM_SSL)
+ addrs6[i].conf.ssl = addr[i].ssl;
+#endif
+
+ len = ngx_sock_ntop(addr[i].sockaddr, addr[i].socklen, buf,
+ NGX_SOCKADDR_STRLEN, 1);
+
+ p = ngx_pnalloc(cf->pool, len);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(p, buf, len);
+
+ addrs6[i].conf.addr_text.len = len;
+ addrs6[i].conf.addr_text.data = p;
+ }
+
+ return NGX_OK;
+}
+
+#endif
+
+
+static ngx_int_t
+ngx_stream_cmp_conf_addrs(const void *one, const void *two)
+{
+ ngx_stream_conf_addr_t *first, *second;
+
+ first = (ngx_stream_conf_addr_t *) one;
+ second = (ngx_stream_conf_addr_t *) two;
+
+ if (first->wildcard) {
+ /* a wildcard must be the last resort, shift it to the end */
+ return 1;
+ }
+
+ if (second->wildcard) {
+ /* a wildcard must be the last resort, shift it to the end */
+ return -1;
+ }
+
+ if (first->bind && !second->bind) {
+ /* shift explicit bind()ed addresses to the start */
+ return -1;
+ }
+
+ if (!first->bind && second->bind) {
+ /* shift explicit bind()ed addresses to the start */
+ return 1;
+ }
+
+ /* do not sort by default */
+
+ return 0;
+}
diff --git a/src/stream/ngx_stream.h b/src/stream/ngx_stream.h
new file mode 100644
index 000000000..83a43a41f
--- /dev/null
+++ b/src/stream/ngx_stream.h
@@ -0,0 +1,205 @@
+
+/*
+ * Copyright (C) Roman Arutyunyan
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_STREAM_H_INCLUDED_
+#define _NGX_STREAM_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+#if (NGX_STREAM_SSL)
+#include <ngx_stream_ssl_module.h>
+#endif
+
+
+typedef struct ngx_stream_session_s ngx_stream_session_t;
+
+
+#include <ngx_stream_upstream.h>
+#include <ngx_stream_upstream_round_robin.h>
+
+
+typedef struct {
+ void **main_conf;
+ void **srv_conf;
+} ngx_stream_conf_ctx_t;
+
+
+typedef struct {
+ u_char sockaddr[NGX_SOCKADDRLEN];
+ socklen_t socklen;
+
+ /* server ctx */
+ ngx_stream_conf_ctx_t *ctx;
+
+ unsigned bind:1;
+ unsigned wildcard:1;
+#if (NGX_STREAM_SSL)
+ unsigned ssl:1;
+#endif
+#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
+ unsigned ipv6only:1;
+#endif
+ unsigned so_keepalive:2;
+#if (NGX_HAVE_KEEPALIVE_TUNABLE)
+ int tcp_keepidle;
+ int tcp_keepintvl;
+ int tcp_keepcnt;
+#endif
+} ngx_stream_listen_t;
+
+
+typedef struct {
+ ngx_stream_conf_ctx_t *ctx;
+ ngx_str_t addr_text;
+#if (NGX_STREAM_SSL)
+ ngx_uint_t ssl; /* unsigned ssl:1; */
+#endif
+} ngx_stream_addr_conf_t;
+
+typedef struct {
+ in_addr_t addr;
+ ngx_stream_addr_conf_t conf;
+} ngx_stream_in_addr_t;
+
+
+#if (NGX_HAVE_INET6)
+
+typedef struct {
+ struct in6_addr addr6;
+ ngx_stream_addr_conf_t conf;
+} ngx_stream_in6_addr_t;
+
+#endif
+
+
+typedef struct {
+ /* ngx_stream_in_addr_t or ngx_stream_in6_addr_t */
+ void *addrs;
+ ngx_uint_t naddrs;
+} ngx_stream_port_t;
+
+
+typedef struct {
+ int family;
+ in_port_t port;
+ ngx_array_t addrs; /* array of ngx_stream_conf_addr_t */
+} ngx_stream_conf_port_t;
+
+
+typedef struct {
+ struct sockaddr *sockaddr;
+ socklen_t socklen;
+
+ ngx_stream_conf_ctx_t *ctx;
+
+ unsigned bind:1;
+ unsigned wildcard:1;
+#if (NGX_STREAM_SSL)
+ unsigned ssl:1;
+#endif
+#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
+ unsigned ipv6only:1;
+#endif
+ unsigned so_keepalive:2;
+#if (NGX_HAVE_KEEPALIVE_TUNABLE)
+ int tcp_keepidle;
+ int tcp_keepintvl;
+ int tcp_keepcnt;
+#endif
+} ngx_stream_conf_addr_t;
+
+
+typedef struct {
+ ngx_array_t servers; /* ngx_stream_core_srv_conf_t */
+ ngx_array_t listen; /* ngx_stream_listen_t */
+} ngx_stream_core_main_conf_t;
+
+
+typedef void (*ngx_stream_handler_pt)(ngx_stream_session_t *s);
+
+
+typedef struct {
+ ngx_stream_handler_pt handler;
+ ngx_stream_conf_ctx_t *ctx;
+ u_char *file_name;
+ ngx_int_t line;
+ ngx_log_t *error_log;
+} ngx_stream_core_srv_conf_t;
+
+
+struct ngx_stream_session_s {
+ uint32_t signature; /* "STRM" */
+
+ ngx_connection_t *connection;
+
+ off_t received;
+
+ ngx_log_handler_pt log_handler;
+
+ void **ctx;
+ void **main_conf;
+ void **srv_conf;
+
+ ngx_stream_upstream_t *upstream;
+};
+
+
+typedef struct {
+ void *(*create_main_conf)(ngx_conf_t *cf);
+ char *(*init_main_conf)(ngx_conf_t *cf, void *conf);
+
+ void *(*create_srv_conf)(ngx_conf_t *cf);
+ char *(*merge_srv_conf)(ngx_conf_t *cf, void *prev,
+ void *conf);
+} ngx_stream_module_t;
+
+
+#define NGX_STREAM_MODULE 0x4d525453 /* "STRM" */
+
+#define NGX_STREAM_MAIN_CONF 0x02000000
+#define NGX_STREAM_SRV_CONF 0x04000000
+#define NGX_STREAM_UPS_CONF 0x08000000
+
+
+#define NGX_STREAM_MAIN_CONF_OFFSET offsetof(ngx_stream_conf_ctx_t, main_conf)
+#define NGX_STREAM_SRV_CONF_OFFSET offsetof(ngx_stream_conf_ctx_t, srv_conf)
+
+
+#define ngx_stream_get_module_ctx(s, module) (s)->ctx[module.ctx_index]
+#define ngx_stream_set_ctx(s, c, module) s->ctx[module.ctx_index] = c;
+#define ngx_stream_delete_ctx(s, module) s->ctx[module.ctx_index] = NULL;
+
+
+#define ngx_stream_get_module_main_conf(s, module) \
+ (s)->main_conf[module.ctx_index]
+#define ngx_stream_get_module_srv_conf(s, module) \
+ (s)->srv_conf[module.ctx_index]
+
+#define ngx_stream_conf_get_module_main_conf(cf, module) \
+ ((ngx_stream_conf_ctx_t *) cf->ctx)->main_conf[module.ctx_index]
+#define ngx_stream_conf_get_module_srv_conf(cf, module) \
+ ((ngx_stream_conf_ctx_t *) cf->ctx)->srv_conf[module.ctx_index]
+
+#define ngx_stream_cycle_get_module_main_conf(cycle, module) \
+ (cycle->conf_ctx[ngx_stream_module.index] ? \
+ ((ngx_stream_conf_ctx_t *) cycle->conf_ctx[ngx_stream_module.index]) \
+ ->main_conf[module.ctx_index]: \
+ NULL)
+
+
+void ngx_stream_init_connection(ngx_connection_t *c);
+void ngx_stream_close_connection(ngx_connection_t *c);
+
+
+extern ngx_module_t ngx_stream_module;
+extern ngx_uint_t ngx_stream_max_module;
+extern ngx_module_t ngx_stream_core_module;
+
+
+#endif /* _NGX_STREAM_H_INCLUDED_ */
diff --git a/src/stream/ngx_stream_core_module.c b/src/stream/ngx_stream_core_module.c
new file mode 100644
index 000000000..c0df412a5
--- /dev/null
+++ b/src/stream/ngx_stream_core_module.c
@@ -0,0 +1,495 @@
+
+/*
+ * Copyright (C) Roman Arutyunyan
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_stream.h>
+
+
+static void *ngx_stream_core_create_main_conf(ngx_conf_t *cf);
+static void *ngx_stream_core_create_srv_conf(ngx_conf_t *cf);
+static char *ngx_stream_core_merge_srv_conf(ngx_conf_t *cf, void *parent,
+ void *child);
+static char *ngx_stream_core_error_log(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_stream_core_server(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_stream_core_listen(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
+
+static ngx_command_t ngx_stream_core_commands[] = {
+
+ { ngx_string("server"),
+ NGX_STREAM_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
+ ngx_stream_core_server,
+ 0,
+ 0,
+ NULL },
+
+ { ngx_string("listen"),
+ NGX_STREAM_SRV_CONF|NGX_CONF_1MORE,
+ ngx_stream_core_listen,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("error_log"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_1MORE,
+ ngx_stream_core_error_log,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ 0,
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_stream_module_t ngx_stream_core_module_ctx = {
+ ngx_stream_core_create_main_conf, /* create main configuration */
+ NULL, /* init main configuration */
+
+ ngx_stream_core_create_srv_conf, /* create server configuration */
+ ngx_stream_core_merge_srv_conf /* merge server configuration */
+};
+
+
+ngx_module_t ngx_stream_core_module = {
+ NGX_MODULE_V1,
+ &ngx_stream_core_module_ctx, /* module context */
+ ngx_stream_core_commands, /* module directives */
+ NGX_STREAM_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 void *
+ngx_stream_core_create_main_conf(ngx_conf_t *cf)
+{
+ ngx_stream_core_main_conf_t *cmcf;
+
+ cmcf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_core_main_conf_t));
+ if (cmcf == NULL) {
+ return NULL;
+ }
+
+ if (ngx_array_init(&cmcf->servers, cf->pool, 4,
+ sizeof(ngx_stream_core_srv_conf_t *))
+ != NGX_OK)
+ {
+ return NULL;
+ }
+
+ if (ngx_array_init(&cmcf->listen, cf->pool, 4, sizeof(ngx_stream_listen_t))
+ != NGX_OK)
+ {
+ return NULL;
+ }
+
+ return cmcf;
+}
+
+
+static void *
+ngx_stream_core_create_srv_conf(ngx_conf_t *cf)
+{
+ ngx_stream_core_srv_conf_t *cscf;
+
+ cscf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_core_srv_conf_t));
+ if (cscf == NULL) {
+ return NULL;
+ }
+
+ /*
+ * set by ngx_pcalloc():
+ *
+ * cscf->handler = NULL;
+ * cscf->error_log = NULL;
+ */
+
+ cscf->file_name = cf->conf_file->file.name.data;
+ cscf->line = cf->conf_file->line;
+
+ return cscf;
+}
+
+
+static char *
+ngx_stream_core_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_stream_core_srv_conf_t *prev = parent;
+ ngx_stream_core_srv_conf_t *conf = child;
+
+ if (conf->handler == NULL) {
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "no handler for server in %s:%ui",
+ conf->file_name, conf->line);
+ return NGX_CONF_ERROR;
+ }
+
+ if (conf->error_log == NULL) {
+ if (prev->error_log) {
+ conf->error_log = prev->error_log;
+ } else {
+ conf->error_log = &cf->cycle->new_log;
+ }
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_stream_core_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_stream_core_srv_conf_t *cscf = conf;
+
+ return ngx_log_set_log(cf, &cscf->error_log);
+}
+
+
+static char *
+ngx_stream_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ char *rv;
+ void *mconf;
+ ngx_uint_t m;
+ ngx_conf_t pcf;
+ ngx_stream_module_t *module;
+ ngx_stream_conf_ctx_t *ctx, *stream_ctx;
+ ngx_stream_core_srv_conf_t *cscf, **cscfp;
+ ngx_stream_core_main_conf_t *cmcf;
+
+ ctx = ngx_pcalloc(cf->pool, sizeof(ngx_stream_conf_ctx_t));
+ if (ctx == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ stream_ctx = cf->ctx;
+ ctx->main_conf = stream_ctx->main_conf;
+
+ /* the server{}'s srv_conf */
+
+ ctx->srv_conf = ngx_pcalloc(cf->pool,
+ sizeof(void *) * ngx_stream_max_module);
+ if (ctx->srv_conf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ for (m = 0; ngx_modules[m]; m++) {
+ if (ngx_modules[m]->type != NGX_STREAM_MODULE) {
+ continue;
+ }
+
+ module = ngx_modules[m]->ctx;
+
+ if (module->create_srv_conf) {
+ mconf = module->create_srv_conf(cf);
+ if (mconf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ctx->srv_conf[ngx_modules[m]->ctx_index] = mconf;
+ }
+ }
+
+ /* the server configuration context */
+
+ cscf = ctx->srv_conf[ngx_stream_core_module.ctx_index];
+ cscf->ctx = ctx;
+
+ cmcf = ctx->main_conf[ngx_stream_core_module.ctx_index];
+
+ cscfp = ngx_array_push(&cmcf->servers);
+ if (cscfp == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *cscfp = cscf;
+
+
+ /* parse inside server{} */
+
+ pcf = *cf;
+ cf->ctx = ctx;
+ cf->cmd_type = NGX_STREAM_SRV_CONF;
+
+ rv = ngx_conf_parse(cf, NULL);
+
+ *cf = pcf;
+
+ return rv;
+}
+
+
+static char *
+ngx_stream_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ size_t len, off;
+ in_port_t port;
+ ngx_str_t *value;
+ ngx_url_t u;
+ ngx_uint_t i;
+ struct sockaddr *sa;
+ struct sockaddr_in *sin;
+ ngx_stream_listen_t *ls;
+ ngx_stream_core_main_conf_t *cmcf;
+#if (NGX_HAVE_INET6)
+ struct sockaddr_in6 *sin6;
+#endif
+
+ value = cf->args->elts;
+
+ ngx_memzero(&u, sizeof(ngx_url_t));
+
+ u.url = value[1];
+ u.listen = 1;
+
+ if (ngx_parse_url(cf->pool, &u) != NGX_OK) {
+ if (u.err) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "%s in \"%V\" of the \"listen\" directive",
+ u.err, &u.url);
+ }
+
+ return NGX_CONF_ERROR;
+ }
+
+ cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);
+
+ ls = cmcf->listen.elts;
+
+ for (i = 0; i < cmcf->listen.nelts; i++) {
+
+ sa = (struct sockaddr *) ls[i].sockaddr;
+
+ if (sa->sa_family != u.family) {
+ continue;
+ }
+
+ switch (sa->sa_family) {
+
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ off = offsetof(struct sockaddr_in6, sin6_addr);
+ len = 16;
+ sin6 = (struct sockaddr_in6 *) sa;
+ port = sin6->sin6_port;
+ break;
+#endif
+
+#if (NGX_HAVE_UNIX_DOMAIN)
+ case AF_UNIX:
+ off = offsetof(struct sockaddr_un, sun_path);
+ len = sizeof(((struct sockaddr_un *) sa)->sun_path);
+ port = 0;
+ break;
+#endif
+
+ default: /* AF_INET */
+ off = offsetof(struct sockaddr_in, sin_addr);
+ len = 4;
+ sin = (struct sockaddr_in *) sa;
+ port = sin->sin_port;
+ break;
+ }
+
+ if (ngx_memcmp(ls[i].sockaddr + off, u.sockaddr + off, len) != 0) {
+ continue;
+ }
+
+ if (port != u.port) {
+ continue;
+ }
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "duplicate \"%V\" address and port pair", &u.url);
+ return NGX_CONF_ERROR;
+ }
+
+ ls = ngx_array_push(&cmcf->listen);
+ if (ls == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_memzero(ls, sizeof(ngx_stream_listen_t));
+
+ ngx_memcpy(ls->sockaddr, u.sockaddr, u.socklen);
+
+ ls->socklen = u.socklen;
+ ls->wildcard = u.wildcard;
+ ls->ctx = cf->ctx;
+
+#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
+ ls->ipv6only = 1;
+#endif
+
+ for (i = 2; i < cf->args->nelts; i++) {
+
+ if (ngx_strcmp(value[i].data, "bind") == 0) {
+ ls->bind = 1;
+ continue;
+ }
+
+ if (ngx_strncmp(value[i].data, "ipv6only=o", 10) == 0) {
+#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
+ struct sockaddr *sa;
+ u_char buf[NGX_SOCKADDR_STRLEN];
+
+ sa = (struct sockaddr *) ls->sockaddr;
+
+ if (sa->sa_family == AF_INET6) {
+
+ if (ngx_strcmp(&value[i].data[10], "n") == 0) {
+ ls->ipv6only = 1;
+
+ } else if (ngx_strcmp(&value[i].data[10], "ff") == 0) {
+ ls->ipv6only = 0;
+
+ } else {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid ipv6only flags \"%s\"",
+ &value[i].data[9]);
+ return NGX_CONF_ERROR;
+ }
+
+ ls->bind = 1;
+
+ } else {
+ len = ngx_sock_ntop(sa, ls->socklen, buf,
+ NGX_SOCKADDR_STRLEN, 1);
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "ipv6only is not supported "
+ "on addr \"%*s\", ignored", len, buf);
+ }
+
+ continue;
+#else
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "bind ipv6only is not supported "
+ "on this platform");
+ return NGX_CONF_ERROR;
+#endif
+ }
+
+ if (ngx_strcmp(value[i].data, "ssl") == 0) {
+#if (NGX_STREAM_SSL)
+ ls->ssl = 1;
+ continue;
+#else
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "the \"ssl\" parameter requires "
+ "ngx_stream_ssl_module");
+ return NGX_CONF_ERROR;
+#endif
+ }
+
+ if (ngx_strncmp(value[i].data, "so_keepalive=", 13) == 0) {
+
+ if (ngx_strcmp(&value[i].data[13], "on") == 0) {
+ ls->so_keepalive = 1;
+
+ } else if (ngx_strcmp(&value[i].data[13], "off") == 0) {
+ ls->so_keepalive = 2;
+
+ } else {
+
+#if (NGX_HAVE_KEEPALIVE_TUNABLE)
+ u_char *p, *end;
+ ngx_str_t s;
+
+ end = value[i].data + value[i].len;
+ s.data = value[i].data + 13;
+
+ p = ngx_strlchr(s.data, end, ':');
+ if (p == NULL) {
+ p = end;
+ }
+
+ if (p > s.data) {
+ s.len = p - s.data;
+
+ ls->tcp_keepidle = ngx_parse_time(&s, 1);
+ if (ls->tcp_keepidle == (time_t) NGX_ERROR) {
+ goto invalid_so_keepalive;
+ }
+ }
+
+ s.data = (p < end) ? (p + 1) : end;
+
+ p = ngx_strlchr(s.data, end, ':');
+ if (p == NULL) {
+ p = end;
+ }
+
+ if (p > s.data) {
+ s.len = p - s.data;
+
+ ls->tcp_keepintvl = ngx_parse_time(&s, 1);
+ if (ls->tcp_keepintvl == (time_t) NGX_ERROR) {
+ goto invalid_so_keepalive;
+ }
+ }
+
+ s.data = (p < end) ? (p + 1) : end;
+
+ if (s.data < end) {
+ s.len = end - s.data;
+
+ ls->tcp_keepcnt = ngx_atoi(s.data, s.len);
+ if (ls->tcp_keepcnt == NGX_ERROR) {
+ goto invalid_so_keepalive;
+ }
+ }
+
+ if (ls->tcp_keepidle == 0 && ls->tcp_keepintvl == 0
+ && ls->tcp_keepcnt == 0)
+ {
+ goto invalid_so_keepalive;
+ }
+
+ ls->so_keepalive = 1;
+
+#else
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "the \"so_keepalive\" parameter accepts "
+ "only \"on\" or \"off\" on this platform");
+ return NGX_CONF_ERROR;
+
+#endif
+ }
+
+ ls->bind = 1;
+
+ continue;
+
+#if (NGX_HAVE_KEEPALIVE_TUNABLE)
+ invalid_so_keepalive:
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid so_keepalive value: \"%s\"",
+ &value[i].data[13]);
+ return NGX_CONF_ERROR;
+#endif
+ }
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "the invalid \"%V\" parameter", &value[i]);
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
diff --git a/src/stream/ngx_stream_handler.c b/src/stream/ngx_stream_handler.c
new file mode 100644
index 000000000..2be5183c1
--- /dev/null
+++ b/src/stream/ngx_stream_handler.c
@@ -0,0 +1,296 @@
+
+/*
+ * Copyright (C) Roman Arutyunyan
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_stream.h>
+
+
+static u_char *ngx_stream_log_error(ngx_log_t *log, u_char *buf, size_t len);
+static void ngx_stream_init_session(ngx_connection_t *c);
+
+#if (NGX_STREAM_SSL)
+static void ngx_stream_ssl_init_connection(ngx_ssl_t *ssl, ngx_connection_t *c);
+static void ngx_stream_ssl_handshake_handler(ngx_connection_t *c);
+#endif
+
+
+void
+ngx_stream_init_connection(ngx_connection_t *c)
+{
+ u_char text[NGX_SOCKADDR_STRLEN];
+ size_t len;
+ ngx_uint_t i;
+ struct sockaddr *sa;
+ ngx_stream_port_t *port;
+ struct sockaddr_in *sin;
+ ngx_stream_in_addr_t *addr;
+ ngx_stream_session_t *s;
+ ngx_stream_addr_conf_t *addr_conf;
+#if (NGX_HAVE_INET6)
+ struct sockaddr_in6 *sin6;
+ ngx_stream_in6_addr_t *addr6;
+#endif
+ ngx_stream_core_srv_conf_t *cscf;
+
+ /* find the server configuration for the address:port */
+
+ port = c->listening->servers;
+
+ if (port->naddrs > 1) {
+
+ /*
+ * There are several addresses on this port and one of them
+ * is the "*:port" wildcard so getsockname() is needed to determine
+ * the server address.
+ *
+ * AcceptEx() already gave this address.
+ */
+
+ if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) {
+ ngx_stream_close_connection(c);
+ return;
+ }
+
+ sa = c->local_sockaddr;
+
+ switch (sa->sa_family) {
+
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *) sa;
+
+ addr6 = port->addrs;
+
+ /* the last address is "*" */
+
+ for (i = 0; i < port->naddrs - 1; i++) {
+ if (ngx_memcmp(&addr6[i].addr6, &sin6->sin6_addr, 16) == 0) {
+ break;
+ }
+ }
+
+ addr_conf = &addr6[i].conf;
+
+ break;
+#endif
+
+ default: /* AF_INET */
+ sin = (struct sockaddr_in *) sa;
+
+ addr = port->addrs;
+
+ /* the last address is "*" */
+
+ for (i = 0; i < port->naddrs - 1; i++) {
+ if (addr[i].addr == sin->sin_addr.s_addr) {
+ break;
+ }
+ }
+
+ addr_conf = &addr[i].conf;
+
+ break;
+ }
+
+ } else {
+ switch (c->local_sockaddr->sa_family) {
+
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ addr6 = port->addrs;
+ addr_conf = &addr6[0].conf;
+ break;
+#endif
+
+ default: /* AF_INET */
+ addr = port->addrs;
+ addr_conf = &addr[0].conf;
+ break;
+ }
+ }
+
+ s = ngx_pcalloc(c->pool, sizeof(ngx_stream_session_t));
+ if (s == NULL) {
+ ngx_stream_close_connection(c);
+ return;
+ }
+
+ s->signature = NGX_STREAM_MODULE;
+ s->main_conf = addr_conf->ctx->main_conf;
+ s->srv_conf = addr_conf->ctx->srv_conf;
+
+ s->connection = c;
+ c->data = s;
+
+ cscf = ngx_stream_get_module_srv_conf(s, ngx_stream_core_module);
+
+ ngx_set_connection_log(c, cscf->error_log);
+
+ 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, &addr_conf->addr_text);
+
+ c->log->connection = c->number;
+ c->log->handler = ngx_stream_log_error;
+ c->log->data = s;
+ c->log->action = "initializing connection";
+ c->log_error = NGX_ERROR_INFO;
+
+#if (NGX_STREAM_SSL)
+ {
+ ngx_stream_ssl_conf_t *sslcf;
+
+ sslcf = ngx_stream_get_module_srv_conf(s, ngx_stream_ssl_module);
+
+ if (addr_conf->ssl) {
+ c->log->action = "SSL handshaking";
+
+ if (sslcf->ssl.ctx == NULL) {
+ ngx_log_error(NGX_LOG_ERR, c->log, 0,
+ "no \"ssl_certificate\" is defined "
+ "in server listening on SSL port");
+ ngx_stream_close_connection(c);
+ return;
+ }
+
+ ngx_stream_ssl_init_connection(&sslcf->ssl, c);
+ return;
+ }
+ }
+#endif
+
+ ngx_stream_init_session(c);
+}
+
+
+static void
+ngx_stream_init_session(ngx_connection_t *c)
+{
+ ngx_stream_session_t *s;
+ ngx_stream_core_srv_conf_t *cscf;
+
+ s = c->data;
+ c->log->action = "handling client connection";
+
+ cscf = ngx_stream_get_module_srv_conf(s, ngx_stream_core_module);
+
+ s->ctx = ngx_pcalloc(c->pool, sizeof(void *) * ngx_stream_max_module);
+ if (s->ctx == NULL) {
+ ngx_stream_close_connection(c);
+ return;
+ }
+
+ cscf->handler(s);
+}
+
+
+#if (NGX_STREAM_SSL)
+
+static void
+ngx_stream_ssl_init_connection(ngx_ssl_t *ssl, ngx_connection_t *c)
+{
+ ngx_stream_session_t *s;
+ ngx_stream_ssl_conf_t *sslcf;
+
+ if (ngx_ssl_create_connection(ssl, c, 0) == NGX_ERROR) {
+ ngx_stream_close_connection(c);
+ return;
+ }
+
+ if (ngx_ssl_handshake(c) == NGX_AGAIN) {
+
+ s = c->data;
+
+ sslcf = ngx_stream_get_module_srv_conf(s, ngx_stream_ssl_module);
+
+ ngx_add_timer(c->read, sslcf->handshake_timeout);
+
+ c->ssl->handler = ngx_stream_ssl_handshake_handler;
+
+ return;
+ }
+
+ ngx_stream_ssl_handshake_handler(c);
+}
+
+
+static void
+ngx_stream_ssl_handshake_handler(ngx_connection_t *c)
+{
+ if (!c->ssl->handshaked) {
+ ngx_stream_close_connection(c);
+ return;
+ }
+
+ if (c->read->timer_set) {
+ ngx_del_timer(c->read);
+ }
+
+ ngx_stream_init_session(c);
+}
+
+#endif
+
+
+void
+ngx_stream_close_connection(ngx_connection_t *c)
+{
+ ngx_pool_t *pool;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0,
+ "close stream connection: %d", c->fd);
+
+#if (NGX_STREAM_SSL)
+
+ if (c->ssl) {
+ if (ngx_ssl_shutdown(c) == NGX_AGAIN) {
+ c->ssl->handler = ngx_stream_close_connection;
+ return;
+ }
+ }
+
+#endif
+
+#if (NGX_STAT_STUB)
+ (void) ngx_atomic_fetch_add(ngx_stat_active, -1);
+#endif
+
+ pool = c->pool;
+
+ ngx_close_connection(c);
+
+ ngx_destroy_pool(pool);
+}
+
+
+static u_char *
+ngx_stream_log_error(ngx_log_t *log, u_char *buf, size_t len)
+{
+ u_char *p;
+ ngx_stream_session_t *s;
+
+ if (log->action) {
+ p = ngx_snprintf(buf, len, " while %s", log->action);
+ len -= p - buf;
+ buf = p;
+ }
+
+ s = log->data;
+
+ p = ngx_snprintf(buf, len, ", client: %V, server: %V",
+ &s->connection->addr_text,
+ &s->connection->listening->addr_text);
+
+ if (s->log_handler) {
+ return s->log_handler(log, p, len);
+ }
+
+ return p;
+}
diff --git a/src/stream/ngx_stream_proxy_module.c b/src/stream/ngx_stream_proxy_module.c
new file mode 100644
index 000000000..8727629fd
--- /dev/null
+++ b/src/stream/ngx_stream_proxy_module.c
@@ -0,0 +1,1291 @@
+
+/*
+ * Copyright (C) Roman Arutyunyan
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_stream.h>
+
+
+typedef void (*ngx_stream_proxy_handler_pt)(ngx_stream_session_t *s);
+
+
+typedef struct {
+ ngx_msec_t connect_timeout;
+ ngx_msec_t timeout;
+ ngx_msec_t next_upstream_timeout;
+ size_t downstream_buf_size;
+ size_t upstream_buf_size;
+ ngx_uint_t next_upstream_tries;
+ ngx_flag_t next_upstream;
+
+#if (NGX_STREAM_SSL)
+ ngx_flag_t ssl_enable;
+ ngx_flag_t ssl_session_reuse;
+ ngx_uint_t ssl_protocols;
+ ngx_str_t ssl_ciphers;
+ ngx_str_t ssl_name;
+ ngx_flag_t ssl_server_name;
+
+ ngx_flag_t ssl_verify;
+ ngx_uint_t ssl_verify_depth;
+ ngx_str_t ssl_trusted_certificate;
+ ngx_str_t ssl_crl;
+ ngx_str_t ssl_certificate;
+ ngx_str_t ssl_certificate_key;
+ ngx_array_t *ssl_passwords;
+
+ ngx_ssl_t *ssl;
+#endif
+
+ ngx_stream_upstream_srv_conf_t *upstream;
+} ngx_stream_proxy_srv_conf_t;
+
+
+static void ngx_stream_proxy_handler(ngx_stream_session_t *s);
+static void ngx_stream_proxy_connect(ngx_stream_session_t *s);
+static void ngx_stream_proxy_init_upstream(ngx_stream_session_t *s);
+static void ngx_stream_proxy_upstream_handler(ngx_event_t *ev);
+static void ngx_stream_proxy_downstream_handler(ngx_event_t *ev);
+static void ngx_stream_proxy_connect_handler(ngx_event_t *ev);
+static ngx_int_t ngx_stream_proxy_test_connect(ngx_connection_t *c);
+static ngx_int_t ngx_stream_proxy_process(ngx_stream_session_t *s,
+ ngx_uint_t from_upstream, ngx_uint_t do_write);
+static void ngx_stream_proxy_next_upstream(ngx_stream_session_t *s);
+static void ngx_stream_proxy_finalize(ngx_stream_session_t *s, ngx_int_t rc);
+static u_char *ngx_stream_proxy_log_error(ngx_log_t *log, u_char *buf,
+ size_t len);
+
+static void *ngx_stream_proxy_create_srv_conf(ngx_conf_t *cf);
+static char *ngx_stream_proxy_merge_srv_conf(ngx_conf_t *cf, void *parent,
+ void *child);
+static char *ngx_stream_proxy_pass(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
+#if (NGX_STREAM_SSL)
+
+static char *ngx_stream_proxy_ssl_password_file(ngx_conf_t *cf,
+ ngx_command_t *cmd, void *conf);
+static void ngx_stream_proxy_ssl_init_connection(ngx_stream_session_t *s);
+static void ngx_stream_proxy_ssl_handshake(ngx_connection_t *pc);
+static ngx_int_t ngx_stream_proxy_ssl_name(ngx_stream_session_t *s);
+static ngx_int_t ngx_stream_proxy_set_ssl(ngx_conf_t *cf,
+ ngx_stream_proxy_srv_conf_t *pscf);
+
+
+static ngx_conf_bitmask_t ngx_stream_proxy_ssl_protocols[] = {
+ { ngx_string("SSLv2"), NGX_SSL_SSLv2 },
+ { ngx_string("SSLv3"), NGX_SSL_SSLv3 },
+ { ngx_string("TLSv1"), NGX_SSL_TLSv1 },
+ { ngx_string("TLSv1.1"), NGX_SSL_TLSv1_1 },
+ { ngx_string("TLSv1.2"), NGX_SSL_TLSv1_2 },
+ { ngx_null_string, 0 }
+};
+
+#endif
+
+
+static ngx_command_t ngx_stream_proxy_commands[] = {
+
+ { ngx_string("proxy_pass"),
+ NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_stream_proxy_pass,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("proxy_connect_timeout"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ offsetof(ngx_stream_proxy_srv_conf_t, connect_timeout),
+ NULL },
+
+ { ngx_string("proxy_timeout"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ offsetof(ngx_stream_proxy_srv_conf_t, timeout),
+ NULL },
+
+ { ngx_string("proxy_downstream_buffer"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ offsetof(ngx_stream_proxy_srv_conf_t, downstream_buf_size),
+ NULL },
+
+ { ngx_string("proxy_upstream_buffer"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ offsetof(ngx_stream_proxy_srv_conf_t, upstream_buf_size),
+ NULL },
+
+ { ngx_string("proxy_next_upstream"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ offsetof(ngx_stream_proxy_srv_conf_t, next_upstream),
+ NULL },
+
+ { ngx_string("proxy_next_upstream_tries"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ offsetof(ngx_stream_proxy_srv_conf_t, next_upstream_tries),
+ NULL },
+
+ { ngx_string("proxy_next_upstream_timeout"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ offsetof(ngx_stream_proxy_srv_conf_t, next_upstream_timeout),
+ NULL },
+
+#if (NGX_STREAM_SSL)
+
+ { ngx_string("proxy_ssl"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ offsetof(ngx_stream_proxy_srv_conf_t, ssl_enable),
+ NULL },
+
+ { ngx_string("proxy_ssl_session_reuse"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ offsetof(ngx_stream_proxy_srv_conf_t, ssl_session_reuse),
+ NULL },
+
+ { ngx_string("proxy_ssl_protocols"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_1MORE,
+ ngx_conf_set_bitmask_slot,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ offsetof(ngx_stream_proxy_srv_conf_t, ssl_protocols),
+ &ngx_stream_proxy_ssl_protocols },
+
+ { ngx_string("proxy_ssl_ciphers"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ offsetof(ngx_stream_proxy_srv_conf_t, ssl_ciphers),
+ NULL },
+
+ { ngx_string("proxy_ssl_name"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ offsetof(ngx_stream_proxy_srv_conf_t, ssl_name),
+ NULL },
+
+ { ngx_string("proxy_ssl_server_name"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ offsetof(ngx_stream_proxy_srv_conf_t, ssl_server_name),
+ NULL },
+
+ { ngx_string("proxy_ssl_verify"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ offsetof(ngx_stream_proxy_srv_conf_t, ssl_verify),
+ NULL },
+
+ { ngx_string("proxy_ssl_verify_depth"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ offsetof(ngx_stream_proxy_srv_conf_t, ssl_verify_depth),
+ NULL },
+
+ { ngx_string("proxy_ssl_trusted_certificate"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ offsetof(ngx_stream_proxy_srv_conf_t, ssl_trusted_certificate),
+ NULL },
+
+ { ngx_string("proxy_ssl_crl"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ offsetof(ngx_stream_proxy_srv_conf_t, ssl_crl),
+ NULL },
+
+ { ngx_string("proxy_ssl_certificate"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ offsetof(ngx_stream_proxy_srv_conf_t, ssl_certificate),
+ NULL },
+
+ { ngx_string("proxy_ssl_certificate_key"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ offsetof(ngx_stream_proxy_srv_conf_t, ssl_certificate_key),
+ NULL },
+
+ { ngx_string("proxy_ssl_password_file"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_stream_proxy_ssl_password_file,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ 0,
+ NULL },
+
+#endif
+
+ ngx_null_command
+};
+
+
+static ngx_stream_module_t ngx_stream_proxy_module_ctx = {
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ ngx_stream_proxy_create_srv_conf, /* create server configuration */
+ ngx_stream_proxy_merge_srv_conf /* merge server configuration */
+};
+
+
+ngx_module_t ngx_stream_proxy_module = {
+ NGX_MODULE_V1,
+ &ngx_stream_proxy_module_ctx, /* module context */
+ ngx_stream_proxy_commands, /* module directives */
+ NGX_STREAM_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 void
+ngx_stream_proxy_handler(ngx_stream_session_t *s)
+{
+ u_char *p;
+ ngx_connection_t *c;
+ ngx_stream_upstream_t *u;
+ ngx_stream_proxy_srv_conf_t *pscf;
+ ngx_stream_upstream_srv_conf_t *uscf;
+
+ c = s->connection;
+
+ pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module);
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0,
+ "proxy connection handler");
+
+ u = ngx_pcalloc(c->pool, sizeof(ngx_stream_upstream_t));
+ if (u == NULL) {
+ ngx_stream_proxy_finalize(s, NGX_ERROR);
+ return;
+ }
+
+ s->upstream = u;
+
+ s->log_handler = ngx_stream_proxy_log_error;
+
+ u->peer.log = c->log;
+ u->peer.log_error = NGX_ERROR_ERR;
+
+ uscf = pscf->upstream;
+
+ if (uscf->peer.init(s, uscf) != NGX_OK) {
+ ngx_stream_proxy_finalize(s, NGX_ERROR);
+ return;
+ }
+
+ u->peer.start_time = ngx_current_msec;
+
+ if (pscf->next_upstream_tries
+ && u->peer.tries > pscf->next_upstream_tries)
+ {
+ u->peer.tries = pscf->next_upstream_tries;
+ }
+
+ p = ngx_pnalloc(c->pool, pscf->downstream_buf_size);
+ if (p == NULL) {
+ ngx_stream_proxy_finalize(s, NGX_ERROR);
+ return;
+ }
+
+ u->downstream_buf.start = p;
+ u->downstream_buf.end = p + pscf->downstream_buf_size;
+ u->downstream_buf.pos = p;
+ u->downstream_buf.last = p;
+
+ c->write->handler = ngx_stream_proxy_downstream_handler;
+ c->read->handler = ngx_stream_proxy_downstream_handler;
+
+ if (ngx_stream_proxy_process(s, 0, 0) != NGX_OK) {
+ return;
+ }
+
+ ngx_stream_proxy_connect(s);
+}
+
+
+static void
+ngx_stream_proxy_connect(ngx_stream_session_t *s)
+{
+ ngx_int_t rc;
+ ngx_connection_t *c, *pc;
+ ngx_stream_upstream_t *u;
+ ngx_stream_proxy_srv_conf_t *pscf;
+
+ c = s->connection;
+
+ c->log->action = "connecting to upstream";
+
+ u = s->upstream;
+
+ rc = ngx_event_connect_peer(&u->peer);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, "proxy connect: %i", rc);
+
+ pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module);
+
+ if (rc == NGX_ERROR) {
+ ngx_stream_proxy_finalize(s, NGX_ERROR);
+ return;
+ }
+
+ if (rc == NGX_BUSY) {
+ ngx_log_error(NGX_LOG_ERR, c->log, 0, "no live upstreams");
+ ngx_stream_proxy_finalize(s, NGX_DECLINED);
+ return;
+ }
+
+ if (rc == NGX_DECLINED) {
+ ngx_stream_proxy_next_upstream(s);
+ return;
+ }
+
+ /* rc == NGX_OK || rc == NGX_AGAIN || rc == NGX_DONE */
+
+ pc = u->peer.connection;
+
+ pc->data = s;
+ pc->log = c->log;
+ pc->pool = c->pool;
+ pc->read->log = c->log;
+ pc->write->log = c->log;
+
+ if (rc != NGX_AGAIN) {
+ ngx_stream_proxy_init_upstream(s);
+ return;
+ }
+
+ pc->read->handler = ngx_stream_proxy_connect_handler;
+ pc->write->handler = ngx_stream_proxy_connect_handler;
+
+ ngx_add_timer(pc->write, pscf->connect_timeout);
+}
+
+
+static void
+ngx_stream_proxy_init_upstream(ngx_stream_session_t *s)
+{
+ u_char *p;
+ ngx_connection_t *c, *pc;
+ ngx_log_handler_pt handler;
+ ngx_stream_upstream_t *u;
+ ngx_stream_proxy_srv_conf_t *pscf;
+
+ pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module);
+
+ u = s->upstream;
+
+ pc = u->peer.connection;
+
+#if (NGX_STREAM_SSL)
+ if (pscf->ssl && pc->ssl == NULL) {
+ ngx_stream_proxy_ssl_init_connection(s);
+ return;
+ }
+#endif
+
+ c = s->connection;
+
+ if (c->log->log_level >= NGX_LOG_INFO) {
+ ngx_str_t s;
+ u_char addr[NGX_SOCKADDR_STRLEN];
+
+ s.len = NGX_SOCKADDR_STRLEN;
+ s.data = addr;
+
+ if (ngx_connection_local_sockaddr(pc, &s, 1) == NGX_OK) {
+ handler = c->log->handler;
+ c->log->handler = NULL;
+
+ ngx_log_error(NGX_LOG_INFO, c->log, 0, "proxy %V connected to %V",
+ &s, u->peer.name);
+
+ c->log->handler = handler;
+ }
+ }
+
+ c->log->action = "proxying connection";
+
+ p = ngx_pnalloc(c->pool, pscf->upstream_buf_size);
+ if (p == NULL) {
+ ngx_stream_proxy_finalize(s, NGX_ERROR);
+ return;
+ }
+
+ u->upstream_buf.start = p;
+ u->upstream_buf.end = p + pscf->upstream_buf_size;
+ u->upstream_buf.pos = p;
+ u->upstream_buf.last = p;
+
+ pc->read->handler = ngx_stream_proxy_upstream_handler;
+ pc->write->handler = ngx_stream_proxy_upstream_handler;
+
+ if (ngx_stream_proxy_process(s, 1, 0) != NGX_OK) {
+ return;
+ }
+
+ ngx_stream_proxy_process(s, 0, 1);
+}
+
+
+#if (NGX_STREAM_SSL)
+
+static char *
+ngx_stream_proxy_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf)
+{
+ ngx_stream_proxy_srv_conf_t *pscf = conf;
+
+ ngx_str_t *value;
+
+ if (pscf->ssl_passwords != NGX_CONF_UNSET_PTR) {
+ return "is duplicate";
+ }
+
+ value = cf->args->elts;
+
+ pscf->ssl_passwords = ngx_ssl_read_password_file(cf, &value[1]);
+
+ if (pscf->ssl_passwords == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static void
+ngx_stream_proxy_ssl_init_connection(ngx_stream_session_t *s)
+{
+ ngx_int_t rc;
+ ngx_connection_t *pc;
+ ngx_stream_upstream_t *u;
+ ngx_stream_proxy_srv_conf_t *pscf;
+
+ u = s->upstream;
+
+ pc = u->peer.connection;
+
+ pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module);
+
+ if (ngx_ssl_create_connection(pscf->ssl, pc, NGX_SSL_BUFFER|NGX_SSL_CLIENT)
+ != NGX_OK)
+ {
+ ngx_stream_proxy_finalize(s, NGX_ERROR);
+ return;
+ }
+
+ if (pscf->ssl_server_name || pscf->ssl_verify) {
+ if (ngx_stream_proxy_ssl_name(s) != NGX_OK) {
+ ngx_stream_proxy_finalize(s, NGX_ERROR);
+ return;
+ }
+ }
+
+ if (pscf->ssl_session_reuse) {
+ if (u->peer.set_session(&u->peer, u->peer.data) != NGX_OK) {
+ ngx_stream_proxy_finalize(s, NGX_ERROR);
+ return;
+ }
+ }
+
+ s->connection->log->action = "SSL handshaking to upstream";
+
+ rc = ngx_ssl_handshake(pc);
+
+ if (rc == NGX_AGAIN) {
+
+ if (!pc->write->timer_set) {
+ ngx_add_timer(pc->write, pscf->connect_timeout);
+ }
+
+ pc->ssl->handler = ngx_stream_proxy_ssl_handshake;
+ return;
+ }
+
+ ngx_stream_proxy_ssl_handshake(pc);
+}
+
+
+static void
+ngx_stream_proxy_ssl_handshake(ngx_connection_t *pc)
+{
+ long rc;
+ ngx_stream_session_t *s;
+ ngx_stream_upstream_t *u;
+ ngx_stream_proxy_srv_conf_t *pscf;
+
+ s = pc->data;
+
+ pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module);
+
+ if (pc->ssl->handshaked) {
+
+ if (pscf->ssl_verify) {
+ rc = SSL_get_verify_result(pc->ssl->connection);
+
+ if (rc != X509_V_OK) {
+ ngx_log_error(NGX_LOG_ERR, pc->log, 0,
+ "upstream SSL certificate verify error: (%l:%s)",
+ rc, X509_verify_cert_error_string(rc));
+ goto failed;
+ }
+
+ u = s->upstream;
+
+ if (ngx_ssl_check_host(pc, &u->ssl_name) != NGX_OK) {
+ ngx_log_error(NGX_LOG_ERR, pc->log, 0,
+ "upstream SSL certificate does not match \"%V\"",
+ &u->ssl_name);
+ goto failed;
+ }
+ }
+
+ if (pscf->ssl_session_reuse) {
+ u = s->upstream;
+ u->peer.save_session(&u->peer, u->peer.data);
+ }
+
+ ngx_stream_proxy_init_upstream(s);
+
+ return;
+ }
+
+failed:
+
+ ngx_stream_proxy_next_upstream(s);
+}
+
+
+static ngx_int_t
+ngx_stream_proxy_ssl_name(ngx_stream_session_t *s)
+{
+ u_char *p, *last;
+ ngx_str_t name;
+ ngx_stream_upstream_t *u;
+ ngx_stream_proxy_srv_conf_t *pscf;
+
+ pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module);
+
+ u = s->upstream;
+
+ name = pscf->ssl_name;
+
+ if (name.len == 0) {
+ name = pscf->upstream->host;
+ }
+
+ if (name.len == 0) {
+ goto done;
+ }
+
+ /*
+ * ssl name here may contain port, strip it for compatibility
+ * with the http module
+ */
+
+ p = name.data;
+ last = name.data + name.len;
+
+ if (*p == '[') {
+ p = ngx_strlchr(p, last, ']');
+
+ if (p == NULL) {
+ p = name.data;
+ }
+ }
+
+ p = ngx_strlchr(p, last, ':');
+
+ if (p != NULL) {
+ name.len = p - name.data;
+ }
+
+ if (!pscf->ssl_server_name) {
+ goto done;
+ }
+
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+
+ /* as per RFC 6066, literal IPv4 and IPv6 addresses are not permitted */
+
+ if (name.len == 0 || *name.data == '[') {
+ goto done;
+ }
+
+ if (ngx_inet_addr(name.data, name.len) != INADDR_NONE) {
+ goto done;
+ }
+
+ /*
+ * SSL_set_tlsext_host_name() needs a null-terminated string,
+ * hence we explicitly null-terminate name here
+ */
+
+ p = ngx_pnalloc(s->connection->pool, name.len + 1);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ (void) ngx_cpystrn(p, name.data, name.len + 1);
+
+ name.data = p;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
+ "upstream SSL server name: \"%s\"", name.data);
+
+ if (SSL_set_tlsext_host_name(u->peer.connection->ssl->connection, name.data)
+ == 0)
+ {
+ ngx_ssl_error(NGX_LOG_ERR, s->connection->log, 0,
+ "SSL_set_tlsext_host_name(\"%s\") failed", name.data);
+ return NGX_ERROR;
+ }
+
+#endif
+
+done:
+
+ u->ssl_name = name;
+
+ return NGX_OK;
+}
+
+#endif
+
+
+static void
+ngx_stream_proxy_downstream_handler(ngx_event_t *ev)
+{
+ ngx_connection_t *c;
+ ngx_stream_session_t *s;
+ ngx_stream_upstream_t *u;
+
+ c = ev->data;
+ s = c->data;
+
+ if (ev->timedout) {
+ ngx_connection_error(c, NGX_ETIMEDOUT, "connection timed out");
+ ngx_stream_proxy_finalize(s, NGX_DECLINED);
+ return;
+ }
+
+ u = s->upstream;
+
+ if (!ev->write) {
+ ngx_stream_proxy_process(s, 0, 0);
+
+ } else if (u->upstream_buf.start) {
+ ngx_stream_proxy_process(s, 1, 1);
+ }
+}
+
+
+static void
+ngx_stream_proxy_upstream_handler(ngx_event_t *ev)
+{
+ ngx_connection_t *c;
+ ngx_stream_session_t *s;
+ ngx_stream_upstream_t *u;
+
+ c = ev->data;
+ s = c->data;
+
+ u = s->upstream;
+
+ if (ev->write) {
+ ngx_stream_proxy_process(s, 0, 1);
+
+ } else if (u->upstream_buf.start) {
+ ngx_stream_proxy_process(s, 1, 0);
+ }
+}
+
+
+static void
+ngx_stream_proxy_connect_handler(ngx_event_t *ev)
+{
+ ngx_connection_t *c;
+ ngx_stream_session_t *s;
+
+ c = ev->data;
+ s = c->data;
+
+ if (ev->timedout) {
+ ngx_log_error(NGX_LOG_ERR, c->log, NGX_ETIMEDOUT, "upstream timed out");
+ ngx_stream_proxy_next_upstream(s);
+ return;
+ }
+
+ ngx_del_timer(c->write);
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0,
+ "stream proxy connect upstream");
+
+ if (ngx_stream_proxy_test_connect(c) != NGX_OK) {
+ ngx_stream_proxy_next_upstream(s);
+ return;
+ }
+
+ ngx_stream_proxy_init_upstream(s);
+}
+
+
+static ngx_int_t
+ngx_stream_proxy_test_connect(ngx_connection_t *c)
+{
+ int err;
+ socklen_t len;
+
+#if (NGX_HAVE_KQUEUE)
+
+ if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
+ err = c->write->kq_errno ? c->write->kq_errno : c->read->kq_errno;
+
+ if (err) {
+ (void) ngx_connection_error(c, err,
+ "kevent() reported that connect() failed");
+ return NGX_ERROR;
+ }
+
+ } else
+#endif
+ {
+ err = 0;
+ len = sizeof(int);
+
+ /*
+ * BSDs and Linux return 0 and set a pending error in err
+ * Solaris returns -1 and sets errno
+ */
+
+ if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, (void *) &err, &len)
+ == -1)
+ {
+ err = ngx_socket_errno;
+ }
+
+ if (err) {
+ (void) ngx_connection_error(c, err, "connect() failed");
+ return NGX_ERROR;
+ }
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_stream_proxy_process(ngx_stream_session_t *s, ngx_uint_t from_upstream,
+ ngx_uint_t do_write)
+{
+ size_t size;
+ ssize_t n;
+ ngx_buf_t *b;
+ ngx_uint_t flags;
+ ngx_connection_t *c, *pc, *src, *dst;
+ ngx_log_handler_pt handler;
+ ngx_stream_upstream_t *u;
+ ngx_stream_proxy_srv_conf_t *pscf;
+
+ u = s->upstream;
+
+ c = s->connection;
+ pc = u->upstream_buf.start ? u->peer.connection : NULL;
+
+ if (from_upstream) {
+ src = pc;
+ dst = c;
+ b = &u->upstream_buf;
+
+ } else {
+ src = c;
+ dst = pc;
+ b = &u->downstream_buf;
+ }
+
+ for ( ;; ) {
+
+ if (do_write) {
+
+ size = b->last - b->pos;
+
+ if (size && dst && dst->write->ready) {
+
+ n = dst->send(dst, b->pos, size);
+
+ if (n == NGX_ERROR) {
+ ngx_stream_proxy_finalize(s, NGX_DECLINED);
+ return NGX_ERROR;
+ }
+
+ if (n > 0) {
+ b->pos += n;
+
+ if (b->pos == b->last) {
+ b->pos = b->start;
+ b->last = b->start;
+ }
+ }
+ }
+ }
+
+ size = b->end - b->last;
+
+ if (size && src->read->ready) {
+
+ n = src->recv(src, b->last, size);
+
+ if (n == NGX_AGAIN || n == 0) {
+ break;
+ }
+
+ if (n > 0) {
+ if (from_upstream) {
+ u->received += n;
+
+ } else {
+ s->received += n;
+ }
+
+ do_write = 1;
+ b->last += n;
+ continue;
+ }
+
+ if (n == NGX_ERROR) {
+ src->read->eof = 1;
+ }
+ }
+
+ break;
+ }
+
+ pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module);
+
+ if (src->read->eof && (b->pos == b->last || (dst && dst->read->eof))) {
+ handler = c->log->handler;
+ c->log->handler = NULL;
+
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "%s disconnected"
+ ", bytes from/to client:%O/%O"
+ ", bytes from/to upstream:%O/%O",
+ from_upstream ? "upstream" : "client",
+ s->received, c->sent, u->received, pc ? pc->sent : 0);
+
+ c->log->handler = handler;
+
+ ngx_stream_proxy_finalize(s, NGX_OK);
+ return NGX_DONE;
+ }
+
+ flags = src->read->eof ? NGX_CLOSE_EVENT : 0;
+
+ if (ngx_handle_read_event(src->read, flags) != NGX_OK) {
+ ngx_stream_proxy_finalize(s, NGX_ERROR);
+ return NGX_ERROR;
+ }
+
+ if (dst) {
+ if (ngx_handle_write_event(dst->write, 0) != NGX_OK) {
+ ngx_stream_proxy_finalize(s, NGX_ERROR);
+ return NGX_ERROR;
+ }
+
+ ngx_add_timer(c->read, pscf->timeout);
+ }
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_stream_proxy_next_upstream(ngx_stream_session_t *s)
+{
+ ngx_msec_t timeout;
+ ngx_connection_t *pc;
+ ngx_stream_upstream_t *u;
+ ngx_stream_proxy_srv_conf_t *pscf;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
+ "stream proxy next upstream");
+
+ u = s->upstream;
+
+ if (u->peer.sockaddr) {
+ u->peer.free(&u->peer, u->peer.data, NGX_PEER_FAILED);
+ u->peer.sockaddr = NULL;
+ }
+
+ pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module);
+
+ timeout = pscf->next_upstream_timeout;
+
+ if (u->peer.tries == 0
+ || !pscf->next_upstream
+ || (timeout && ngx_current_msec - u->peer.start_time >= timeout))
+ {
+ ngx_stream_proxy_finalize(s, NGX_DECLINED);
+ return;
+ }
+
+ pc = u->peer.connection;
+
+ if (pc) {
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
+ "close proxy upstream connection: %d", pc->fd);
+
+#if (NGX_STREAM_SSL)
+ if (pc->ssl) {
+ pc->ssl->no_wait_shutdown = 1;
+ pc->ssl->no_send_shutdown = 1;
+
+ (void) ngx_ssl_shutdown(pc);
+ }
+#endif
+
+ ngx_close_connection(pc);
+ u->peer.connection = NULL;
+ }
+
+ ngx_stream_proxy_connect(s);
+}
+
+
+static void
+ngx_stream_proxy_finalize(ngx_stream_session_t *s, ngx_int_t rc)
+{
+ ngx_connection_t *pc;
+ ngx_stream_upstream_t *u;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
+ "finalize stream proxy: %i", rc);
+
+ u = s->upstream;
+
+ if (u == NULL) {
+ goto noupstream;
+ }
+
+ if (u->peer.free && u->peer.sockaddr) {
+ u->peer.free(&u->peer, u->peer.data, 0);
+ u->peer.sockaddr = NULL;
+ }
+
+ pc = u->peer.connection;
+
+ if (pc) {
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
+ "close stream proxy upstream connection: %d", pc->fd);
+
+#if (NGX_STREAM_SSL)
+ if (pc->ssl) {
+ pc->ssl->no_wait_shutdown = 1;
+ (void) ngx_ssl_shutdown(pc);
+ }
+#endif
+
+ ngx_close_connection(pc);
+ u->peer.connection = NULL;
+ }
+
+noupstream:
+
+ ngx_stream_close_connection(s->connection);
+}
+
+
+static u_char *
+ngx_stream_proxy_log_error(ngx_log_t *log, u_char *buf, size_t len)
+{
+ u_char *p;
+ ngx_connection_t *pc;
+ ngx_stream_session_t *s;
+ ngx_stream_upstream_t *u;
+
+ s = log->data;
+
+ u = s->upstream;
+
+ p = buf;
+
+ if (u->peer.name) {
+ p = ngx_snprintf(p, len, ", upstream: \"%V\"", u->peer.name);
+ len -= p - buf;
+ }
+
+ pc = u->peer.connection;
+
+ p = ngx_snprintf(p, len,
+ ", bytes from/to client:%O/%O"
+ ", bytes from/to upstream:%O/%O",
+ s->received, s->connection->sent,
+ u->received, pc ? pc->sent : 0);
+
+ return p;
+}
+
+
+static void *
+ngx_stream_proxy_create_srv_conf(ngx_conf_t *cf)
+{
+ ngx_stream_proxy_srv_conf_t *conf;
+
+ conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_proxy_srv_conf_t));
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ /*
+ * set by ngx_pcalloc():
+ *
+ * conf->ssl_protocols = 0;
+ * conf->ssl_ciphers = { 0, NULL };
+ * conf->ssl_name = { 0, NULL };
+ * conf->ssl_trusted_certificate = { 0, NULL };
+ * conf->ssl_crl = { 0, NULL };
+ * conf->ssl_certificate = { 0, NULL };
+ * conf->ssl_certificate_key = { 0, NULL };
+ *
+ * conf->ssl = NULL;
+ * conf->upstream = NULL;
+ */
+
+ conf->connect_timeout = NGX_CONF_UNSET_MSEC;
+ conf->timeout = NGX_CONF_UNSET_MSEC;
+ conf->next_upstream_timeout = NGX_CONF_UNSET_MSEC;
+ conf->downstream_buf_size = NGX_CONF_UNSET_SIZE;
+ conf->upstream_buf_size = NGX_CONF_UNSET_SIZE;
+ conf->next_upstream_tries = NGX_CONF_UNSET_UINT;
+ conf->next_upstream = NGX_CONF_UNSET;
+
+#if (NGX_STREAM_SSL)
+ conf->ssl_enable = NGX_CONF_UNSET;
+ conf->ssl_session_reuse = NGX_CONF_UNSET;
+ conf->ssl_server_name = NGX_CONF_UNSET;
+ conf->ssl_verify = NGX_CONF_UNSET;
+ conf->ssl_verify_depth = NGX_CONF_UNSET_UINT;
+ conf->ssl_passwords = NGX_CONF_UNSET_PTR;
+#endif
+
+ return conf;
+}
+
+
+static char *
+ngx_stream_proxy_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_stream_proxy_srv_conf_t *prev = parent;
+ ngx_stream_proxy_srv_conf_t *conf = child;
+
+ ngx_conf_merge_msec_value(conf->connect_timeout,
+ prev->connect_timeout, 60000);
+
+ ngx_conf_merge_msec_value(conf->timeout,
+ prev->timeout, 10 * 60000);
+
+ ngx_conf_merge_msec_value(conf->next_upstream_timeout,
+ prev->next_upstream_timeout, 0);
+
+ ngx_conf_merge_size_value(conf->downstream_buf_size,
+ prev->downstream_buf_size, 16384);
+
+ ngx_conf_merge_size_value(conf->upstream_buf_size,
+ prev->upstream_buf_size, 16384);
+
+ ngx_conf_merge_uint_value(conf->next_upstream_tries,
+ prev->next_upstream_tries, 0);
+
+ ngx_conf_merge_value(conf->next_upstream, prev->next_upstream, 1);
+
+#if (NGX_STREAM_SSL)
+
+ ngx_conf_merge_value(conf->ssl_enable, prev->ssl_enable, 0);
+
+ ngx_conf_merge_value(conf->ssl_session_reuse,
+ prev->ssl_session_reuse, 1);
+
+ ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols,
+ (NGX_CONF_BITMASK_SET|NGX_SSL_SSLv3
+ |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1
+ |NGX_SSL_TLSv1_2));
+
+ ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers, "DEFAULT");
+
+ ngx_conf_merge_str_value(conf->ssl_name, prev->ssl_name, "");
+
+ ngx_conf_merge_value(conf->ssl_server_name, prev->ssl_server_name, 0);
+
+ ngx_conf_merge_value(conf->ssl_verify, prev->ssl_verify, 0);
+
+ ngx_conf_merge_uint_value(conf->ssl_verify_depth,
+ prev->ssl_verify_depth, 1);
+
+ ngx_conf_merge_str_value(conf->ssl_trusted_certificate,
+ prev->ssl_trusted_certificate, "");
+
+ ngx_conf_merge_str_value(conf->ssl_crl, prev->ssl_crl, "");
+
+ ngx_conf_merge_str_value(conf->ssl_certificate,
+ prev->ssl_certificate, "");
+
+ ngx_conf_merge_str_value(conf->ssl_certificate_key,
+ prev->ssl_certificate_key, "");
+
+ ngx_conf_merge_ptr_value(conf->ssl_passwords, prev->ssl_passwords, NULL);
+
+ if (conf->ssl_enable && ngx_stream_proxy_set_ssl(cf, conf) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+#endif
+
+ return NGX_CONF_OK;
+}
+
+
+#if (NGX_STREAM_SSL)
+
+static ngx_int_t
+ngx_stream_proxy_set_ssl(ngx_conf_t *cf, ngx_stream_proxy_srv_conf_t *pscf)
+{
+ ngx_pool_cleanup_t *cln;
+
+ pscf->ssl = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_t));
+ if (pscf->ssl == NULL) {
+ return NGX_ERROR;
+ }
+
+ pscf->ssl->log = cf->log;
+
+ if (ngx_ssl_create(pscf->ssl, pscf->ssl_protocols, NULL) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ cln = ngx_pool_cleanup_add(cf->pool, 0);
+ if (cln == NULL) {
+ return NGX_ERROR;
+ }
+
+ cln->handler = ngx_ssl_cleanup_ctx;
+ cln->data = pscf->ssl;
+
+ if (pscf->ssl_certificate.len) {
+
+ if (pscf->ssl_certificate_key.len == 0) {
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "no \"proxy_ssl_certificate_key\" is defined "
+ "for certificate \"%V\"", &pscf->ssl_certificate);
+ return NGX_ERROR;
+ }
+
+ if (ngx_ssl_certificate(cf, pscf->ssl, &pscf->ssl_certificate,
+ &pscf->ssl_certificate_key, pscf->ssl_passwords)
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+ }
+
+ if (SSL_CTX_set_cipher_list(pscf->ssl->ctx,
+ (const char *) pscf->ssl_ciphers.data)
+ == 0)
+ {
+ ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0,
+ "SSL_CTX_set_cipher_list(\"%V\") failed",
+ &pscf->ssl_ciphers);
+ return NGX_ERROR;
+ }
+
+ if (pscf->ssl_verify) {
+ if (pscf->ssl_trusted_certificate.len == 0) {
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "no proxy_ssl_trusted_certificate for proxy_ssl_verify");
+ return NGX_ERROR;
+ }
+
+ if (ngx_ssl_trusted_certificate(cf, pscf->ssl,
+ &pscf->ssl_trusted_certificate,
+ pscf->ssl_verify_depth)
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ if (ngx_ssl_crl(cf, pscf->ssl, &pscf->ssl_crl) != NGX_OK) {
+ return NGX_ERROR;
+ }
+ }
+
+ return NGX_OK;
+}
+
+#endif
+
+
+static char *
+ngx_stream_proxy_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_stream_proxy_srv_conf_t *pscf = conf;
+
+ ngx_url_t u;
+ ngx_str_t *value, *url;
+ ngx_stream_core_srv_conf_t *cscf;
+
+ if (pscf->upstream) {
+ return "is duplicate";
+ }
+
+ cscf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_core_module);
+
+ cscf->handler = ngx_stream_proxy_handler;
+
+ value = cf->args->elts;
+
+ url = &value[1];
+
+ ngx_memzero(&u, sizeof(ngx_url_t));
+
+ u.url = *url;
+ u.no_resolve = 1;
+
+ pscf->upstream = ngx_stream_upstream_add(cf, &u, 0);
+ if (pscf->upstream == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
diff --git a/src/stream/ngx_stream_ssl_module.c b/src/stream/ngx_stream_ssl_module.c
new file mode 100644
index 000000000..ecdd14c56
--- /dev/null
+++ b/src/stream/ngx_stream_ssl_module.c
@@ -0,0 +1,456 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_stream.h>
+
+
+#define NGX_DEFAULT_CIPHERS "HIGH:!aNULL:!MD5"
+#define NGX_DEFAULT_ECDH_CURVE "prime256v1"
+
+
+static void *ngx_stream_ssl_create_conf(ngx_conf_t *cf);
+static char *ngx_stream_ssl_merge_conf(ngx_conf_t *cf, void *parent,
+ void *child);
+
+static char *ngx_stream_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_stream_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
+
+static ngx_conf_bitmask_t ngx_stream_ssl_protocols[] = {
+ { ngx_string("SSLv2"), NGX_SSL_SSLv2 },
+ { ngx_string("SSLv3"), NGX_SSL_SSLv3 },
+ { ngx_string("TLSv1"), NGX_SSL_TLSv1 },
+ { ngx_string("TLSv1.1"), NGX_SSL_TLSv1_1 },
+ { ngx_string("TLSv1.2"), NGX_SSL_TLSv1_2 },
+ { ngx_null_string, 0 }
+};
+
+
+static ngx_command_t ngx_stream_ssl_commands[] = {
+
+ { ngx_string("ssl_handshake_timeout"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ offsetof(ngx_stream_ssl_conf_t, handshake_timeout),
+ NULL },
+
+ { ngx_string("ssl_certificate"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ offsetof(ngx_stream_ssl_conf_t, certificate),
+ NULL },
+
+ { ngx_string("ssl_certificate_key"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ offsetof(ngx_stream_ssl_conf_t, certificate_key),
+ NULL },
+
+ { ngx_string("ssl_password_file"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_stream_ssl_password_file,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("ssl_dhparam"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ offsetof(ngx_stream_ssl_conf_t, dhparam),
+ NULL },
+
+ { ngx_string("ssl_ecdh_curve"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ offsetof(ngx_stream_ssl_conf_t, ecdh_curve),
+ NULL },
+
+ { ngx_string("ssl_protocols"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_1MORE,
+ ngx_conf_set_bitmask_slot,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ offsetof(ngx_stream_ssl_conf_t, protocols),
+ &ngx_stream_ssl_protocols },
+
+ { ngx_string("ssl_ciphers"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ offsetof(ngx_stream_ssl_conf_t, ciphers),
+ NULL },
+
+ { ngx_string("ssl_prefer_server_ciphers"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ offsetof(ngx_stream_ssl_conf_t, prefer_server_ciphers),
+ NULL },
+
+ { ngx_string("ssl_session_cache"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE12,
+ ngx_stream_ssl_session_cache,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("ssl_session_tickets"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ offsetof(ngx_stream_ssl_conf_t, session_tickets),
+ NULL },
+
+ { ngx_string("ssl_session_ticket_key"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_array_slot,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ offsetof(ngx_stream_ssl_conf_t, session_ticket_keys),
+ NULL },
+
+ { ngx_string("ssl_session_timeout"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_sec_slot,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ offsetof(ngx_stream_ssl_conf_t, session_timeout),
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_stream_module_t ngx_stream_ssl_module_ctx = {
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ ngx_stream_ssl_create_conf, /* create server configuration */
+ ngx_stream_ssl_merge_conf /* merge server configuration */
+};
+
+
+ngx_module_t ngx_stream_ssl_module = {
+ NGX_MODULE_V1,
+ &ngx_stream_ssl_module_ctx, /* module context */
+ ngx_stream_ssl_commands, /* module directives */
+ NGX_STREAM_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_str_t ngx_stream_ssl_sess_id_ctx = ngx_string("STREAM");
+
+
+static void *
+ngx_stream_ssl_create_conf(ngx_conf_t *cf)
+{
+ ngx_stream_ssl_conf_t *scf;
+
+ scf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_ssl_conf_t));
+ if (scf == NULL) {
+ return NULL;
+ }
+
+ /*
+ * set by ngx_pcalloc():
+ *
+ * scf->protocols = 0;
+ * scf->certificate = { 0, NULL };
+ * scf->certificate_key = { 0, NULL };
+ * scf->dhparam = { 0, NULL };
+ * scf->ecdh_curve = { 0, NULL };
+ * scf->ciphers = { 0, NULL };
+ * scf->shm_zone = NULL;
+ */
+
+ scf->handshake_timeout = NGX_CONF_UNSET_MSEC;
+ scf->passwords = NGX_CONF_UNSET_PTR;
+ scf->prefer_server_ciphers = NGX_CONF_UNSET;
+ scf->builtin_session_cache = NGX_CONF_UNSET;
+ scf->session_timeout = NGX_CONF_UNSET;
+ scf->session_tickets = NGX_CONF_UNSET;
+ scf->session_ticket_keys = NGX_CONF_UNSET_PTR;
+
+ return scf;
+}
+
+
+static char *
+ngx_stream_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_stream_ssl_conf_t *prev = parent;
+ ngx_stream_ssl_conf_t *conf = child;
+
+ ngx_pool_cleanup_t *cln;
+
+ ngx_conf_merge_msec_value(conf->handshake_timeout,
+ prev->handshake_timeout, 60000);
+
+ ngx_conf_merge_value(conf->session_timeout,
+ prev->session_timeout, 300);
+
+ ngx_conf_merge_value(conf->prefer_server_ciphers,
+ prev->prefer_server_ciphers, 0);
+
+ ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols,
+ (NGX_CONF_BITMASK_SET|NGX_SSL_SSLv3|NGX_SSL_TLSv1
+ |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2));
+
+ ngx_conf_merge_str_value(conf->certificate, prev->certificate, "");
+ ngx_conf_merge_str_value(conf->certificate_key, prev->certificate_key, "");
+
+ ngx_conf_merge_ptr_value(conf->passwords, prev->passwords, NULL);
+
+ ngx_conf_merge_str_value(conf->dhparam, prev->dhparam, "");
+
+ ngx_conf_merge_str_value(conf->ecdh_curve, prev->ecdh_curve,
+ NGX_DEFAULT_ECDH_CURVE);
+
+ ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFAULT_CIPHERS);
+
+
+ conf->ssl.log = cf->log;
+
+ if (conf->certificate.len == 0) {
+ return NGX_CONF_OK;
+ }
+
+ if (conf->certificate_key.len == 0) {
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "no \"ssl_certificate_key\" is defined "
+ "for certificate \"%V\"",
+ &conf->certificate);
+ return NGX_CONF_ERROR;
+ }
+
+ if (ngx_ssl_create(&conf->ssl, conf->protocols, 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 = &conf->ssl;
+
+ if (ngx_ssl_certificate(cf, &conf->ssl, &conf->certificate,
+ &conf->certificate_key, conf->passwords)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ if (SSL_CTX_set_cipher_list(conf->ssl.ctx,
+ (const char *) conf->ciphers.data)
+ == 0)
+ {
+ ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0,
+ "SSL_CTX_set_cipher_list(\"%V\") failed",
+ &conf->ciphers);
+ return NGX_CONF_ERROR;
+ }
+
+ if (conf->prefer_server_ciphers) {
+ SSL_CTX_set_options(conf->ssl.ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
+ }
+
+ SSL_CTX_set_tmp_rsa_callback(conf->ssl.ctx, ngx_ssl_rsa512_key_callback);
+
+ if (ngx_ssl_dhparam(cf, &conf->ssl, &conf->dhparam) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (ngx_ssl_ecdh_curve(cf, &conf->ssl, &conf->ecdh_curve) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_conf_merge_value(conf->builtin_session_cache,
+ prev->builtin_session_cache, NGX_SSL_NONE_SCACHE);
+
+ if (conf->shm_zone == NULL) {
+ conf->shm_zone = prev->shm_zone;
+ }
+
+ if (ngx_ssl_session_cache(&conf->ssl, &ngx_stream_ssl_sess_id_ctx,
+ conf->builtin_session_cache,
+ conf->shm_zone, conf->session_timeout)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_conf_merge_value(conf->session_tickets,
+ prev->session_tickets, 1);
+
+#ifdef SSL_OP_NO_TICKET
+ if (!conf->session_tickets) {
+ SSL_CTX_set_options(conf->ssl.ctx, SSL_OP_NO_TICKET);
+ }
+#endif
+
+ ngx_conf_merge_ptr_value(conf->session_ticket_keys,
+ prev->session_ticket_keys, NULL);
+
+ if (ngx_ssl_session_ticket_keys(cf, &conf->ssl, conf->session_ticket_keys)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_stream_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_stream_ssl_conf_t *scf = conf;
+
+ ngx_str_t *value;
+
+ if (scf->passwords != NGX_CONF_UNSET_PTR) {
+ return "is duplicate";
+ }
+
+ value = cf->args->elts;
+
+ scf->passwords = ngx_ssl_read_password_file(cf, &value[1]);
+
+ if (scf->passwords == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_stream_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_stream_ssl_conf_t *scf = conf;
+
+ size_t len;
+ ngx_str_t *value, name, size;
+ ngx_int_t n;
+ ngx_uint_t i, j;
+
+ value = cf->args->elts;
+
+ for (i = 1; i < cf->args->nelts; i++) {
+
+ if (ngx_strcmp(value[i].data, "off") == 0) {
+ scf->builtin_session_cache = NGX_SSL_NO_SCACHE;
+ continue;
+ }
+
+ if (ngx_strcmp(value[i].data, "none") == 0) {
+ scf->builtin_session_cache = NGX_SSL_NONE_SCACHE;
+ continue;
+ }
+
+ if (ngx_strcmp(value[i].data, "builtin") == 0) {
+ scf->builtin_session_cache = NGX_SSL_DFLT_BUILTIN_SCACHE;
+ continue;
+ }
+
+ if (value[i].len > sizeof("builtin:") - 1
+ && ngx_strncmp(value[i].data, "builtin:", sizeof("builtin:") - 1)
+ == 0)
+ {
+ n = ngx_atoi(value[i].data + sizeof("builtin:") - 1,
+ value[i].len - (sizeof("builtin:") - 1));
+
+ if (n == NGX_ERROR) {
+ goto invalid;
+ }
+
+ scf->builtin_session_cache = n;
+
+ continue;
+ }
+
+ if (value[i].len > sizeof("shared:") - 1
+ && ngx_strncmp(value[i].data, "shared:", sizeof("shared:") - 1)
+ == 0)
+ {
+ len = 0;
+
+ for (j = sizeof("shared:") - 1; j < value[i].len; j++) {
+ if (value[i].data[j] == ':') {
+ break;
+ }
+
+ len++;
+ }
+
+ if (len == 0) {
+ goto invalid;
+ }
+
+ name.len = len;
+ name.data = value[i].data + sizeof("shared:") - 1;
+
+ size.len = value[i].len - j - 1;
+ size.data = name.data + len + 1;
+
+ n = ngx_parse_size(&size);
+
+ if (n == NGX_ERROR) {
+ goto invalid;
+ }
+
+ if (n < (ngx_int_t) (8 * ngx_pagesize)) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "session cache \"%V\" is too small",
+ &value[i]);
+
+ return NGX_CONF_ERROR;
+ }
+
+ scf->shm_zone = ngx_shared_memory_add(cf, &name, n,
+ &ngx_stream_ssl_module);
+ if (scf->shm_zone == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ scf->shm_zone->init = ngx_ssl_session_cache_init;
+
+ continue;
+ }
+
+ goto invalid;
+ }
+
+ if (scf->shm_zone && scf->builtin_session_cache == NGX_CONF_UNSET) {
+ scf->builtin_session_cache = NGX_SSL_NO_BUILTIN_SCACHE;
+ }
+
+ return NGX_CONF_OK;
+
+invalid:
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid session cache \"%V\"", &value[i]);
+
+ return NGX_CONF_ERROR;
+}
diff --git a/src/stream/ngx_stream_ssl_module.h b/src/stream/ngx_stream_ssl_module.h
new file mode 100644
index 000000000..85e8b6ede
--- /dev/null
+++ b/src/stream/ngx_stream_ssl_module.h
@@ -0,0 +1,49 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_STREAM_SSL_H_INCLUDED_
+#define _NGX_STREAM_SSL_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_stream.h>
+
+
+typedef struct {
+ ngx_msec_t handshake_timeout;
+
+ ngx_flag_t prefer_server_ciphers;
+
+ ngx_ssl_t ssl;
+
+ ngx_uint_t protocols;
+
+ ssize_t builtin_session_cache;
+
+ time_t session_timeout;
+
+ ngx_str_t certificate;
+ ngx_str_t certificate_key;
+ ngx_str_t dhparam;
+ ngx_str_t ecdh_curve;
+
+ ngx_str_t ciphers;
+
+ ngx_array_t *passwords;
+
+ ngx_shm_zone_t *shm_zone;
+
+ ngx_flag_t session_tickets;
+ ngx_array_t *session_ticket_keys;
+} ngx_stream_ssl_conf_t;
+
+
+extern ngx_module_t ngx_stream_ssl_module;
+
+
+#endif /* _NGX_STREAM_SSL_H_INCLUDED_ */
diff --git a/src/stream/ngx_stream_upstream.c b/src/stream/ngx_stream_upstream.c
new file mode 100644
index 000000000..a991f8a9f
--- /dev/null
+++ b/src/stream/ngx_stream_upstream.c
@@ -0,0 +1,462 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_stream.h>
+
+
+static char *ngx_stream_upstream(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *dummy);
+static char *ngx_stream_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static void *ngx_stream_upstream_create_main_conf(ngx_conf_t *cf);
+static char *ngx_stream_upstream_init_main_conf(ngx_conf_t *cf, void *conf);
+
+
+static ngx_command_t ngx_stream_upstream_commands[] = {
+
+ { ngx_string("upstream"),
+ NGX_STREAM_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE1,
+ ngx_stream_upstream,
+ 0,
+ 0,
+ NULL },
+
+ { ngx_string("server"),
+ NGX_STREAM_UPS_CONF|NGX_CONF_1MORE,
+ ngx_stream_upstream_server,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ 0,
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_stream_module_t ngx_stream_upstream_module_ctx = {
+ ngx_stream_upstream_create_main_conf, /* create main configuration */
+ ngx_stream_upstream_init_main_conf, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+};
+
+
+ngx_module_t ngx_stream_upstream_module = {
+ NGX_MODULE_V1,
+ &ngx_stream_upstream_module_ctx, /* module context */
+ ngx_stream_upstream_commands, /* module directives */
+ NGX_STREAM_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_stream_upstream(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy)
+{
+ char *rv;
+ void *mconf;
+ ngx_str_t *value;
+ ngx_url_t u;
+ ngx_uint_t m;
+ ngx_conf_t pcf;
+ ngx_stream_module_t *module;
+ ngx_stream_conf_ctx_t *ctx, *stream_ctx;
+ ngx_stream_upstream_srv_conf_t *uscf;
+
+ ngx_memzero(&u, sizeof(ngx_url_t));
+
+ value = cf->args->elts;
+ u.host = value[1];
+ u.no_resolve = 1;
+ u.no_port = 1;
+
+ uscf = ngx_stream_upstream_add(cf, &u, NGX_STREAM_UPSTREAM_CREATE
+ |NGX_STREAM_UPSTREAM_WEIGHT
+ |NGX_STREAM_UPSTREAM_MAX_FAILS
+ |NGX_STREAM_UPSTREAM_FAIL_TIMEOUT
+ |NGX_STREAM_UPSTREAM_DOWN
+ |NGX_STREAM_UPSTREAM_BACKUP);
+ if (uscf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+
+ ctx = ngx_pcalloc(cf->pool, sizeof(ngx_stream_conf_ctx_t));
+ if (ctx == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ stream_ctx = cf->ctx;
+ ctx->main_conf = stream_ctx->main_conf;
+
+ /* the upstream{}'s srv_conf */
+
+ ctx->srv_conf = ngx_pcalloc(cf->pool,
+ sizeof(void *) * ngx_stream_max_module);
+ if (ctx->srv_conf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ctx->srv_conf[ngx_stream_upstream_module.ctx_index] = uscf;
+
+ uscf->srv_conf = ctx->srv_conf;
+
+ for (m = 0; ngx_modules[m]; m++) {
+ if (ngx_modules[m]->type != NGX_STREAM_MODULE) {
+ continue;
+ }
+
+ module = ngx_modules[m]->ctx;
+
+ if (module->create_srv_conf) {
+ mconf = module->create_srv_conf(cf);
+ if (mconf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ctx->srv_conf[ngx_modules[m]->ctx_index] = mconf;
+ }
+ }
+
+ uscf->servers = ngx_array_create(cf->pool, 4,
+ sizeof(ngx_stream_upstream_server_t));
+ if (uscf->servers == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+
+ /* parse inside upstream{} */
+
+ pcf = *cf;
+ cf->ctx = ctx;
+ cf->cmd_type = NGX_STREAM_UPS_CONF;
+
+ rv = ngx_conf_parse(cf, NULL);
+
+ *cf = pcf;
+
+ if (rv != NGX_CONF_OK) {
+ return rv;
+ }
+
+ if (uscf->servers->nelts == 0) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "no servers are inside upstream");
+ return NGX_CONF_ERROR;
+ }
+
+ return rv;
+}
+
+
+static char *
+ngx_stream_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_stream_upstream_srv_conf_t *uscf = conf;
+
+ time_t fail_timeout;
+ ngx_str_t *value, s;
+ ngx_url_t u;
+ ngx_int_t weight, max_fails;
+ ngx_uint_t i;
+ ngx_stream_upstream_server_t *us;
+
+ us = ngx_array_push(uscf->servers);
+ if (us == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_memzero(us, sizeof(ngx_stream_upstream_server_t));
+
+ value = cf->args->elts;
+
+ weight = 1;
+ max_fails = 1;
+ fail_timeout = 10;
+
+ for (i = 2; i < cf->args->nelts; i++) {
+
+ if (ngx_strncmp(value[i].data, "weight=", 7) == 0) {
+
+ if (!(uscf->flags & NGX_STREAM_UPSTREAM_WEIGHT)) {
+ goto not_supported;
+ }
+
+ weight = ngx_atoi(&value[i].data[7], value[i].len - 7);
+
+ if (weight == NGX_ERROR || weight == 0) {
+ goto invalid;
+ }
+
+ continue;
+ }
+
+ if (ngx_strncmp(value[i].data, "max_fails=", 10) == 0) {
+
+ if (!(uscf->flags & NGX_STREAM_UPSTREAM_MAX_FAILS)) {
+ goto not_supported;
+ }
+
+ max_fails = ngx_atoi(&value[i].data[10], value[i].len - 10);
+
+ if (max_fails == NGX_ERROR) {
+ goto invalid;
+ }
+
+ continue;
+ }
+
+ if (ngx_strncmp(value[i].data, "fail_timeout=", 13) == 0) {
+
+ if (!(uscf->flags & NGX_STREAM_UPSTREAM_FAIL_TIMEOUT)) {
+ goto not_supported;
+ }
+
+ s.len = value[i].len - 13;
+ s.data = &value[i].data[13];
+
+ fail_timeout = ngx_parse_time(&s, 1);
+
+ if (fail_timeout == (time_t) NGX_ERROR) {
+ goto invalid;
+ }
+
+ continue;
+ }
+
+ if (ngx_strcmp(value[i].data, "backup") == 0) {
+
+ if (!(uscf->flags & NGX_STREAM_UPSTREAM_BACKUP)) {
+ goto not_supported;
+ }
+
+ us->backup = 1;
+
+ continue;
+ }
+
+ if (ngx_strcmp(value[i].data, "down") == 0) {
+
+ if (!(uscf->flags & NGX_STREAM_UPSTREAM_DOWN)) {
+ goto not_supported;
+ }
+
+ us->down = 1;
+
+ continue;
+ }
+
+ goto invalid;
+ }
+
+ ngx_memzero(&u, sizeof(ngx_url_t));
+
+ u.url = value[1];
+
+ if (ngx_parse_url(cf->pool, &u) != NGX_OK) {
+ if (u.err) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "%s in upstream \"%V\"", u.err, &u.url);
+ }
+
+ return NGX_CONF_ERROR;
+ }
+
+ if (u.no_port) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "no port in upstream \"%V\"", &u.url);
+ return NGX_CONF_ERROR;
+ }
+
+ us->name = u.url;
+ us->addrs = u.addrs;
+ us->naddrs = u.naddrs;
+ us->weight = weight;
+ us->max_fails = max_fails;
+ us->fail_timeout = fail_timeout;
+
+ return NGX_CONF_OK;
+
+invalid:
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid parameter \"%V\"", &value[i]);
+
+ return NGX_CONF_ERROR;
+
+not_supported:
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "balancing method does not support parameter \"%V\"",
+ &value[i]);
+
+ return NGX_CONF_ERROR;
+}
+
+
+ngx_stream_upstream_srv_conf_t *
+ngx_stream_upstream_add(ngx_conf_t *cf, ngx_url_t *u, ngx_uint_t flags)
+{
+ ngx_uint_t i;
+ ngx_stream_upstream_server_t *us;
+ ngx_stream_upstream_srv_conf_t *uscf, **uscfp;
+ ngx_stream_upstream_main_conf_t *umcf;
+
+ if (!(flags & NGX_STREAM_UPSTREAM_CREATE)) {
+
+ if (ngx_parse_url(cf->pool, u) != NGX_OK) {
+ if (u->err) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "%s in upstream \"%V\"", u->err, &u->url);
+ }
+
+ return NULL;
+ }
+ }
+
+ umcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_upstream_module);
+
+ uscfp = umcf->upstreams.elts;
+
+ for (i = 0; i < umcf->upstreams.nelts; i++) {
+
+ if (uscfp[i]->host.len != u->host.len
+ || ngx_strncasecmp(uscfp[i]->host.data, u->host.data, u->host.len)
+ != 0)
+ {
+ continue;
+ }
+
+ if ((flags & NGX_STREAM_UPSTREAM_CREATE)
+ && (uscfp[i]->flags & NGX_STREAM_UPSTREAM_CREATE))
+ {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "duplicate upstream \"%V\"", &u->host);
+ return NULL;
+ }
+
+ if ((uscfp[i]->flags & NGX_STREAM_UPSTREAM_CREATE) && !u->no_port) {
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "upstream \"%V\" may not have port %d",
+ &u->host, u->port);
+ return NULL;
+ }
+
+ if ((flags & NGX_STREAM_UPSTREAM_CREATE) && !uscfp[i]->no_port) {
+ ngx_log_error(NGX_LOG_WARN, cf->log, 0,
+ "upstream \"%V\" may not have port %d in %s:%ui",
+ &u->host, uscfp[i]->port,
+ uscfp[i]->file_name, uscfp[i]->line);
+ return NULL;
+ }
+
+ if (uscfp[i]->port != u->port) {
+ continue;
+ }
+
+ if (flags & NGX_STREAM_UPSTREAM_CREATE) {
+ uscfp[i]->flags = flags;
+ }
+
+ return uscfp[i];
+ }
+
+ uscf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_upstream_srv_conf_t));
+ if (uscf == NULL) {
+ return NULL;
+ }
+
+ uscf->flags = flags;
+ uscf->host = u->host;
+ uscf->file_name = cf->conf_file->file.name.data;
+ uscf->line = cf->conf_file->line;
+ uscf->port = u->port;
+ uscf->no_port = u->no_port;
+
+ if (u->naddrs == 1) {
+ uscf->servers = ngx_array_create(cf->pool, 1,
+ sizeof(ngx_stream_upstream_server_t));
+ if (uscf->servers == NULL) {
+ return NULL;
+ }
+
+ us = ngx_array_push(uscf->servers);
+ if (us == NULL) {
+ return NULL;
+ }
+
+ ngx_memzero(us, sizeof(ngx_stream_upstream_server_t));
+
+ us->addrs = u->addrs;
+ us->naddrs = 1;
+ }
+
+ uscfp = ngx_array_push(&umcf->upstreams);
+ if (uscfp == NULL) {
+ return NULL;
+ }
+
+ *uscfp = uscf;
+
+ return uscf;
+}
+
+
+static void *
+ngx_stream_upstream_create_main_conf(ngx_conf_t *cf)
+{
+ ngx_stream_upstream_main_conf_t *umcf;
+
+ umcf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_upstream_main_conf_t));
+ if (umcf == NULL) {
+ return NULL;
+ }
+
+ if (ngx_array_init(&umcf->upstreams, cf->pool, 4,
+ sizeof(ngx_stream_upstream_srv_conf_t *))
+ != NGX_OK)
+ {
+ return NULL;
+ }
+
+ return umcf;
+}
+
+
+static char *
+ngx_stream_upstream_init_main_conf(ngx_conf_t *cf, void *conf)
+{
+ ngx_stream_upstream_main_conf_t *umcf = conf;
+
+ ngx_uint_t i;
+ ngx_stream_upstream_init_pt init;
+ ngx_stream_upstream_srv_conf_t **uscfp;
+
+ uscfp = umcf->upstreams.elts;
+
+ for (i = 0; i < umcf->upstreams.nelts; i++) {
+
+ init = uscfp[i]->peer.init_upstream
+ ? uscfp[i]->peer.init_upstream
+ : ngx_stream_upstream_init_round_robin;
+
+ if (init(cf, uscfp[i]) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ return NGX_CONF_OK;
+}
diff --git a/src/stream/ngx_stream_upstream.h b/src/stream/ngx_stream_upstream.h
new file mode 100644
index 000000000..83353edca
--- /dev/null
+++ b/src/stream/ngx_stream_upstream.h
@@ -0,0 +1,103 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_STREAM_UPSTREAM_H_INCLUDED_
+#define _NGX_STREAM_UPSTREAM_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_stream.h>
+#include <ngx_event_connect.h>
+
+
+#define NGX_STREAM_UPSTREAM_CREATE 0x0001
+#define NGX_STREAM_UPSTREAM_WEIGHT 0x0002
+#define NGX_STREAM_UPSTREAM_MAX_FAILS 0x0004
+#define NGX_STREAM_UPSTREAM_FAIL_TIMEOUT 0x0008
+#define NGX_STREAM_UPSTREAM_DOWN 0x0010
+#define NGX_STREAM_UPSTREAM_BACKUP 0x0020
+
+
+typedef struct {
+ ngx_array_t upstreams;
+ /* ngx_stream_upstream_srv_conf_t */
+} ngx_stream_upstream_main_conf_t;
+
+
+typedef struct ngx_stream_upstream_srv_conf_s ngx_stream_upstream_srv_conf_t;
+
+
+typedef ngx_int_t (*ngx_stream_upstream_init_pt)(ngx_conf_t *cf,
+ ngx_stream_upstream_srv_conf_t *us);
+typedef ngx_int_t (*ngx_stream_upstream_init_peer_pt)(ngx_stream_session_t *s,
+ ngx_stream_upstream_srv_conf_t *us);
+
+
+typedef struct {
+ ngx_stream_upstream_init_pt init_upstream;
+ ngx_stream_upstream_init_peer_pt init;
+ void *data;
+} ngx_stream_upstream_peer_t;
+
+
+typedef struct {
+ ngx_str_t name;
+ ngx_addr_t *addrs;
+ ngx_uint_t naddrs;
+ ngx_uint_t weight;
+ ngx_uint_t max_fails;
+ time_t fail_timeout;
+
+ unsigned down:1;
+ unsigned backup:1;
+} ngx_stream_upstream_server_t;
+
+
+struct ngx_stream_upstream_srv_conf_s {
+ ngx_stream_upstream_peer_t peer;
+ void **srv_conf;
+
+ ngx_array_t *servers;
+ /* ngx_stream_upstream_server_t */
+
+ ngx_uint_t flags;
+ ngx_str_t host;
+ u_char *file_name;
+ ngx_uint_t line;
+ in_port_t port;
+ ngx_uint_t no_port; /* unsigned no_port:1 */
+
+#if (NGX_STREAM_UPSTREAM_ZONE)
+ ngx_shm_zone_t *shm_zone;
+#endif
+};
+
+
+typedef struct {
+ ngx_peer_connection_t peer;
+ ngx_buf_t downstream_buf;
+ ngx_buf_t upstream_buf;
+ off_t received;
+#if (NGX_STREAM_SSL)
+ ngx_str_t ssl_name;
+#endif
+} ngx_stream_upstream_t;
+
+
+ngx_stream_upstream_srv_conf_t *ngx_stream_upstream_add(ngx_conf_t *cf,
+ ngx_url_t *u, ngx_uint_t flags);
+
+
+#define ngx_stream_conf_upstream_srv_conf(uscf, module) \
+ uscf->srv_conf[module.ctx_index]
+
+
+extern ngx_module_t ngx_stream_upstream_module;
+
+
+#endif /* _NGX_STREAM_UPSTREAM_H_INCLUDED_ */
diff --git a/src/stream/ngx_stream_upstream_hash_module.c b/src/stream/ngx_stream_upstream_hash_module.c
new file mode 100644
index 000000000..14f50222d
--- /dev/null
+++ b/src/stream/ngx_stream_upstream_hash_module.c
@@ -0,0 +1,643 @@
+
+/*
+ * Copyright (C) Roman Arutyunyan
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_stream.h>
+
+
+typedef struct {
+ uint32_t hash;
+ ngx_str_t *server;
+} ngx_stream_upstream_chash_point_t;
+
+
+typedef struct {
+ ngx_uint_t number;
+ ngx_stream_upstream_chash_point_t point[1];
+} ngx_stream_upstream_chash_points_t;
+
+
+typedef struct {
+ ngx_stream_upstream_chash_points_t *points;
+} ngx_stream_upstream_hash_srv_conf_t;
+
+
+typedef struct {
+ /* the round robin data must be first */
+ ngx_stream_upstream_rr_peer_data_t rrp;
+ ngx_stream_upstream_hash_srv_conf_t *conf;
+ ngx_str_t key;
+ ngx_uint_t tries;
+ ngx_uint_t rehash;
+ uint32_t hash;
+ ngx_event_get_peer_pt get_rr_peer;
+} ngx_stream_upstream_hash_peer_data_t;
+
+
+static ngx_int_t ngx_stream_upstream_init_hash(ngx_conf_t *cf,
+ ngx_stream_upstream_srv_conf_t *us);
+static ngx_int_t ngx_stream_upstream_init_hash_peer(ngx_stream_session_t *s,
+ ngx_stream_upstream_srv_conf_t *us);
+static ngx_int_t ngx_stream_upstream_get_hash_peer(ngx_peer_connection_t *pc,
+ void *data);
+
+static ngx_int_t ngx_stream_upstream_init_chash(ngx_conf_t *cf,
+ ngx_stream_upstream_srv_conf_t *us);
+static int ngx_libc_cdecl
+ ngx_stream_upstream_chash_cmp_points(const void *one, const void *two);
+static ngx_uint_t ngx_stream_upstream_find_chash_point(
+ ngx_stream_upstream_chash_points_t *points, uint32_t hash);
+static ngx_int_t ngx_stream_upstream_init_chash_peer(ngx_stream_session_t *s,
+ ngx_stream_upstream_srv_conf_t *us);
+static ngx_int_t ngx_stream_upstream_get_chash_peer(ngx_peer_connection_t *pc,
+ void *data);
+
+static void *ngx_stream_upstream_hash_create_conf(ngx_conf_t *cf);
+static char *ngx_stream_upstream_hash(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
+
+static ngx_command_t ngx_stream_upstream_hash_commands[] = {
+
+ { ngx_string("hash"),
+ NGX_STREAM_UPS_CONF|NGX_CONF_TAKE12,
+ ngx_stream_upstream_hash,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ 0,
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_stream_module_t ngx_stream_upstream_hash_module_ctx = {
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ ngx_stream_upstream_hash_create_conf, /* create server configuration */
+ NULL, /* merge server configuration */
+};
+
+
+ngx_module_t ngx_stream_upstream_hash_module = {
+ NGX_MODULE_V1,
+ &ngx_stream_upstream_hash_module_ctx, /* module context */
+ ngx_stream_upstream_hash_commands, /* module directives */
+ NGX_STREAM_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_int_t
+ngx_stream_upstream_init_hash(ngx_conf_t *cf,
+ ngx_stream_upstream_srv_conf_t *us)
+{
+ if (ngx_stream_upstream_init_round_robin(cf, us) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ us->peer.init = ngx_stream_upstream_init_hash_peer;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_stream_upstream_init_hash_peer(ngx_stream_session_t *s,
+ ngx_stream_upstream_srv_conf_t *us)
+{
+ ngx_stream_upstream_hash_srv_conf_t *hcf;
+ ngx_stream_upstream_hash_peer_data_t *hp;
+
+ hp = ngx_palloc(s->connection->pool,
+ sizeof(ngx_stream_upstream_hash_peer_data_t));
+ if (hp == NULL) {
+ return NGX_ERROR;
+ }
+
+ s->upstream->peer.data = &hp->rrp;
+
+ if (ngx_stream_upstream_init_round_robin_peer(s, us) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ s->upstream->peer.get = ngx_stream_upstream_get_hash_peer;
+
+ hcf = ngx_stream_conf_upstream_srv_conf(us,
+ ngx_stream_upstream_hash_module);
+
+ hp->key = s->connection->addr_text;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
+ "upstream hash key:\"%V\"", &hp->key);
+
+ hp->conf = hcf;
+ hp->tries = 0;
+ hp->rehash = 0;
+ hp->hash = 0;
+ hp->get_rr_peer = ngx_stream_upstream_get_round_robin_peer;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_stream_upstream_get_hash_peer(ngx_peer_connection_t *pc, void *data)
+{
+ ngx_stream_upstream_hash_peer_data_t *hp = data;
+
+ time_t now;
+ u_char buf[NGX_INT_T_LEN];
+ size_t size;
+ uint32_t hash;
+ ngx_int_t w;
+ uintptr_t m;
+ ngx_uint_t n, p;
+ ngx_stream_upstream_rr_peer_t *peer;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0,
+ "get hash peer, try: %ui", pc->tries);
+
+ ngx_stream_upstream_rr_peers_wlock(hp->rrp.peers);
+
+ if (hp->tries > 20 || hp->rrp.peers->single) {
+ ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers);
+ return hp->get_rr_peer(pc, &hp->rrp);
+ }
+
+ now = ngx_time();
+
+ pc->connection = NULL;
+
+ for ( ;; ) {
+
+ /*
+ * Hash expression is compatible with Cache::Memcached:
+ * ((crc32([REHASH] KEY) >> 16) & 0x7fff) + PREV_HASH
+ * with REHASH omitted at the first iteration.
+ */
+
+ ngx_crc32_init(hash);
+
+ if (hp->rehash > 0) {
+ size = ngx_sprintf(buf, "%ui", hp->rehash) - buf;
+ ngx_crc32_update(&hash, buf, size);
+ }
+
+ ngx_crc32_update(&hash, hp->key.data, hp->key.len);
+ ngx_crc32_final(hash);
+
+ hash = (hash >> 16) & 0x7fff;
+
+ hp->hash += hash;
+ hp->rehash++;
+
+ w = hp->hash % hp->rrp.peers->total_weight;
+ peer = hp->rrp.peers->peer;
+ p = 0;
+
+ while (w >= peer->weight) {
+ w -= peer->weight;
+ peer = peer->next;
+ p++;
+ }
+
+ n = p / (8 * sizeof(uintptr_t));
+ m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t));
+
+ if (hp->rrp.tried[n] & m) {
+ goto next;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_STREAM, pc->log, 0,
+ "get hash peer, value:%uD, peer:%ui", hp->hash, p);
+
+ if (peer->down) {
+ goto next;
+ }
+
+ if (peer->max_fails
+ && peer->fails >= peer->max_fails
+ && now - peer->checked <= peer->fail_timeout)
+ {
+ goto next;
+ }
+
+ break;
+
+ next:
+
+ if (++hp->tries > 20) {
+ ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers);
+ return hp->get_rr_peer(pc, &hp->rrp);
+ }
+ }
+
+ hp->rrp.current = peer;
+
+ pc->sockaddr = peer->sockaddr;
+ pc->socklen = peer->socklen;
+ pc->name = &peer->name;
+
+ peer->conns++;
+
+ if (now - peer->checked > peer->fail_timeout) {
+ peer->checked = now;
+ }
+
+ ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers);
+
+ hp->rrp.tried[n] |= m;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_stream_upstream_init_chash(ngx_conf_t *cf,
+ ngx_stream_upstream_srv_conf_t *us)
+{
+ u_char *host, *port, c;
+ size_t host_len, port_len, size;
+ uint32_t hash, base_hash, prev_hash;
+ ngx_str_t *server;
+ ngx_uint_t npoints, i, j;
+ ngx_stream_upstream_rr_peer_t *peer;
+ ngx_stream_upstream_rr_peers_t *peers;
+ ngx_stream_upstream_chash_points_t *points;
+ ngx_stream_upstream_hash_srv_conf_t *hcf;
+
+ if (ngx_stream_upstream_init_round_robin(cf, us) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ us->peer.init = ngx_stream_upstream_init_chash_peer;
+
+ peers = us->peer.data;
+ npoints = peers->total_weight * 160;
+
+ size = sizeof(ngx_stream_upstream_chash_points_t)
+ + sizeof(ngx_stream_upstream_chash_point_t) * (npoints - 1);
+
+ points = ngx_palloc(cf->pool, size);
+ if (points == NULL) {
+ return NGX_ERROR;
+ }
+
+ points->number = 0;
+
+ for (peer = peers->peer; peer; peer = peer->next) {
+ server = &peer->server;
+
+ /*
+ * Hash expression is compatible with Cache::Memcached::Fast:
+ * crc32(HOST \0 PORT PREV_HASH).
+ */
+
+ if (server->len >= 5
+ && ngx_strncasecmp(server->data, (u_char *) "unix:", 5) == 0)
+ {
+ host = server->data + 5;
+ host_len = server->len - 5;
+ port = NULL;
+ port_len = 0;
+ goto done;
+ }
+
+ for (j = 0; j < server->len; j++) {
+ c = server->data[server->len - j - 1];
+
+ if (c == ':') {
+ host = server->data;
+ host_len = server->len - j - 1;
+ port = server->data + server->len - j;
+ port_len = j;
+ goto done;
+ }
+
+ if (c < '0' || c > '9') {
+ break;
+ }
+ }
+
+ host = server->data;
+ host_len = server->len;
+ port = NULL;
+ port_len = 0;
+
+ done:
+
+ ngx_crc32_init(base_hash);
+ ngx_crc32_update(&base_hash, host, host_len);
+ ngx_crc32_update(&base_hash, (u_char *) "", 1);
+ ngx_crc32_update(&base_hash, port, port_len);
+
+ prev_hash = 0;
+ npoints = peer->weight * 160;
+
+ for (j = 0; j < npoints; j++) {
+ hash = base_hash;
+
+ ngx_crc32_update(&hash, (u_char *) &prev_hash, sizeof(uint32_t));
+ ngx_crc32_final(hash);
+
+ points->point[points->number].hash = hash;
+ points->point[points->number].server = server;
+ points->number++;
+
+ prev_hash = hash;
+ }
+ }
+
+ ngx_qsort(points->point,
+ points->number,
+ sizeof(ngx_stream_upstream_chash_point_t),
+ ngx_stream_upstream_chash_cmp_points);
+
+ for (i = 0, j = 1; j < points->number; j++) {
+ if (points->point[i].hash != points->point[j].hash) {
+ points->point[++i] = points->point[j];
+ }
+ }
+
+ points->number = i + 1;
+
+ hcf = ngx_stream_conf_upstream_srv_conf(us,
+ ngx_stream_upstream_hash_module);
+ hcf->points = points;
+
+ return NGX_OK;
+}
+
+
+static int ngx_libc_cdecl
+ngx_stream_upstream_chash_cmp_points(const void *one, const void *two)
+{
+ ngx_stream_upstream_chash_point_t *first =
+ (ngx_stream_upstream_chash_point_t *) one;
+ ngx_stream_upstream_chash_point_t *second =
+ (ngx_stream_upstream_chash_point_t *) two;
+
+ if (first->hash < second->hash) {
+ return -1;
+
+ } else if (first->hash > second->hash) {
+ return 1;
+
+ } else {
+ return 0;
+ }
+}
+
+
+static ngx_uint_t
+ngx_stream_upstream_find_chash_point(ngx_stream_upstream_chash_points_t *points,
+ uint32_t hash)
+{
+ ngx_uint_t i, j, k;
+ ngx_stream_upstream_chash_point_t *point;
+
+ /* find first point >= hash */
+
+ point = &points->point[0];
+
+ i = 0;
+ j = points->number;
+
+ while (i < j) {
+ k = (i + j) / 2;
+
+ if (hash > point[k].hash) {
+ i = k + 1;
+
+ } else if (hash < point[k].hash) {
+ j = k;
+
+ } else {
+ return k;
+ }
+ }
+
+ return i;
+}
+
+
+static ngx_int_t
+ngx_stream_upstream_init_chash_peer(ngx_stream_session_t *s,
+ ngx_stream_upstream_srv_conf_t *us)
+{
+ uint32_t hash;
+ ngx_stream_upstream_hash_srv_conf_t *hcf;
+ ngx_stream_upstream_hash_peer_data_t *hp;
+
+ if (ngx_stream_upstream_init_hash_peer(s, us) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ s->upstream->peer.get = ngx_stream_upstream_get_chash_peer;
+
+ hp = s->upstream->peer.data;
+ hcf = ngx_stream_conf_upstream_srv_conf(us,
+ ngx_stream_upstream_hash_module);
+
+ hash = ngx_crc32_long(hp->key.data, hp->key.len);
+
+ ngx_stream_upstream_rr_peers_rlock(hp->rrp.peers);
+
+ hp->hash = ngx_stream_upstream_find_chash_point(hcf->points, hash);
+
+ ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_stream_upstream_get_chash_peer(ngx_peer_connection_t *pc, void *data)
+{
+ ngx_stream_upstream_hash_peer_data_t *hp = data;
+
+ time_t now;
+ intptr_t m;
+ ngx_str_t *server;
+ ngx_int_t total;
+ ngx_uint_t i, n, best_i;
+ ngx_stream_upstream_rr_peer_t *peer, *best;
+ ngx_stream_upstream_chash_point_t *point;
+ ngx_stream_upstream_chash_points_t *points;
+ ngx_stream_upstream_hash_srv_conf_t *hcf;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0,
+ "get consistent hash peer, try: %ui", pc->tries);
+
+ ngx_stream_upstream_rr_peers_wlock(hp->rrp.peers);
+
+ pc->connection = NULL;
+
+ now = ngx_time();
+ hcf = hp->conf;
+
+ points = hcf->points;
+ point = &points->point[0];
+
+ for ( ;; ) {
+ server = point[hp->hash % points->number].server;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_STREAM, pc->log, 0,
+ "consistent hash peer:%uD, server:\"%V\"",
+ hp->hash, server);
+
+ best = NULL;
+ best_i = 0;
+ total = 0;
+
+ for (peer = hp->rrp.peers->peer, i = 0;
+ peer;
+ peer = peer->next, i++)
+ {
+
+ n = i / (8 * sizeof(uintptr_t));
+ m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));
+
+ if (hp->rrp.tried[n] & m) {
+ continue;
+ }
+
+ if (peer->down) {
+ continue;
+ }
+
+ if (peer->server.len != server->len
+ || ngx_strncmp(peer->server.data, server->data, server->len)
+ != 0)
+ {
+ continue;
+ }
+
+ if (peer->max_fails
+ && peer->fails >= peer->max_fails
+ && now - peer->checked <= peer->fail_timeout)
+ {
+ continue;
+ }
+
+ peer->current_weight += peer->effective_weight;
+ total += peer->effective_weight;
+
+ if (peer->effective_weight < peer->weight) {
+ peer->effective_weight++;
+ }
+
+ if (best == NULL || peer->current_weight > best->current_weight) {
+ best = peer;
+ best_i = i;
+ }
+ }
+
+ if (best) {
+ best->current_weight -= total;
+ break;
+ }
+
+ hp->hash++;
+ hp->tries++;
+
+ if (hp->tries >= points->number) {
+ ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers);
+ return NGX_BUSY;
+ }
+ }
+
+ hp->rrp.current = best;
+
+ pc->sockaddr = best->sockaddr;
+ pc->socklen = best->socklen;
+ pc->name = &best->name;
+
+ best->conns++;
+
+ if (now - best->checked > best->fail_timeout) {
+ best->checked = now;
+ }
+
+ ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers);
+
+ n = best_i / (8 * sizeof(uintptr_t));
+ m = (uintptr_t) 1 << best_i % (8 * sizeof(uintptr_t));
+
+ hp->rrp.tried[n] |= m;
+
+ return NGX_OK;
+}
+
+
+static void *
+ngx_stream_upstream_hash_create_conf(ngx_conf_t *cf)
+{
+ ngx_stream_upstream_hash_srv_conf_t *conf;
+
+ conf = ngx_palloc(cf->pool, sizeof(ngx_stream_upstream_hash_srv_conf_t));
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ conf->points = NULL;
+
+ return conf;
+}
+
+
+static char *
+ngx_stream_upstream_hash(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_str_t *value;
+ ngx_stream_upstream_srv_conf_t *uscf;
+
+ value = cf->args->elts;
+
+ if (ngx_strcmp(value[1].data, "$remote_addr")) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "unsupported hash key \"%V\", use $remote_addr",
+ &value[1]);
+ return NGX_CONF_ERROR;
+ }
+
+ uscf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_upstream_module);
+
+ if (uscf->peer.init_upstream) {
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "load balancing method redefined");
+ }
+
+ uscf->flags = NGX_STREAM_UPSTREAM_CREATE
+ |NGX_STREAM_UPSTREAM_WEIGHT
+ |NGX_STREAM_UPSTREAM_MAX_FAILS
+ |NGX_STREAM_UPSTREAM_FAIL_TIMEOUT
+ |NGX_STREAM_UPSTREAM_DOWN;
+
+ if (cf->args->nelts == 2) {
+ uscf->peer.init_upstream = ngx_stream_upstream_init_hash;
+
+ } else if (ngx_strcmp(value[2].data, "consistent") == 0) {
+ uscf->peer.init_upstream = ngx_stream_upstream_init_chash;
+
+ } else {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid parameter \"%V\"", &value[2]);
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
diff --git a/src/stream/ngx_stream_upstream_least_conn_module.c b/src/stream/ngx_stream_upstream_least_conn_module.c
new file mode 100644
index 000000000..eae4b177d
--- /dev/null
+++ b/src/stream/ngx_stream_upstream_least_conn_module.c
@@ -0,0 +1,305 @@
+
+/*
+ * Copyright (C) Maxim Dounin
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_stream.h>
+
+
+static ngx_int_t ngx_stream_upstream_init_least_conn_peer(
+ ngx_stream_session_t *s, ngx_stream_upstream_srv_conf_t *us);
+static ngx_int_t ngx_stream_upstream_get_least_conn_peer(
+ ngx_peer_connection_t *pc, void *data);
+static char *ngx_stream_upstream_least_conn(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
+
+static ngx_command_t ngx_stream_upstream_least_conn_commands[] = {
+
+ { ngx_string("least_conn"),
+ NGX_STREAM_UPS_CONF|NGX_CONF_NOARGS,
+ ngx_stream_upstream_least_conn,
+ 0,
+ 0,
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_stream_module_t ngx_stream_upstream_least_conn_module_ctx = {
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+};
+
+
+ngx_module_t ngx_stream_upstream_least_conn_module = {
+ NGX_MODULE_V1,
+ &ngx_stream_upstream_least_conn_module_ctx, /* module context */
+ ngx_stream_upstream_least_conn_commands, /* module directives */
+ NGX_STREAM_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_int_t
+ngx_stream_upstream_init_least_conn(ngx_conf_t *cf,
+ ngx_stream_upstream_srv_conf_t *us)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, cf->log, 0,
+ "init least conn");
+
+ if (ngx_stream_upstream_init_round_robin(cf, us) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ us->peer.init = ngx_stream_upstream_init_least_conn_peer;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_stream_upstream_init_least_conn_peer(ngx_stream_session_t *s,
+ ngx_stream_upstream_srv_conf_t *us)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
+ "init least conn peer");
+
+ if (ngx_stream_upstream_init_round_robin_peer(s, us) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ s->upstream->peer.get = ngx_stream_upstream_get_least_conn_peer;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_stream_upstream_get_least_conn_peer(ngx_peer_connection_t *pc, void *data)
+{
+ ngx_stream_upstream_rr_peer_data_t *rrp = data;
+
+ time_t now;
+ uintptr_t m;
+ ngx_int_t rc, total;
+ ngx_uint_t i, n, p, many;
+ ngx_stream_upstream_rr_peer_t *peer, *best;
+ ngx_stream_upstream_rr_peers_t *peers;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0,
+ "get least conn peer, try: %ui", pc->tries);
+
+ if (rrp->peers->single) {
+ return ngx_stream_upstream_get_round_robin_peer(pc, rrp);
+ }
+
+ pc->connection = NULL;
+
+ now = ngx_time();
+
+ peers = rrp->peers;
+
+ ngx_stream_upstream_rr_peers_wlock(peers);
+
+ best = NULL;
+ total = 0;
+
+#if (NGX_SUPPRESS_WARN)
+ many = 0;
+ p = 0;
+#endif
+
+ for (peer = peers->peer, i = 0;
+ peer;
+ peer = peer->next, i++)
+ {
+
+ n = i / (8 * sizeof(uintptr_t));
+ m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));
+
+ if (rrp->tried[n] & m) {
+ continue;
+ }
+
+ if (peer->down) {
+ continue;
+ }
+
+ if (peer->max_fails
+ && peer->fails >= peer->max_fails
+ && now - peer->checked <= peer->fail_timeout)
+ {
+ continue;
+ }
+
+ /*
+ * select peer with least number of connections; if there are
+ * multiple peers with the same number of connections, select
+ * based on round-robin
+ */
+
+ if (best == NULL
+ || peer->conns * best->weight < best->conns * peer->weight)
+ {
+ best = peer;
+ many = 0;
+ p = i;
+
+ } else if (peer->conns * best->weight == best->conns * peer->weight) {
+ many = 1;
+ }
+ }
+
+ if (best == NULL) {
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, pc->log, 0,
+ "get least conn peer, no peer found");
+
+ goto failed;
+ }
+
+ if (many) {
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, pc->log, 0,
+ "get least conn peer, many");
+
+ for (peer = best, i = p;
+ peer;
+ peer = peer->next, i++)
+ {
+ n = i / (8 * sizeof(uintptr_t));
+ m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));
+
+ if (rrp->tried[n] & m) {
+ continue;
+ }
+
+ if (peer->down) {
+ continue;
+ }
+
+ if (peer->conns * best->weight != best->conns * peer->weight) {
+ continue;
+ }
+
+ if (peer->max_fails
+ && peer->fails >= peer->max_fails
+ && now - peer->checked <= peer->fail_timeout)
+ {
+ continue;
+ }
+
+ peer->current_weight += peer->effective_weight;
+ total += peer->effective_weight;
+
+ if (peer->effective_weight < peer->weight) {
+ peer->effective_weight++;
+ }
+
+ if (peer->current_weight > best->current_weight) {
+ best = peer;
+ p = i;
+ }
+ }
+ }
+
+ best->current_weight -= total;
+
+ if (now - best->checked > best->fail_timeout) {
+ best->checked = now;
+ }
+
+ pc->sockaddr = best->sockaddr;
+ pc->socklen = best->socklen;
+ pc->name = &best->name;
+
+ best->conns++;
+
+ rrp->current = best;
+
+ n = p / (8 * sizeof(uintptr_t));
+ m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t));
+
+ rrp->tried[n] |= m;
+
+ ngx_stream_upstream_rr_peers_unlock(peers);
+
+ return NGX_OK;
+
+failed:
+
+ if (peers->next) {
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, pc->log, 0,
+ "get least conn peer, backup servers");
+
+ rrp->peers = peers->next;
+
+ n = (rrp->peers->number + (8 * sizeof(uintptr_t) - 1))
+ / (8 * sizeof(uintptr_t));
+
+ for (i = 0; i < n; i++) {
+ rrp->tried[i] = 0;
+ }
+
+ ngx_stream_upstream_rr_peers_unlock(peers);
+
+ rc = ngx_stream_upstream_get_least_conn_peer(pc, rrp);
+
+ if (rc != NGX_BUSY) {
+ return rc;
+ }
+
+ ngx_stream_upstream_rr_peers_wlock(peers);
+ }
+
+ /* all peers failed, mark them as live for quick recovery */
+
+ for (peer = peers->peer; peer; peer = peer->next) {
+ peer->fails = 0;
+ }
+
+ ngx_stream_upstream_rr_peers_unlock(peers);
+
+ pc->name = peers->name;
+
+ return NGX_BUSY;
+}
+
+
+static char *
+ngx_stream_upstream_least_conn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_stream_upstream_srv_conf_t *uscf;
+
+ uscf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_upstream_module);
+
+ if (uscf->peer.init_upstream) {
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "load balancing method redefined");
+ }
+
+ uscf->peer.init_upstream = ngx_stream_upstream_init_least_conn;
+
+ uscf->flags = NGX_STREAM_UPSTREAM_CREATE
+ |NGX_STREAM_UPSTREAM_WEIGHT
+ |NGX_STREAM_UPSTREAM_MAX_FAILS
+ |NGX_STREAM_UPSTREAM_FAIL_TIMEOUT
+ |NGX_STREAM_UPSTREAM_DOWN
+ |NGX_STREAM_UPSTREAM_BACKUP;
+
+ return NGX_CONF_OK;
+}
diff --git a/src/stream/ngx_stream_upstream_round_robin.c b/src/stream/ngx_stream_upstream_round_robin.c
new file mode 100644
index 000000000..c9157cd99
--- /dev/null
+++ b/src/stream/ngx_stream_upstream_round_robin.c
@@ -0,0 +1,697 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_stream.h>
+
+
+#define ngx_stream_upstream_tries(p) ((p)->number \
+ + ((p)->next ? (p)->next->number : 0))
+
+
+static ngx_stream_upstream_rr_peer_t *ngx_stream_upstream_get_peer(
+ ngx_stream_upstream_rr_peer_data_t *rrp);
+
+#if (NGX_STREAM_SSL)
+
+static ngx_int_t ngx_stream_upstream_set_round_robin_peer_session(
+ ngx_peer_connection_t *pc, void *data);
+static void ngx_stream_upstream_save_round_robin_peer_session(
+ ngx_peer_connection_t *pc, void *data);
+
+#endif
+
+
+ngx_int_t
+ngx_stream_upstream_init_round_robin(ngx_conf_t *cf,
+ ngx_stream_upstream_srv_conf_t *us)
+{
+ ngx_url_t u;
+ ngx_uint_t i, j, n, w;
+ ngx_stream_upstream_server_t *server;
+ ngx_stream_upstream_rr_peer_t *peer, **peerp;
+ ngx_stream_upstream_rr_peers_t *peers, *backup;
+
+ us->peer.init = ngx_stream_upstream_init_round_robin_peer;
+
+ if (us->servers) {
+ server = us->servers->elts;
+
+ n = 0;
+ w = 0;
+
+ for (i = 0; i < us->servers->nelts; i++) {
+ if (server[i].backup) {
+ continue;
+ }
+
+ n += server[i].naddrs;
+ w += server[i].naddrs * server[i].weight;
+ }
+
+ if (n == 0) {
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "no servers in upstream \"%V\" in %s:%ui",
+ &us->host, us->file_name, us->line);
+ return NGX_ERROR;
+ }
+
+ peers = ngx_pcalloc(cf->pool, sizeof(ngx_stream_upstream_rr_peers_t));
+ if (peers == NULL) {
+ return NGX_ERROR;
+ }
+
+ peer = ngx_pcalloc(cf->pool, sizeof(ngx_stream_upstream_rr_peer_t) * n);
+ if (peer == NULL) {
+ return NGX_ERROR;
+ }
+
+ peers->single = (n == 1);
+ peers->number = n;
+ peers->weighted = (w != n);
+ peers->total_weight = w;
+ peers->name = &us->host;
+
+ n = 0;
+ peerp = &peers->peer;
+
+ for (i = 0; i < us->servers->nelts; i++) {
+ if (server[i].backup) {
+ continue;
+ }
+
+ for (j = 0; j < server[i].naddrs; j++) {
+ peer[n].sockaddr = server[i].addrs[j].sockaddr;
+ peer[n].socklen = server[i].addrs[j].socklen;
+ peer[n].name = server[i].addrs[j].name;
+ peer[n].weight = server[i].weight;
+ peer[n].effective_weight = server[i].weight;
+ peer[n].current_weight = 0;
+ peer[n].max_fails = server[i].max_fails;
+ peer[n].fail_timeout = server[i].fail_timeout;
+ peer[n].down = server[i].down;
+ peer[n].server = server[i].name;
+
+ *peerp = &peer[n];
+ peerp = &peer[n].next;
+ n++;
+ }
+ }
+
+ us->peer.data = peers;
+
+ /* backup servers */
+
+ n = 0;
+ w = 0;
+
+ for (i = 0; i < us->servers->nelts; i++) {
+ if (!server[i].backup) {
+ continue;
+ }
+
+ n += server[i].naddrs;
+ w += server[i].naddrs * server[i].weight;
+ }
+
+ if (n == 0) {
+ return NGX_OK;
+ }
+
+ backup = ngx_pcalloc(cf->pool, sizeof(ngx_stream_upstream_rr_peers_t));
+ if (backup == NULL) {
+ return NGX_ERROR;
+ }
+
+ peer = ngx_pcalloc(cf->pool, sizeof(ngx_stream_upstream_rr_peer_t) * n);
+ if (peer == NULL) {
+ return NGX_ERROR;
+ }
+
+ peers->single = 0;
+ backup->single = 0;
+ backup->number = n;
+ backup->weighted = (w != n);
+ backup->total_weight = w;
+ backup->name = &us->host;
+
+ n = 0;
+ peerp = &backup->peer;
+
+ for (i = 0; i < us->servers->nelts; i++) {
+ if (!server[i].backup) {
+ continue;
+ }
+
+ for (j = 0; j < server[i].naddrs; j++) {
+ peer[n].sockaddr = server[i].addrs[j].sockaddr;
+ peer[n].socklen = server[i].addrs[j].socklen;
+ peer[n].name = server[i].addrs[j].name;
+ peer[n].weight = server[i].weight;
+ peer[n].effective_weight = server[i].weight;
+ peer[n].current_weight = 0;
+ peer[n].max_fails = server[i].max_fails;
+ peer[n].fail_timeout = server[i].fail_timeout;
+ peer[n].down = server[i].down;
+ peer[n].server = server[i].name;
+
+ *peerp = &peer[n];
+ peerp = &peer[n].next;
+ n++;
+ }
+ }
+
+ peers->next = backup;
+
+ 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 = 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_stream_upstream_rr_peers_t));
+ if (peers == NULL) {
+ return NGX_ERROR;
+ }
+
+ peer = ngx_pcalloc(cf->pool, sizeof(ngx_stream_upstream_rr_peer_t) * n);
+ if (peer == NULL) {
+ return NGX_ERROR;
+ }
+
+ peers->single = (n == 1);
+ peers->number = n;
+ peers->weighted = 0;
+ peers->total_weight = n;
+ peers->name = &us->host;
+
+ peerp = &peers->peer;
+
+ for (i = 0; i < u.naddrs; i++) {
+ peer[i].sockaddr = u.addrs[i].sockaddr;
+ peer[i].socklen = u.addrs[i].socklen;
+ peer[i].name = u.addrs[i].name;
+ peer[i].weight = 1;
+ peer[i].effective_weight = 1;
+ peer[i].current_weight = 0;
+ peer[i].max_fails = 1;
+ peer[i].fail_timeout = 10;
+ *peerp = &peer[i];
+ peerp = &peer[i].next;
+ }
+
+ us->peer.data = peers;
+
+ /* implicitly defined upstream has no backup servers */
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_stream_upstream_init_round_robin_peer(ngx_stream_session_t *s,
+ ngx_stream_upstream_srv_conf_t *us)
+{
+ ngx_uint_t n;
+ ngx_stream_upstream_rr_peer_data_t *rrp;
+
+ rrp = s->upstream->peer.data;
+
+ if (rrp == NULL) {
+ rrp = ngx_palloc(s->connection->pool,
+ sizeof(ngx_stream_upstream_rr_peer_data_t));
+ if (rrp == NULL) {
+ return NGX_ERROR;
+ }
+
+ s->upstream->peer.data = rrp;
+ }
+
+ rrp->peers = us->peer.data;
+ rrp->current = NULL;
+
+ n = rrp->peers->number;
+
+ if (rrp->peers->next && rrp->peers->next->number > n) {
+ n = rrp->peers->next->number;
+ }
+
+ if (n <= 8 * sizeof(uintptr_t)) {
+ rrp->tried = &rrp->data;
+ rrp->data = 0;
+
+ } else {
+ n = (n + (8 * sizeof(uintptr_t) - 1)) / (8 * sizeof(uintptr_t));
+
+ rrp->tried = ngx_pcalloc(s->connection->pool, n * sizeof(uintptr_t));
+ if (rrp->tried == NULL) {
+ return NGX_ERROR;
+ }
+ }
+
+ s->upstream->peer.get = ngx_stream_upstream_get_round_robin_peer;
+ s->upstream->peer.free = ngx_stream_upstream_free_round_robin_peer;
+ s->upstream->peer.tries = ngx_stream_upstream_tries(rrp->peers);
+#if (NGX_STREAM_SSL)
+ s->upstream->peer.set_session =
+ ngx_stream_upstream_set_round_robin_peer_session;
+ s->upstream->peer.save_session =
+ ngx_stream_upstream_save_round_robin_peer_session;
+#endif
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_stream_upstream_get_round_robin_peer(ngx_peer_connection_t *pc, void *data)
+{
+ ngx_stream_upstream_rr_peer_data_t *rrp = data;
+
+ ngx_int_t rc;
+ ngx_uint_t i, n;
+ ngx_stream_upstream_rr_peer_t *peer;
+ ngx_stream_upstream_rr_peers_t *peers;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0,
+ "get rr peer, try: %ui", pc->tries);
+
+ pc->connection = NULL;
+
+ peers = rrp->peers;
+ ngx_stream_upstream_rr_peers_wlock(peers);
+
+ if (peers->single) {
+ peer = peers->peer;
+
+ if (peer->down) {
+ goto failed;
+ }
+
+ rrp->current = peer;
+
+ } else {
+
+ /* there are several peers */
+
+ peer = ngx_stream_upstream_get_peer(rrp);
+
+ if (peer == NULL) {
+ goto failed;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_STREAM, pc->log, 0,
+ "get rr peer, current: %p %i",
+ peer, peer->current_weight);
+ }
+
+ pc->sockaddr = peer->sockaddr;
+ pc->socklen = peer->socklen;
+ pc->name = &peer->name;
+
+ peer->conns++;
+
+ ngx_stream_upstream_rr_peers_unlock(peers);
+
+ return NGX_OK;
+
+failed:
+
+ if (peers->next) {
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, pc->log, 0, "backup servers");
+
+ rrp->peers = peers->next;
+
+ n = (rrp->peers->number + (8 * sizeof(uintptr_t) - 1))
+ / (8 * sizeof(uintptr_t));
+
+ for (i = 0; i < n; i++) {
+ rrp->tried[i] = 0;
+ }
+
+ ngx_stream_upstream_rr_peers_unlock(peers);
+
+ rc = ngx_stream_upstream_get_round_robin_peer(pc, rrp);
+
+ if (rc != NGX_BUSY) {
+ return rc;
+ }
+
+ ngx_stream_upstream_rr_peers_wlock(peers);
+ }
+
+ /* all peers failed, mark them as live for quick recovery */
+
+ for (peer = peers->peer; peer; peer = peer->next) {
+ peer->fails = 0;
+ }
+
+ ngx_stream_upstream_rr_peers_unlock(peers);
+
+ pc->name = peers->name;
+
+ return NGX_BUSY;
+}
+
+
+static ngx_stream_upstream_rr_peer_t *
+ngx_stream_upstream_get_peer(ngx_stream_upstream_rr_peer_data_t *rrp)
+{
+ time_t now;
+ uintptr_t m;
+ ngx_int_t total;
+ ngx_uint_t i, n, p;
+ ngx_stream_upstream_rr_peer_t *peer, *best;
+
+ now = ngx_time();
+
+ best = NULL;
+ total = 0;
+
+#if (NGX_SUPPRESS_WARN)
+ p = 0;
+#endif
+
+ for (peer = rrp->peers->peer, i = 0;
+ peer;
+ peer = peer->next, i++)
+ {
+
+ n = i / (8 * sizeof(uintptr_t));
+ m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));
+
+ if (rrp->tried[n] & m) {
+ continue;
+ }
+
+ if (peer->down) {
+ continue;
+ }
+
+ if (peer->max_fails
+ && peer->fails >= peer->max_fails
+ && now - peer->checked <= peer->fail_timeout)
+ {
+ continue;
+ }
+
+ peer->current_weight += peer->effective_weight;
+ total += peer->effective_weight;
+
+ if (peer->effective_weight < peer->weight) {
+ peer->effective_weight++;
+ }
+
+ if (best == NULL || peer->current_weight > best->current_weight) {
+ best = peer;
+ p = i;
+ }
+ }
+
+ if (best == NULL) {
+ return NULL;
+ }
+
+ rrp->current = best;
+
+ n = p / (8 * sizeof(uintptr_t));
+ m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t));
+
+ rrp->tried[n] |= m;
+
+ best->current_weight -= total;
+
+ if (now - best->checked > best->fail_timeout) {
+ best->checked = now;
+ }
+
+ return best;
+}
+
+
+void
+ngx_stream_upstream_free_round_robin_peer(ngx_peer_connection_t *pc, void *data,
+ ngx_uint_t state)
+{
+ ngx_stream_upstream_rr_peer_data_t *rrp = data;
+
+ time_t now;
+ ngx_stream_upstream_rr_peer_t *peer;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_STREAM, pc->log, 0,
+ "free rr peer %ui %ui", pc->tries, state);
+
+ peer = rrp->current;
+
+ ngx_stream_upstream_rr_peers_rlock(rrp->peers);
+ ngx_stream_upstream_rr_peer_lock(rrp->peers, peer);
+
+ if (rrp->peers->single) {
+ peer->conns--;
+
+ ngx_stream_upstream_rr_peer_unlock(rrp->peers, peer);
+ ngx_stream_upstream_rr_peers_unlock(rrp->peers);
+
+ pc->tries = 0;
+ return;
+ }
+
+ if (state & NGX_PEER_FAILED) {
+ now = ngx_time();
+
+ peer->fails++;
+ peer->accessed = now;
+ peer->checked = now;
+
+ if (peer->max_fails) {
+ peer->effective_weight -= peer->weight / peer->max_fails;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_STREAM, pc->log, 0,
+ "free rr peer failed: %p %i",
+ peer, peer->effective_weight);
+
+ if (peer->effective_weight < 0) {
+ peer->effective_weight = 0;
+ }
+
+ } else {
+
+ /* mark peer live if check passed */
+
+ if (peer->accessed < peer->checked) {
+ peer->fails = 0;
+ }
+ }
+
+ peer->conns--;
+
+ ngx_stream_upstream_rr_peer_unlock(rrp->peers, peer);
+ ngx_stream_upstream_rr_peers_unlock(rrp->peers);
+
+ if (pc->tries) {
+ pc->tries--;
+ }
+}
+
+
+#if (NGX_STREAM_SSL)
+
+static ngx_int_t
+ngx_stream_upstream_set_round_robin_peer_session(ngx_peer_connection_t *pc,
+ void *data)
+{
+ ngx_stream_upstream_rr_peer_data_t *rrp = data;
+
+ ngx_int_t rc;
+ ngx_ssl_session_t *ssl_session;
+ ngx_stream_upstream_rr_peer_t *peer;
+#if (NGX_STREAM_UPSTREAM_ZONE)
+ int len;
+#if OPENSSL_VERSION_NUMBER >= 0x0090707fL
+ const
+#endif
+ u_char *p;
+ ngx_stream_upstream_rr_peers_t *peers;
+ u_char buf[NGX_SSL_MAX_SESSION_SIZE];
+#endif
+
+ peer = rrp->current;
+
+#if (NGX_STREAM_UPSTREAM_ZONE)
+ peers = rrp->peers;
+
+ if (peers->shpool) {
+ ngx_stream_upstream_rr_peers_rlock(peers);
+ ngx_stream_upstream_rr_peer_lock(peers, peer);
+
+ if (peer->ssl_session == NULL) {
+ ngx_stream_upstream_rr_peer_unlock(peers, peer);
+ ngx_stream_upstream_rr_peers_unlock(peers);
+ return NGX_OK;
+ }
+
+ len = peer->ssl_session_len;
+
+ ngx_memcpy(buf, peer->ssl_session, len);
+
+ ngx_stream_upstream_rr_peer_unlock(peers, peer);
+ ngx_stream_upstream_rr_peers_unlock(peers);
+
+ p = buf;
+ ssl_session = d2i_SSL_SESSION(NULL, &p, len);
+
+ rc = ngx_ssl_set_session(pc->connection, ssl_session);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0,
+ "set session: %p", ssl_session);
+
+ ngx_ssl_free_session(ssl_session);
+
+ return rc;
+ }
+#endif
+
+ ssl_session = peer->ssl_session;
+
+ rc = ngx_ssl_set_session(pc->connection, ssl_session);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0,
+ "set session: %p", ssl_session);
+
+ return rc;
+}
+
+
+static void
+ngx_stream_upstream_save_round_robin_peer_session(ngx_peer_connection_t *pc,
+ void *data)
+{
+ ngx_stream_upstream_rr_peer_data_t *rrp = data;
+
+ ngx_ssl_session_t *old_ssl_session, *ssl_session;
+ ngx_stream_upstream_rr_peer_t *peer;
+#if (NGX_STREAM_UPSTREAM_ZONE)
+ int len;
+ u_char *p;
+ ngx_stream_upstream_rr_peers_t *peers;
+ u_char buf[NGX_SSL_MAX_SESSION_SIZE];
+#endif
+
+#if (NGX_STREAM_UPSTREAM_ZONE)
+ peers = rrp->peers;
+
+ if (peers->shpool) {
+
+ ssl_session = SSL_get0_session(pc->connection->ssl->connection);
+
+ if (ssl_session == NULL) {
+ return;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0,
+ "save session: %p", ssl_session);
+
+ len = i2d_SSL_SESSION(ssl_session, NULL);
+
+ /* do not cache too big session */
+
+ if (len > NGX_SSL_MAX_SESSION_SIZE) {
+ return;
+ }
+
+ p = buf;
+ (void) i2d_SSL_SESSION(ssl_session, &p);
+
+ peer = rrp->current;
+
+ ngx_stream_upstream_rr_peers_rlock(peers);
+ ngx_stream_upstream_rr_peer_lock(peers, peer);
+
+ if (len > peer->ssl_session_len) {
+ ngx_shmtx_lock(&peers->shpool->mutex);
+
+ if (peer->ssl_session) {
+ ngx_slab_free_locked(peers->shpool, peer->ssl_session);
+ }
+
+ peer->ssl_session = ngx_slab_alloc_locked(peers->shpool, len);
+
+ ngx_shmtx_unlock(&peers->shpool->mutex);
+
+ if (peer->ssl_session == NULL) {
+ peer->ssl_session_len = 0;
+
+ ngx_stream_upstream_rr_peer_unlock(peers, peer);
+ ngx_stream_upstream_rr_peers_unlock(peers);
+ return;
+ }
+
+ peer->ssl_session_len = len;
+ }
+
+ ngx_memcpy(peer->ssl_session, buf, len);
+
+ ngx_stream_upstream_rr_peer_unlock(peers, peer);
+ ngx_stream_upstream_rr_peers_unlock(peers);
+
+ return;
+ }
+#endif
+
+ ssl_session = ngx_ssl_get_session(pc->connection);
+
+ if (ssl_session == NULL) {
+ return;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0,
+ "save session: %p", ssl_session);
+
+ peer = rrp->current;
+
+ old_ssl_session = peer->ssl_session;
+ peer->ssl_session = ssl_session;
+
+ if (old_ssl_session) {
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0,
+ "old session: %p", old_ssl_session);
+
+ /* TODO: may block */
+
+ ngx_ssl_free_session(old_ssl_session);
+ }
+}
+
+#endif
diff --git a/src/stream/ngx_stream_upstream_round_robin.h b/src/stream/ngx_stream_upstream_round_robin.h
new file mode 100644
index 000000000..83fd8b5a8
--- /dev/null
+++ b/src/stream/ngx_stream_upstream_round_robin.h
@@ -0,0 +1,138 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_STREAM_UPSTREAM_ROUND_ROBIN_H_INCLUDED_
+#define _NGX_STREAM_UPSTREAM_ROUND_ROBIN_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_stream.h>
+
+
+typedef struct ngx_stream_upstream_rr_peer_s ngx_stream_upstream_rr_peer_t;
+
+struct ngx_stream_upstream_rr_peer_s {
+ struct sockaddr *sockaddr;
+ socklen_t socklen;
+ ngx_str_t name;
+ ngx_str_t server;
+
+ ngx_int_t current_weight;
+ ngx_int_t effective_weight;
+ ngx_int_t weight;
+
+ ngx_uint_t conns;
+
+ ngx_uint_t fails;
+ time_t accessed;
+ time_t checked;
+
+ ngx_uint_t max_fails;
+ time_t fail_timeout;
+
+ ngx_uint_t down; /* unsigned down:1; */
+
+#if (NGX_STREAM_SSL)
+ void *ssl_session;
+ int ssl_session_len;
+#endif
+
+ ngx_stream_upstream_rr_peer_t *next;
+
+#if (NGX_STREAM_UPSTREAM_ZONE)
+ ngx_atomic_t lock;
+#endif
+};
+
+
+typedef struct ngx_stream_upstream_rr_peers_s ngx_stream_upstream_rr_peers_t;
+
+struct ngx_stream_upstream_rr_peers_s {
+ ngx_uint_t number;
+
+#if (NGX_STREAM_UPSTREAM_ZONE)
+ ngx_slab_pool_t *shpool;
+ ngx_atomic_t rwlock;
+#endif
+
+ ngx_uint_t total_weight;
+
+ unsigned single:1;
+ unsigned weighted:1;
+
+ ngx_str_t *name;
+
+ ngx_stream_upstream_rr_peers_t *next;
+
+ ngx_stream_upstream_rr_peer_t *peer;
+};
+
+
+#if (NGX_STREAM_UPSTREAM_ZONE)
+
+#define ngx_stream_upstream_rr_peers_rlock(peers) \
+ \
+ if (peers->shpool) { \
+ ngx_rwlock_rlock(&peers->rwlock); \
+ }
+
+#define ngx_stream_upstream_rr_peers_wlock(peers) \
+ \
+ if (peers->shpool) { \
+ ngx_rwlock_wlock(&peers->rwlock); \
+ }
+
+#define ngx_stream_upstream_rr_peers_unlock(peers) \
+ \
+ if (peers->shpool) { \
+ ngx_rwlock_unlock(&peers->rwlock); \
+ }
+
+
+#define ngx_stream_upstream_rr_peer_lock(peers, peer) \
+ \
+ if (peers->shpool) { \
+ ngx_rwlock_wlock(&peer->lock); \
+ }
+
+#define ngx_stream_upstream_rr_peer_unlock(peers, peer) \
+ \
+ if (peers->shpool) { \
+ ngx_rwlock_unlock(&peer->lock); \
+ }
+
+#else
+
+#define ngx_stream_upstream_rr_peers_rlock(peers)
+#define ngx_stream_upstream_rr_peers_wlock(peers)
+#define ngx_stream_upstream_rr_peers_unlock(peers)
+#define ngx_stream_upstream_rr_peer_lock(peers, peer)
+#define ngx_stream_upstream_rr_peer_unlock(peers, peer)
+
+#endif
+
+
+typedef struct {
+ ngx_stream_upstream_rr_peers_t *peers;
+ ngx_stream_upstream_rr_peer_t *current;
+ uintptr_t *tried;
+ uintptr_t data;
+} ngx_stream_upstream_rr_peer_data_t;
+
+
+ngx_int_t ngx_stream_upstream_init_round_robin(ngx_conf_t *cf,
+ ngx_stream_upstream_srv_conf_t *us);
+ngx_int_t ngx_stream_upstream_init_round_robin_peer(ngx_stream_session_t *s,
+ ngx_stream_upstream_srv_conf_t *us);
+ngx_int_t ngx_stream_upstream_get_round_robin_peer(ngx_peer_connection_t *pc,
+ void *data);
+void ngx_stream_upstream_free_round_robin_peer(ngx_peer_connection_t *pc,
+ void *data, ngx_uint_t state);
+
+
+#endif /* _NGX_STREAM_UPSTREAM_ROUND_ROBIN_H_INCLUDED_ */
diff --git a/src/stream/ngx_stream_upstream_zone_module.c b/src/stream/ngx_stream_upstream_zone_module.c
new file mode 100644
index 000000000..95a778f10
--- /dev/null
+++ b/src/stream/ngx_stream_upstream_zone_module.c
@@ -0,0 +1,221 @@
+
+/*
+ * Copyright (C) Ruslan Ermilov
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_stream.h>
+
+
+static char *ngx_stream_upstream_zone(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static ngx_int_t ngx_stream_upstream_init_zone(ngx_shm_zone_t *shm_zone,
+ void *data);
+static ngx_int_t ngx_stream_upstream_zone_copy_peers(ngx_slab_pool_t *shpool,
+ ngx_stream_upstream_srv_conf_t *uscf);
+
+
+static ngx_command_t ngx_stream_upstream_zone_commands[] = {
+
+ { ngx_string("zone"),
+ NGX_STREAM_UPS_CONF|NGX_CONF_TAKE12,
+ ngx_stream_upstream_zone,
+ 0,
+ 0,
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_stream_module_t ngx_stream_upstream_zone_module_ctx = {
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+};
+
+
+ngx_module_t ngx_stream_upstream_zone_module = {
+ NGX_MODULE_V1,
+ &ngx_stream_upstream_zone_module_ctx, /* module context */
+ ngx_stream_upstream_zone_commands, /* module directives */
+ NGX_STREAM_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_stream_upstream_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ssize_t size;
+ ngx_str_t *value;
+ ngx_stream_upstream_srv_conf_t *uscf;
+ ngx_stream_upstream_main_conf_t *umcf;
+
+ uscf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_upstream_module);
+ umcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_upstream_module);
+
+ value = cf->args->elts;
+
+ if (!value[1].len) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid zone name \"%V\"", &value[1]);
+ return NGX_CONF_ERROR;
+ }
+
+ if (cf->args->nelts == 3) {
+ size = ngx_parse_size(&value[2]);
+
+ if (size == NGX_ERROR) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid zone size \"%V\"", &value[2]);
+ return NGX_CONF_ERROR;
+ }
+
+ if (size < (ssize_t) (8 * ngx_pagesize)) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "zone \"%V\" is too small", &value[1]);
+ return NGX_CONF_ERROR;
+ }
+
+ } else {
+ size = 0;
+ }
+
+ uscf->shm_zone = ngx_shared_memory_add(cf, &value[1], size,
+ &ngx_stream_upstream_module);
+ if (uscf->shm_zone == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ uscf->shm_zone->init = ngx_stream_upstream_init_zone;
+ uscf->shm_zone->data = umcf;
+
+ uscf->shm_zone->noreuse = 1;
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_stream_upstream_init_zone(ngx_shm_zone_t *shm_zone, void *data)
+{
+ size_t len;
+ ngx_uint_t i;
+ ngx_slab_pool_t *shpool;
+ ngx_stream_upstream_srv_conf_t *uscf, **uscfp;
+ ngx_stream_upstream_main_conf_t *umcf;
+
+ shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
+
+ if (shm_zone->shm.exists) {
+ return NGX_ERROR;
+ }
+
+ len = sizeof(" in upstream zone \"\"") + shm_zone->shm.name.len;
+
+ shpool->log_ctx = ngx_slab_alloc(shpool, len);
+ if (shpool->log_ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_sprintf(shpool->log_ctx, " in upstream zone \"%V\"%Z",
+ &shm_zone->shm.name);
+
+
+ /* copy peers to shared memory */
+
+ umcf = shm_zone->data;
+ uscfp = umcf->upstreams.elts;
+
+ for (i = 0; i < umcf->upstreams.nelts; i++) {
+ uscf = uscfp[i];
+
+ if (uscf->shm_zone != shm_zone) {
+ continue;
+ }
+
+ if (ngx_stream_upstream_zone_copy_peers(shpool, uscf) != NGX_OK) {
+ return NGX_ERROR;
+ }
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_stream_upstream_zone_copy_peers(ngx_slab_pool_t *shpool,
+ ngx_stream_upstream_srv_conf_t *uscf)
+{
+ ngx_stream_upstream_rr_peer_t *peer, **peerp;
+ ngx_stream_upstream_rr_peers_t *peers, *backup;
+
+ peers = ngx_slab_alloc(shpool, sizeof(ngx_stream_upstream_rr_peers_t));
+ if (peers == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(peers, uscf->peer.data, sizeof(ngx_stream_upstream_rr_peers_t));
+
+ peers->shpool = shpool;
+
+ for (peerp = &peers->peer; *peerp; peerp = &peer->next) {
+ /* pool is unlocked */
+ peer = ngx_slab_calloc_locked(shpool,
+ sizeof(ngx_stream_upstream_rr_peer_t));
+ if (peer == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(peer, *peerp, sizeof(ngx_stream_upstream_rr_peer_t));
+
+ *peerp = peer;
+ }
+
+ if (peers->next == NULL) {
+ goto done;
+ }
+
+ backup = ngx_slab_alloc(shpool, sizeof(ngx_stream_upstream_rr_peers_t));
+ if (backup == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(backup, peers->next, sizeof(ngx_stream_upstream_rr_peers_t));
+
+ backup->shpool = shpool;
+
+ for (peerp = &backup->peer; *peerp; peerp = &peer->next) {
+ /* pool is unlocked */
+ peer = ngx_slab_calloc_locked(shpool,
+ sizeof(ngx_stream_upstream_rr_peer_t));
+ if (peer == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(peer, *peerp, sizeof(ngx_stream_upstream_rr_peer_t));
+
+ *peerp = peer;
+ }
+
+ peers->next = backup;
+
+done:
+
+ uscf->peer.data = peers;
+
+ return NGX_OK;
+}