diff options
author | nginx <nginx@nginx.org> | 2015-04-28 15:42:09 +0000 |
---|---|---|
committer | Jon Kolb <kolbyjack@gmail.com> | 2015-04-28 15:42:09 +0000 |
commit | cf6aa562bf80ee87c803ec940cb23234487f1444 (patch) | |
tree | 264d9212b7eaa078be7cb179d4e170c01dee387a | |
parent | a3055d1ce9febb3fa0c8d25ba406c7c473efba43 (diff) | |
download | nginx-cf6aa562bf80ee87c803ec940cb23234487f1444.tar.gz |
Changes with nginx 1.9.0 28 Apr 2015v1.9.0
72 files changed, 7030 insertions, 1993 deletions
@@ -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 @@ -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; +} |