From noreply at nginx.com Wed Nov 6 16:17:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Wed, 6 Nov 2024 16:17:02 +0000 (UTC) Subject: [njs] QuickJS: introduced process.kill(). Message-ID: <20241106161702.39BAF478D7@pubserv1.nginx> details: https://github.com/nginx/njs/commit/361a7fc60f3d0fa223160fc1864f097af849ad70 branches: master commit: 361a7fc60f3d0fa223160fc1864f097af849ad70 user: Dmitry Volyntsev date: Thu, 31 Oct 2024 18:15:22 -0700 description: QuickJS: introduced process.kill(). --- src/qjs.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/src/qjs.c b/src/qjs.c index b529f1bb..44e9fe95 100644 --- a/src/qjs.c +++ b/src/qjs.c @@ -9,6 +9,14 @@ #include #include +#include +#include + + +typedef struct { + njs_str_t name; + int value; +} qjs_signal_entry_t; extern char **environ; @@ -19,10 +27,37 @@ static JSValue qjs_njs_to_string_tag(JSContext *ctx, JSValueConst this_val); static JSValue qjs_process_to_string_tag(JSContext *ctx, JSValueConst this_val); static JSValue qjs_process_argv(JSContext *ctx, JSValueConst this_val); static JSValue qjs_process_env(JSContext *ctx, JSValueConst this_val); +static JSValue qjs_process_kill(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); static JSValue qjs_process_pid(JSContext *ctx, JSValueConst this_val); static JSValue qjs_process_ppid(JSContext *ctx, JSValueConst this_val); +/* P1990 signals from `man 7 signal` are supported */ +static qjs_signal_entry_t qjs_signals_table[] = { + { njs_str("ABRT"), SIGABRT }, + { njs_str("ALRM"), SIGALRM }, + { njs_str("CHLD"), SIGCHLD }, + { njs_str("CONT"), SIGCONT }, + { njs_str("FPE"), SIGFPE }, + { njs_str("HUP"), SIGHUP }, + { njs_str("ILL"), SIGILL }, + { njs_str("INT"), SIGINT }, + { njs_str("KILL"), SIGKILL }, + { njs_str("PIPE"), SIGPIPE }, + { njs_str("QUIT"), SIGQUIT }, + { njs_str("SEGV"), SIGSEGV }, + { njs_str("STOP"), SIGSTOP }, + { njs_str("TSTP"), SIGTSTP }, + { njs_str("TERM"), SIGTERM }, + { njs_str("TTIN"), SIGTTIN }, + { njs_str("TTOU"), SIGTTOU }, + { njs_str("USR1"), SIGUSR1 }, + { njs_str("USR2"), SIGUSR2 }, + { njs_null_str, 0 } +}; + + static const JSCFunctionListEntry qjs_global_proto[] = { JS_CGETSET_DEF("njs", qjs_njs_getter, NULL), }; @@ -39,6 +74,7 @@ static const JSCFunctionListEntry qjs_process_proto[] = { JS_CGETSET_DEF("[Symbol.toStringTag]", qjs_process_to_string_tag, NULL), JS_CGETSET_DEF("argv", qjs_process_argv, NULL), JS_CGETSET_DEF("env", qjs_process_env, NULL), + JS_CFUNC_DEF("kill", 2, qjs_process_kill), JS_CGETSET_DEF("pid", qjs_process_pid, NULL), JS_CGETSET_DEF("ppid", qjs_process_ppid, NULL), }; @@ -251,6 +287,72 @@ error: } +static JSValue +qjs_process_kill(JSContext *ctx, JSValueConst this_val, int argc, + JSValueConst *argv) +{ + int signo, pid; + JSValue val; + njs_str_t name; + const char *signal; + qjs_signal_entry_t *entry; + + if (JS_ToInt32(ctx, &pid, argv[0]) < 0) { + return JS_EXCEPTION; + } + + if (JS_IsNumber(argv[1])) { + if (JS_ToInt32(ctx, &signo, argv[1]) < 0) { + return JS_EXCEPTION; + } + + if (signo < 0 || signo >= NSIG) { + return JS_ThrowTypeError(ctx, "unknown signal: %d", signo); + } + + } else { + val = JS_ToString(ctx, argv[1]); + if (JS_IsException(val)) { + return JS_EXCEPTION; + } + + signal = JS_ToCString(ctx, val); + if (signal == NULL) { + JS_FreeValue(ctx, val); + return JS_EXCEPTION; + } + + if (njs_strlen(signal) < 3 || memcmp(signal, "SIG", 3) != 0) { + JS_FreeCString(ctx, signal); + return JS_ThrowTypeError(ctx, "unknown signal: %s", signal); + } + + name.start = (u_char *) signal + 3; + name.length = njs_strlen(signal) - 3; + + for (entry = qjs_signals_table; entry->name.length != 0; entry++) { + if (njs_strstr_eq(&entry->name, &name)) { + signo = entry->value; + break; + } + } + + JS_FreeCString(ctx, signal); + + if (entry->name.length == 0) { + return JS_ThrowTypeError(ctx, "unknown signal: %s", signal); + } + } + + if (kill(pid, signo) < 0) { + return JS_ThrowTypeError(ctx, "kill failed with (%d:%s)", errno, + strerror(errno)); + } + + return JS_TRUE; +} + + static JSValue qjs_process_pid(JSContext *ctx, JSValueConst this_val) { From noreply at nginx.com Wed Nov 6 16:17:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Wed, 6 Nov 2024 16:17:02 +0000 (UTC) Subject: [njs] Introduced process.kill() function. Message-ID: <20241106161702.3F375478D8@pubserv1.nginx> details: https://github.com/nginx/njs/commit/8666e2110b5e88cb99cbca2afe926569f60b58b9 branches: master commit: 8666e2110b5e88cb99cbca2afe926569f60b58b9 user: Stefan Sundin date: Fri, 25 Oct 2024 12:50:45 -0700 description: Introduced process.kill() function. --- src/njs_builtin.c | 95 +++++++++++++++++++++++++++++++++++++++++++++++++ test/shell_test.exp | 3 ++ test/shell_test_njs.exp | 2 ++ ts/njs_core.d.ts | 5 +++ 4 files changed, 105 insertions(+) diff --git a/src/njs_builtin.c b/src/njs_builtin.c index 3ff6f547..812f18d2 100644 --- a/src/njs_builtin.c +++ b/src/njs_builtin.c @@ -7,6 +7,7 @@ #include +#include typedef struct { @@ -22,6 +23,12 @@ typedef struct { } njs_builtin_traverse_t; +typedef struct { + njs_str_t name; + int value; +} njs_signal_entry_t; + + static njs_int_t njs_global_this_prop_handler(njs_vm_t *vm, njs_object_prop_t *self, njs_value_t *global, njs_value_t *setval, njs_value_t *retval); @@ -99,6 +106,31 @@ static const njs_object_type_init_t *const }; +/* P1990 signals from `man 7 signal` are supported */ +static njs_signal_entry_t njs_signals_table[] = { + { njs_str("ABRT"), SIGABRT }, + { njs_str("ALRM"), SIGALRM }, + { njs_str("CHLD"), SIGCHLD }, + { njs_str("CONT"), SIGCONT }, + { njs_str("FPE"), SIGFPE }, + { njs_str("HUP"), SIGHUP }, + { njs_str("ILL"), SIGILL }, + { njs_str("INT"), SIGINT }, + { njs_str("KILL"), SIGKILL }, + { njs_str("PIPE"), SIGPIPE }, + { njs_str("QUIT"), SIGQUIT }, + { njs_str("SEGV"), SIGSEGV }, + { njs_str("STOP"), SIGSTOP }, + { njs_str("TSTP"), SIGTSTP }, + { njs_str("TERM"), SIGTERM }, + { njs_str("TTIN"), SIGTTIN }, + { njs_str("TTOU"), SIGTTOU }, + { njs_str("USR1"), SIGUSR1 }, + { njs_str("USR2"), SIGUSR2 }, + { njs_null_str, 0 } +}; + + njs_inline njs_int_t njs_object_hash_init(njs_vm_t *vm, njs_lvlhsh_t *hash, const njs_object_init_t *init) @@ -1380,6 +1412,67 @@ njs_process_object_ppid(njs_vm_t *vm, njs_object_prop_t *prop, } +static njs_int_t +njs_ext_process_kill(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, + njs_index_t magic, njs_value_t *retval) +{ + int signal; + njs_str_t str; + njs_uint_t pid; + njs_value_t *arg; + njs_signal_entry_t *s; + + arg = njs_arg(args, nargs, 1); + if (!njs_value_is_number(arg)) { + njs_vm_type_error(vm, "\"pid\" is not a number"); + return NJS_ERROR; + } + + pid = njs_value_number(arg); + signal = SIGTERM; + + arg = njs_arg(args, nargs, 2); + if (njs_value_is_number(arg)) { + signal = njs_value_number(arg); + + } else if (njs_value_is_string(arg)) { + njs_value_string_get(arg, &str); + + if (str.length < 3 || memcmp(str.start, "SIG", 3) != 0) { + njs_vm_type_error(vm, "\"signal\" unknown value: \"%V\"", &str); + return NJS_ERROR; + } + + str.start += 3; + str.length -= 3; + + for (s = &njs_signals_table[0]; s->name.length != 0; s++) { + if (njs_strstr_eq(&str, &s->name)) { + signal = s->value; + break; + } + } + + if (s->name.length == 0) { + njs_vm_type_error(vm, "\"signal\" unknown value"); + return NJS_ERROR; + } + + } else if (!njs_value_is_undefined(arg)) { + njs_vm_type_error(vm, "\"signal\" invalid type"); + return NJS_ERROR; + } + + if (kill(pid, signal) < 0) { + njs_vm_error(vm, "kill failed with (%d:%s)", errno, strerror(errno)); + return NJS_ERROR; + } + + njs_set_boolean(retval, 1); + return NJS_OK; +} + + static const njs_object_prop_t njs_process_object_properties[] = { { @@ -1396,6 +1489,8 @@ static const njs_object_prop_t njs_process_object_properties[] = NJS_DECLARE_PROP_HANDLER("pid", njs_process_object_pid, 0, 0, 0), NJS_DECLARE_PROP_HANDLER("ppid", njs_process_object_ppid, 0, 0, 0), + + NJS_DECLARE_PROP_NATIVE("kill", njs_ext_process_kill, 2, 0), }; diff --git a/test/shell_test.exp b/test/shell_test.exp index 139daa17..b713ed09 100644 --- a/test/shell_test.exp +++ b/test/shell_test.exp @@ -423,6 +423,9 @@ njs_run {"-c" "console.log(process.pid)"} "\\d+" njs_run {"-c" "console.log(process.ppid)"} "\\d+" +njs_run {"-c" "console.log(process.kill(process.pid, 0))"} "true" +njs_run {"-c" "console.log(process.kill(process.pid, 'SIGCHLD'))"} "true" + # script args diff --git a/test/shell_test_njs.exp b/test/shell_test_njs.exp index fac0fe3a..9c15b81a 100644 --- a/test/shell_test_njs.exp +++ b/test/shell_test_njs.exp @@ -124,6 +124,8 @@ njs_run {"-c" "console.log(process.pid)"} "\\d+" njs_run {"-c" "console.log(process.ppid)"} "\\d+" +njs_run {"-c" "console.log(process.kill(process.pid, 0))"} "true" + # script args diff --git a/ts/njs_core.d.ts b/ts/njs_core.d.ts index 0e286f93..2f1d45d0 100644 --- a/ts/njs_core.d.ts +++ b/ts/njs_core.d.ts @@ -579,6 +579,11 @@ interface NjsProcess { readonly ppid: number; readonly argv: string[]; readonly env: NjsEnv; + + /** + * @since 0.8.8 + */ + kill(pid: number, signal?: string | number): true; } declare const process: NjsProcess; From noreply at nginx.com Wed Nov 6 16:17:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Wed, 6 Nov 2024 16:17:02 +0000 (UTC) Subject: [njs] Modules: fixed process.env object. Message-ID: <20241106161702.44D2F478D9@pubserv1.nginx> details: https://github.com/nginx/njs/commit/07545f1670fa6e0cbd6c4dd13244246265560852 branches: master commit: 07545f1670fa6e0cbd6c4dd13244246265560852 user: Dmitry Volyntsev date: Tue, 5 Nov 2024 18:19:48 -0800 description: Modules: fixed process.env object. Previously, it ignored changes to environment variables introduced with "env" directive. --- nginx/ngx_js.c | 4 ++ nginx/t/js_process.t | 84 ++++++++++++++++++++++++++++++++++ nginx/t/stream_js_process.t | 108 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 196 insertions(+) diff --git a/nginx/ngx_js.c b/nginx/ngx_js.c index 1ac53baa..20c8b2f3 100644 --- a/nginx/ngx_js.c +++ b/nginx/ngx_js.c @@ -3832,6 +3832,10 @@ ngx_js_init_conf_vm(ngx_conf_t *cf, ngx_js_loc_conf_t *conf, ngx_pool_cleanup_t *cln; ngx_js_named_path_t *import; + if (ngx_set_environment(cf->cycle, NULL) == NULL) { + return NGX_ERROR; + } + if (conf->preload_objects != NGX_CONF_UNSET_PTR) { if (ngx_js_init_preload_vm(cf, (ngx_js_loc_conf_t *)conf) != NGX_OK) { return NGX_ERROR; diff --git a/nginx/t/js_process.t b/nginx/t/js_process.t new file mode 100644 index 00000000..40c1fc1b --- /dev/null +++ b/nginx/t/js_process.t @@ -0,0 +1,84 @@ +#!/usr/bin/perl + +# (C) Dmitry Volyntsev +# (C) Nginx, Inc. + +# Tests for http njs module, process object. + +############################################################################### + +use warnings; +use strict; + +use Test::More; +use Socket qw/ CRLF /; + +BEGIN { use FindBin; chdir($FindBin::Bin); } + +use lib 'lib'; +use Test::Nginx; + +############################################################################### + +select STDERR; $| = 1; +select STDOUT; $| = 1; + +my $t = Test::Nginx->new()->has(qw/http/) + ->write_file_expand('nginx.conf', <<'EOF'); + +%%TEST_GLOBALS%% + +daemon off; + +events { +} + +env FOO=bar; +env BAR=baz; + +http { + %%TEST_GLOBALS_HTTP%% + + js_import test.js; + + server { + listen 127.0.0.1:8080; + server_name localhost; + + location /argv { + js_content test.argv; + } + + location /env { + js_content test.env; + } + } +} + +EOF + +$t->write_file('test.js', <= 0}`); + } + + function env(r) { + var e = process.env[r.args.var]; + r.return(200, e ? e : 'undefined'); + } + + export default { argv, env }; + +EOF + +$t->try_run('no njs process object')->plan(4); + +############################################################################### + +like(http_get('/argv'), qr/true true/, 'argv'); +like(http_get('/env?var=FOO'), qr/bar/, 'env FOO'); +like(http_get('/env?var=BAR'), qr/baz/, 'env BAR'); +like(http_get('/env?var=HOME'), qr/undefined/, 'env HOME'); + +############################################################################### diff --git a/nginx/t/stream_js_process.t b/nginx/t/stream_js_process.t new file mode 100644 index 00000000..85becd31 --- /dev/null +++ b/nginx/t/stream_js_process.t @@ -0,0 +1,108 @@ +#!/usr/bin/perl + +# (C) Dmitry Volyntsev +# (C) Nginx, Inc. + +# Tests for stream njs module, process object. + +############################################################################### + +use warnings; +use strict; + +use Test::More; + +BEGIN { use FindBin; chdir($FindBin::Bin); } + +use lib 'lib'; +use Test::Nginx; +use Test::Nginx::Stream qw/ stream /; + +############################################################################### + +select STDERR; $| = 1; +select STDOUT; $| = 1; + +my $t = Test::Nginx->new()->has(qw/stream stream_return/) + ->write_file_expand('nginx.conf', <<'EOF'); + +%%TEST_GLOBALS%% + +daemon off; + +events { +} + +env FOO=bar; +env BAR=baz; + +stream { + %%TEST_GLOBALS_STREAM%% + + js_import test.js; + + js_set $env_foo test.env_foo; + js_set $env_bar test.env_bar; + js_set $env_home test.env_home; + js_set $argv test.argv; + + server { + listen 127.0.0.1:8081; + return $env_foo; + } + + server { + listen 127.0.0.1:8082; + return $env_bar; + } + + server { + listen 127.0.0.1:8083; + return $env_home; + } + + server { + listen 127.0.0.1:8084; + return $argv; + } +} + +EOF + +$t->write_file('test.js', <= 0}`; + } + + export default { env_foo, env_bar, env_home, argv }; + +EOF + +$t->try_run('no njs stream session object')->plan(4); + +############################################################################### + +is(stream('127.0.0.1:' . port(8081))->read(), 'bar', 'env.FOO'); +is(stream('127.0.0.1:' . port(8082))->read(), 'baz', 'env.BAR'); +is(stream('127.0.0.1:' . port(8083))->read(), 'undefined', 'env HOME'); +is(stream('127.0.0.1:' . port(8084))->read(), 'true true', 'argv'); + +############################################################################### From noreply at nginx.com Wed Nov 6 16:17:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Wed, 6 Nov 2024 16:17:02 +0000 (UTC) Subject: [njs] QuickJS: reworked process object. Message-ID: <20241106161702.369E7478D4@pubserv1.nginx> details: https://github.com/nginx/njs/commit/98fff325573c3554203e7cbc467066f7c8a45c2c branches: master commit: 98fff325573c3554203e7cbc467066f7c8a45c2c user: Dmitry Volyntsev date: Thu, 31 Oct 2024 17:47:41 -0700 description: QuickJS: reworked process object. --- external/njs_shell.c | 122 +++--------------------------- nginx/ngx_js.c | 9 +++ src/qjs.c | 204 ++++++++++++++++++++++++++++++++++++++++----------- src/qjs.h | 2 +- 4 files changed, 182 insertions(+), 155 deletions(-) diff --git a/external/njs_shell.c b/external/njs_shell.c index 30d4d8c6..776c2536 100644 --- a/external/njs_shell.c +++ b/external/njs_shell.c @@ -1901,15 +1901,17 @@ njs_qjs_clear_timeout(JSContext *ctx, JSValueConst this_val, int argc, } +static JSValue +njs_qjs_console_to_string_tag(JSContext *ctx, JSValueConst this_val) +{ + return JS_NewString(ctx, "Console"); +} + + static JSValue njs_qjs_process_getter(JSContext *ctx, JSValueConst this_val) { - char **ep; - JSAtom atom; - JSValue obj, val, str, name, env; - njs_int_t ret; - njs_uint_t i; - const char *entry, *value; + JSValue obj; njs_console_t *console; console = JS_GetRuntimeOpaque(JS_GetRuntime(ctx)); @@ -1918,106 +1920,8 @@ njs_qjs_process_getter(JSContext *ctx, JSValueConst this_val) return JS_DupValue(ctx, console->process); } - obj = JS_NewObject(ctx); - if (JS_IsException(obj)) { - return JS_EXCEPTION; - } - - ret = qjs_set_to_string_tag(ctx, obj, "process"); - if (ret == -1) { - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } - - val = JS_NewArray(ctx); - if (JS_IsException(val)) { - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } - - ret = JS_SetPropertyStr(ctx, obj, "argv", val); - if (ret == -1) { - JS_FreeValue(ctx, obj); - JS_FreeValue(ctx, val); - return JS_EXCEPTION; - } - - for (i = 0; i < console->argc; i++) { - str = JS_NewStringLen(ctx, console->argv[i], - njs_strlen(console->argv[i])); - if (JS_IsException(str)) { - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } - - ret = JS_DefinePropertyValueUint32(ctx, val, i, str, JS_PROP_C_W_E); - if (ret == -1) { - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } - } - - env = JS_NewObject(ctx); + obj = qjs_process_object(ctx, console->argc, (const char **) console->argv); if (JS_IsException(obj)) { - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } - - ret = JS_SetPropertyStr(ctx, obj, "env", env); - if (ret == -1) { - JS_FreeValue(ctx, obj); - JS_FreeValue(ctx, env); - return JS_EXCEPTION; - } - - ep = environ; - - while (*ep != NULL) { - entry = *ep++; - - value = (const char *) njs_strchr(entry, '='); - if (njs_slow_path(value == NULL)) { - continue; - } - - str = JS_UNDEFINED; - name = JS_NewStringLen(ctx, entry, value - entry); - if (JS_IsException(name)) { - goto error; - } - - str = JS_NewStringLen(ctx, value, njs_strlen(value)); - if (JS_IsException(str)) { - goto error; - } - - atom = JS_ValueToAtom(ctx, name); - if (atom == JS_ATOM_NULL) { - goto error; - } - - ret = JS_DefinePropertyValue(ctx, env, atom, str, JS_PROP_C_W_E); - JS_FreeAtom(ctx, atom); - if (ret == -1) { -error: - JS_FreeValue(ctx, name); - JS_FreeValue(ctx, str); - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } - - JS_FreeValue(ctx, name); - } - - ret = JS_SetPropertyStr(ctx, obj, "pid", JS_NewInt32(ctx, getpid())); - if (ret == -1) { - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } - - ret = JS_SetPropertyStr(ctx, obj, "ppid", JS_NewInt32(ctx, getppid())); - if (ret == -1) { - JS_FreeValue(ctx, obj); return JS_EXCEPTION; } @@ -2583,6 +2487,7 @@ static const JSCFunctionListEntry njs_qjs_global_proto[] = { static const JSCFunctionListEntry njs_qjs_console_proto[] = { + JS_CGETSET_DEF("[Symbol.toStringTag]", njs_qjs_console_to_string_tag, NULL), JS_CFUNC_MAGIC_DEF("error", 0, njs_qjs_console_log, NJS_LOG_ERROR), JS_CFUNC_MAGIC_DEF("info", 0, njs_qjs_console_log, NJS_LOG_INFO), JS_CFUNC_MAGIC_DEF("log", 0, njs_qjs_console_log, NJS_LOG_INFO), @@ -2759,13 +2664,6 @@ njs_engine_qjs_init(njs_engine_t *engine, njs_opts_t *opts) goto done; } - ret = qjs_set_to_string_tag(ctx, obj, "Console"); - if (ret == -1) { - njs_stderror("qjs_set_to_string_tag() failed\n"); - ret = NJS_ERROR; - goto done; - } - JS_SetOpaque(obj, &njs_console); JS_SetPropertyFunctionList(ctx, obj, njs_qjs_console_proto, diff --git a/nginx/ngx_js.c b/nginx/ngx_js.c index f70288cf..1ac53baa 100644 --- a/nginx/ngx_js.c +++ b/nginx/ngx_js.c @@ -70,6 +70,7 @@ static ngx_int_t ngx_engine_qjs_pending(ngx_engine_t *engine); static ngx_int_t ngx_engine_qjs_string(ngx_engine_t *e, njs_opaque_value_t *value, ngx_str_t *str); +static JSValue ngx_qjs_process_getter(JSContext *ctx, JSValueConst this_val); static JSValue ngx_qjs_ext_set_timeout(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int immediate); static JSValue ngx_qjs_ext_clear_timeout(JSContext *cx, JSValueConst this_val, @@ -457,6 +458,7 @@ static const JSCFunctionListEntry ngx_qjs_ext_console[] = { static const JSCFunctionListEntry ngx_qjs_ext_global[] = { + JS_CGETSET_DEF("process", ngx_qjs_process_getter, NULL), JS_CFUNC_MAGIC_DEF("setTimeout", 1, ngx_qjs_ext_set_timeout, 0), JS_CFUNC_MAGIC_DEF("setImmediate", 1, ngx_qjs_ext_set_timeout, 1), JS_CFUNC_DEF("clearTimeout", 1, ngx_qjs_ext_clear_timeout), @@ -1566,6 +1568,13 @@ ngx_qjs_clear_timer(ngx_qjs_event_t *event) } +static JSValue +ngx_qjs_process_getter(JSContext *cx, JSValueConst this_val) +{ + return qjs_process_object(cx, ngx_argc, (const char **) ngx_argv); +} + + static JSValue ngx_qjs_ext_set_timeout(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int immediate) diff --git a/src/qjs.c b/src/qjs.c index 3d378fcc..b529f1bb 100644 --- a/src/qjs.c +++ b/src/qjs.c @@ -7,14 +7,42 @@ #include #include /* NJS_VERSION */ +#include +#include + + +extern char **environ; + static JSValue qjs_njs_getter(JSContext *ctx, JSValueConst this_val); +static JSValue qjs_njs_to_string_tag(JSContext *ctx, JSValueConst this_val); +static JSValue qjs_process_to_string_tag(JSContext *ctx, JSValueConst this_val); +static JSValue qjs_process_argv(JSContext *ctx, JSValueConst this_val); +static JSValue qjs_process_env(JSContext *ctx, JSValueConst this_val); +static JSValue qjs_process_pid(JSContext *ctx, JSValueConst this_val); +static JSValue qjs_process_ppid(JSContext *ctx, JSValueConst this_val); static const JSCFunctionListEntry qjs_global_proto[] = { JS_CGETSET_DEF("njs", qjs_njs_getter, NULL), }; +static const JSCFunctionListEntry qjs_njs_proto[] = { + JS_CGETSET_DEF("[Symbol.toStringTag]", qjs_njs_to_string_tag, NULL), + JS_PROP_STRING_DEF("version", NJS_VERSION, JS_PROP_C_W_E), + JS_PROP_INT32_DEF("version_number", NJS_VERSION_NUMBER, + JS_PROP_C_W_E), + JS_PROP_STRING_DEF("engine", "QuickJS", JS_PROP_C_W_E), +}; + +static const JSCFunctionListEntry qjs_process_proto[] = { + JS_CGETSET_DEF("[Symbol.toStringTag]", qjs_process_to_string_tag, NULL), + JS_CGETSET_DEF("argv", qjs_process_argv, NULL), + JS_CGETSET_DEF("env", qjs_process_env, NULL), + JS_CGETSET_DEF("pid", qjs_process_pid, NULL), + JS_CGETSET_DEF("ppid", qjs_process_ppid, NULL), +}; + JSContext * qjs_new_context(JSRuntime *rt, qjs_module_t **addons) @@ -91,7 +119,6 @@ qjs_new_context(JSRuntime *rt, qjs_module_t **addons) static JSValue qjs_njs_getter(JSContext *ctx, JSValueConst this_val) { - int ret; JSValue obj; obj = JS_NewObject(ctx); @@ -99,73 +126,166 @@ qjs_njs_getter(JSContext *ctx, JSValueConst this_val) return JS_EXCEPTION; } - ret = qjs_set_to_string_tag(ctx, obj, "njs"); - if (ret == -1) { - JS_FreeValue(ctx, obj); + JS_SetPropertyFunctionList(ctx, obj, qjs_njs_proto, + njs_nitems(qjs_njs_proto)); + + return obj; +} + + +static JSValue +qjs_njs_to_string_tag(JSContext *ctx, JSValueConst this_val) +{ + return JS_NewString(ctx, "njs"); +} + + +static JSValue +qjs_process_to_string_tag(JSContext *ctx, JSValueConst this_val) +{ + return JS_NewString(ctx, "process"); +} + + +static JSValue +qjs_process_argv(JSContext *ctx, JSValueConst this_val) +{ + int i, ret, argc; + JSValue val, str; + const char **argv; + + val = JS_GetPropertyStr(ctx, this_val, "argc"); + if (JS_IsException(val)) { return JS_EXCEPTION; } - ret = JS_SetPropertyStr(ctx, obj, "version_number", - JS_NewInt32(ctx, NJS_VERSION_NUMBER)); - if (ret == -1) { - JS_FreeValue(ctx, obj); + if (JS_ToInt32(ctx, &argc, val) < 0) { return JS_EXCEPTION; } - ret = JS_SetPropertyStr(ctx, obj, "version", - JS_NewString(ctx, NJS_VERSION)); - if (ret == -1) { - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; + argv = JS_GetOpaque(this_val, JS_GetClassID(this_val)); + if (argv == NULL) { + return JS_NewArray(ctx); } - ret = JS_SetPropertyStr(ctx, obj, "engine", JS_NewString(ctx, "QuickJS")); - if (ret == -1) { - JS_FreeValue(ctx, obj); + val = JS_NewArray(ctx); + if (JS_IsException(val)) { return JS_EXCEPTION; } - return obj; + for (i = 0; i < argc; i++) { + str = JS_NewStringLen(ctx, argv[i], njs_strlen(argv[i])); + if (JS_IsException(str)) { + JS_FreeValue(ctx, val); + return JS_EXCEPTION; + } + + ret = JS_DefinePropertyValueUint32(ctx, val, i, str, JS_PROP_C_W_E); + if (ret < 0) { + JS_FreeValue(ctx, str); + JS_FreeValue(ctx, val); + return JS_EXCEPTION; + } + } + + return val; } -int -qjs_set_to_string_tag(JSContext *ctx, JSValueConst val, const char *tag) +static JSValue +qjs_process_env(JSContext *ctx, JSValueConst this_val) { - int ret; - JSAtom atom; - JSValue global_obj, symbol, toStringTag; - - global_obj = JS_GetGlobalObject(ctx); + int ret; + char **ep; + JSValue obj; + JSAtom atom; + JSValue str, name; + const char *entry, *value; - symbol = JS_GetPropertyStr(ctx, global_obj, "Symbol"); - JS_FreeValue(ctx, global_obj); - if (JS_IsException(symbol)) { - return -1; + obj = JS_NewObject(ctx); + if (JS_IsException(obj)) { + return JS_EXCEPTION; } - toStringTag = JS_GetPropertyStr(ctx, symbol, "toStringTag"); - if (JS_IsException(toStringTag)) { - JS_FreeValue(ctx, symbol); - return -1; + ep = environ; + + while (*ep != NULL) { + entry = *ep++; + + value = (const char *) njs_strchr(entry, '='); + if (value == NULL) { + continue; + } + + str = JS_UNDEFINED; + name = JS_NewStringLen(ctx, entry, value - entry); + if (JS_IsException(name)) { + goto error; + } + + value++; + + str = JS_NewStringLen(ctx, value, njs_strlen(value)); + if (JS_IsException(str)) { + goto error; + } + + atom = JS_ValueToAtom(ctx, name); + if (atom == JS_ATOM_NULL) { + goto error; + } + + ret = JS_DefinePropertyValue(ctx, obj, atom, str, JS_PROP_C_W_E); + JS_FreeAtom(ctx, atom); + if (ret < 0) { +error: + JS_FreeValue(ctx, name); + JS_FreeValue(ctx, str); + return JS_EXCEPTION; + } + + JS_FreeValue(ctx, name); } - atom = JS_ValueToAtom(ctx, toStringTag); + return obj; +} - JS_FreeValue(ctx, symbol); - JS_FreeValue(ctx, toStringTag); - if (atom == JS_ATOM_NULL) { - JS_ThrowInternalError(ctx, "failed to get atom"); - return -1; +static JSValue +qjs_process_pid(JSContext *ctx, JSValueConst this_val) +{ + return JS_NewInt32(ctx, getpid()); +} + + +static JSValue +qjs_process_ppid(JSContext *ctx, JSValueConst this_val) +{ + return JS_NewInt32(ctx, getppid()); +} + + +JSValue +qjs_process_object(JSContext *ctx, int argc, const char **argv) +{ + JSValue obj; + + obj = JS_NewObject(ctx); + if (JS_IsException(obj)) { + return JS_EXCEPTION; } - ret = JS_DefinePropertyValue(ctx, val, atom, JS_NewString(ctx, tag), - JS_PROP_C_W_E); + JS_SetPropertyFunctionList(ctx, obj, qjs_process_proto, + njs_nitems(qjs_process_proto)); - JS_FreeAtom(ctx, atom); + JS_SetOpaque(obj, argv); - return ret; + if (JS_SetPropertyStr(ctx, obj, "argc", JS_NewInt32(ctx, argc)) < 0) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + + return obj; } diff --git a/src/qjs.h b/src/qjs.h index 2418e6cd..81769abb 100644 --- a/src/qjs.h +++ b/src/qjs.h @@ -70,7 +70,7 @@ typedef struct { const qjs_buffer_encoding_t *qjs_buffer_encoding(JSContext *ctx, JSValueConst value, JS_BOOL thrw); -int qjs_set_to_string_tag(JSContext *ctx, JSValueConst val, const char *tag); +JSValue qjs_process_object(JSContext *ctx, int argc, const char **argv); typedef struct { int tag; From noreply at nginx.com Wed Nov 6 21:16:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Wed, 6 Nov 2024 21:16:02 +0000 (UTC) Subject: [njs] FS: removed fs.promises.readSync() added by mistake. Message-ID: <20241106211602.510E3478D4@pubserv1.nginx> details: https://github.com/nginx/njs/commit/1a6cc6d2b82e134f175650988949d25061ac9783 branches: master commit: 1a6cc6d2b82e134f175650988949d25061ac9783 user: Dmitry Volyntsev date: Tue, 5 Nov 2024 19:01:52 -0800 description: FS: removed fs.promises.readSync() added by mistake. This closes #810 issue on Github. --- external/njs_fs_module.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/external/njs_fs_module.c b/external/njs_fs_module.c index f5221883..5f8924ae 100644 --- a/external/njs_fs_module.c +++ b/external/njs_fs_module.c @@ -395,17 +395,6 @@ static njs_external_t njs_ext_fs_promises[] = { } }, - { - .flags = NJS_EXTERN_METHOD, - .name.string = njs_str("readSync"), - .writable = 1, - .configurable = 1, - .u.method = { - .native = njs_fs_read, - .magic8 = NJS_FS_PROMISE, - } - }, - { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("readdir"), From noreply at nginx.com Thu Nov 7 15:58:03 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Thu, 7 Nov 2024 15:58:03 +0000 (UTC) Subject: [nginx] Core: inheritance of non-reusable shared memory zones. Message-ID: <20241107155803.3216746C6E@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/1524c5e3fc9cbff6ef97ab97017a4b73bd85694b branches: master commit: 1524c5e3fc9cbff6ef97ab97017a4b73bd85694b user: Ruslan Ermilov date: Fri, 3 Nov 2017 22:22:21 +0300 description: Core: inheritance of non-reusable shared memory zones. When re-creating a non-reusable zone, make the pointer to the old zone available during the new zone initialization. --- src/core/ngx_cycle.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/core/ngx_cycle.c b/src/core/ngx_cycle.c index 6978c3e43..a75bdf878 100644 --- a/src/core/ngx_cycle.c +++ b/src/core/ngx_cycle.c @@ -38,7 +38,7 @@ static ngx_connection_t dumb; ngx_cycle_t * ngx_init_cycle(ngx_cycle_t *old_cycle) { - void *rv; + void *rv, *data; char **senv; ngx_uint_t i, n; ngx_log_t *log; @@ -438,6 +438,8 @@ ngx_init_cycle(ngx_cycle_t *old_cycle) opart = &old_cycle->shared_memory.part; oshm_zone = opart->elts; + data = NULL; + for (n = 0; /* void */ ; n++) { if (n >= opart->nelts) { @@ -461,9 +463,13 @@ ngx_init_cycle(ngx_cycle_t *old_cycle) continue; } + if (shm_zone[i].tag == oshm_zone[n].tag && shm_zone[i].noreuse) { + data = oshm_zone[n].data; + break; + } + if (shm_zone[i].tag == oshm_zone[n].tag - && shm_zone[i].shm.size == oshm_zone[n].shm.size - && !shm_zone[i].noreuse) + && shm_zone[i].shm.size == oshm_zone[n].shm.size) { shm_zone[i].shm.addr = oshm_zone[n].shm.addr; #if (NGX_WIN32) @@ -490,7 +496,7 @@ ngx_init_cycle(ngx_cycle_t *old_cycle) goto failed; } - if (shm_zone[i].init(&shm_zone[i], NULL) != NGX_OK) { + if (shm_zone[i].init(&shm_zone[i], data) != NGX_OK) { goto failed; } From noreply at nginx.com Thu Nov 7 15:58:03 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Thu, 7 Nov 2024 15:58:03 +0000 (UTC) Subject: [nginx] Upstream: per-upstream resolver. Message-ID: <20241107155803.3EF6046C70@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/ea4654550ab021b5576c03b708079e3ce3e5d9ed branches: master commit: ea4654550ab021b5576c03b708079e3ce3e5d9ed user: Vladimir Homutov date: Fri, 18 Oct 2019 16:33:15 +0300 description: Upstream: per-upstream resolver. The "resolver" and "resolver_timeout" directives can now be specified directly in the "upstream" block. --- src/http/ngx_http_upstream.c | 51 +++++++++++++++++++++++++++ src/http/ngx_http_upstream_round_robin.c | 12 +++---- src/stream/ngx_stream_upstream.c | 52 ++++++++++++++++++++++++++++ src/stream/ngx_stream_upstream_round_robin.c | 12 +++---- 4 files changed, 115 insertions(+), 12 deletions(-) diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c index d090d1618..82a230024 100644 --- a/src/http/ngx_http_upstream.c +++ b/src/http/ngx_http_upstream.c @@ -169,6 +169,10 @@ static ngx_int_t ngx_http_upstream_cookie_variable(ngx_http_request_t *r, static char *ngx_http_upstream(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy); static char *ngx_http_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +#if (NGX_HTTP_UPSTREAM_ZONE) +static char *ngx_http_upstream_resolver(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +#endif static ngx_int_t ngx_http_upstream_set_local(ngx_http_request_t *r, ngx_http_upstream_t *u, ngx_http_upstream_local_t *local); @@ -339,6 +343,24 @@ static ngx_command_t ngx_http_upstream_commands[] = { 0, NULL }, +#if (NGX_HTTP_UPSTREAM_ZONE) + + { ngx_string("resolver"), + NGX_HTTP_UPS_CONF|NGX_CONF_1MORE, + ngx_http_upstream_resolver, + NGX_HTTP_SRV_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("resolver_timeout"), + NGX_HTTP_UPS_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_upstream_srv_conf_t, resolver_timeout), + NULL }, + +#endif + ngx_null_command }; @@ -6434,6 +6456,32 @@ not_supported: } +#if (NGX_HTTP_UPSTREAM_ZONE) + +static char * +ngx_http_upstream_resolver(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_upstream_srv_conf_t *uscf = conf; + + ngx_str_t *value; + + if (uscf->resolver) { + return "is duplicate"; + } + + value = cf->args->elts; + + uscf->resolver = ngx_resolver_create(cf, &value[1], cf->args->nelts - 1); + if (uscf->resolver == NULL) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + +#endif + + ngx_http_upstream_srv_conf_t * ngx_http_upstream_add(ngx_conf_t *cf, ngx_url_t *u, ngx_uint_t flags) { @@ -6515,6 +6563,9 @@ ngx_http_upstream_add(ngx_conf_t *cf, ngx_url_t *u, ngx_uint_t flags) uscf->line = cf->conf_file->line; uscf->port = u->port; uscf->no_port = u->no_port; +#if (NGX_HTTP_UPSTREAM_ZONE) + uscf->resolver_timeout = NGX_CONF_UNSET_MSEC; +#endif if (u->naddrs == 1 && (u->port || u->family == AF_UNIX)) { uscf->servers = ngx_array_create(cf->pool, 1, diff --git a/src/http/ngx_http_upstream_round_robin.c b/src/http/ngx_http_upstream_round_robin.c index 5dbd4e626..304494b3c 100644 --- a/src/http/ngx_http_upstream_round_robin.c +++ b/src/http/ngx_http_upstream_round_robin.c @@ -97,15 +97,15 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf, clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); - us->resolver = clcf->resolver; - us->resolver_timeout = clcf->resolver_timeout; + if (us->resolver == NULL) { + us->resolver = clcf->resolver; + } /* - * Without "resolver_timeout" in http{}, the value is unset. - * Even if we set it in ngx_http_core_merge_loc_conf(), it's - * still dependent on the module order and unreliable. + * Without "resolver_timeout" in http{} the merged value is unset. */ - ngx_conf_init_msec_value(us->resolver_timeout, 30000); + ngx_conf_merge_msec_value(us->resolver_timeout, + clcf->resolver_timeout, 30000); if (resolve && (us->resolver == NULL diff --git a/src/stream/ngx_stream_upstream.c b/src/stream/ngx_stream_upstream.c index be4f13016..6526d3c22 100644 --- a/src/stream/ngx_stream_upstream.c +++ b/src/stream/ngx_stream_upstream.c @@ -22,6 +22,11 @@ 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); +#if (NGX_STREAM_UPSTREAM_ZONE) +static char *ngx_stream_upstream_resolver(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +#endif + 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); @@ -42,6 +47,24 @@ static ngx_command_t ngx_stream_upstream_commands[] = { 0, NULL }, +#if (NGX_STREAM_UPSTREAM_ZONE) + + { ngx_string("resolver"), + NGX_STREAM_UPS_CONF|NGX_CONF_1MORE, + ngx_stream_upstream_resolver, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("resolver_timeout"), + NGX_STREAM_UPS_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_upstream_srv_conf_t, resolver_timeout), + NULL }, + +#endif + ngx_null_command }; @@ -661,6 +684,32 @@ not_supported: } +#if (NGX_STREAM_UPSTREAM_ZONE) + +static char * +ngx_stream_upstream_resolver(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_stream_upstream_srv_conf_t *uscf = conf; + + ngx_str_t *value; + + if (uscf->resolver) { + return "is duplicate"; + } + + value = cf->args->elts; + + uscf->resolver = ngx_resolver_create(cf, &value[1], cf->args->nelts - 1); + if (uscf->resolver == NULL) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + +#endif + + ngx_stream_upstream_srv_conf_t * ngx_stream_upstream_add(ngx_conf_t *cf, ngx_url_t *u, ngx_uint_t flags) { @@ -739,6 +788,9 @@ ngx_stream_upstream_add(ngx_conf_t *cf, ngx_url_t *u, ngx_uint_t flags) uscf->line = cf->conf_file->line; uscf->port = u->port; uscf->no_port = u->no_port; +#if (NGX_STREAM_UPSTREAM_ZONE) + uscf->resolver_timeout = NGX_CONF_UNSET_MSEC; +#endif if (u->naddrs == 1 && (u->port || u->family == AF_UNIX)) { uscf->servers = ngx_array_create(cf->pool, 1, diff --git a/src/stream/ngx_stream_upstream_round_robin.c b/src/stream/ngx_stream_upstream_round_robin.c index e1903b3d2..5b5f20db7 100644 --- a/src/stream/ngx_stream_upstream_round_robin.c +++ b/src/stream/ngx_stream_upstream_round_robin.c @@ -104,15 +104,15 @@ ngx_stream_upstream_init_round_robin(ngx_conf_t *cf, cscf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_core_module); - us->resolver = cscf->resolver; - us->resolver_timeout = cscf->resolver_timeout; + if (us->resolver == NULL) { + us->resolver = cscf->resolver; + } /* - * Without "resolver_timeout" in stream{}, the value is unset. - * Even if we set it in ngx_stream_core_merge_srv_conf(), it's - * still dependent on the module order and unreliable. + * Without "resolver_timeout" in stream{} the merged value is unset. */ - ngx_conf_init_msec_value(us->resolver_timeout, 30000); + ngx_conf_merge_msec_value(us->resolver_timeout, + cscf->resolver_timeout, 30000); if (resolve && (us->resolver == NULL From noreply at nginx.com Thu Nov 7 15:58:03 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Thu, 7 Nov 2024 15:58:03 +0000 (UTC) Subject: [nginx] Upstream: construct upstream peers from DNS SRV records. Message-ID: <20241107155803.2D83246C6D@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/9fe119b431c957824d7bed75fce47dfbda74ca33 branches: master commit: 9fe119b431c957824d7bed75fce47dfbda74ca33 user: Dmitry Volyntsev date: Thu, 17 Mar 2016 18:42:31 +0300 description: Upstream: construct upstream peers from DNS SRV records. --- src/http/modules/ngx_http_upstream_zone_module.c | 183 ++++++++++++++++++---- src/http/ngx_http_upstream.c | 38 +++++ src/http/ngx_http_upstream.h | 3 +- src/http/ngx_http_upstream_round_robin.c | 12 +- src/http/ngx_http_upstream_round_robin.h | 3 +- src/stream/ngx_stream_upstream.c | 45 +++++- src/stream/ngx_stream_upstream.h | 4 +- src/stream/ngx_stream_upstream_round_robin.c | 12 +- src/stream/ngx_stream_upstream_round_robin.h | 3 +- src/stream/ngx_stream_upstream_zone_module.c | 184 +++++++++++++++++++---- 10 files changed, 420 insertions(+), 67 deletions(-) diff --git a/src/http/modules/ngx_http_upstream_zone_module.c b/src/http/modules/ngx_http_upstream_zone_module.c index 355df39ab..e3ede9268 100644 --- a/src/http/modules/ngx_http_upstream_zone_module.c +++ b/src/http/modules/ngx_http_upstream_zone_module.c @@ -359,6 +359,18 @@ ngx_http_upstream_zone_copy_peer(ngx_http_upstream_rr_peers_t *peers, dst->host->name.len = src->host->name.len; ngx_memcpy(dst->host->name.data, src->host->name.data, src->host->name.len); + + if (src->host->service.len) { + dst->host->service.data = ngx_slab_alloc_locked(pool, + src->host->service.len); + if (dst->host->service.data == NULL) { + goto failed; + } + + dst->host->service.len = src->host->service.len; + ngx_memcpy(dst->host->service.data, src->host->service.data, + src->host->service.len); + } } } @@ -367,6 +379,10 @@ ngx_http_upstream_zone_copy_peer(ngx_http_upstream_rr_peers_t *peers, failed: if (dst->host) { + if (dst->host->name.data) { + ngx_slab_free_locked(pool, dst->host->name.data); + } + ngx_slab_free_locked(pool, dst->host); } @@ -510,6 +526,7 @@ ngx_http_upstream_zone_resolve_timer(ngx_event_t *event) ctx->handler = ngx_http_upstream_zone_resolve_handler; ctx->data = host; ctx->timeout = uscf->resolver_timeout; + ctx->service = host->service; ctx->cancelable = 1; if (ngx_resolve_name(ctx) == NGX_OK) { @@ -522,15 +539,28 @@ retry: } +#define ngx_http_upstream_zone_addr_marked(addr) \ + ((uintptr_t) (addr)->sockaddr & 1) + +#define ngx_http_upstream_zone_mark_addr(addr) \ + (addr)->sockaddr = (struct sockaddr *) ((uintptr_t) (addr)->sockaddr | 1) + +#define ngx_http_upstream_zone_unmark_addr(addr) \ + (addr)->sockaddr = \ + (struct sockaddr *) ((uintptr_t) (addr)->sockaddr & ~((uintptr_t) 1)) + static void ngx_http_upstream_zone_resolve_handler(ngx_resolver_ctx_t *ctx) { time_t now; + u_short min_priority; in_port_t port; + ngx_str_t *server; ngx_msec_t timer; - ngx_uint_t i, j; + ngx_uint_t i, j, backup, addr_backup; ngx_event_t *event; ngx_resolver_addr_t *addr; + ngx_resolver_srv_name_t *srv; ngx_http_upstream_host_t *host; ngx_http_upstream_rr_peer_t *peer, *template, **peerp; ngx_http_upstream_rr_peers_t *peers; @@ -546,11 +576,32 @@ ngx_http_upstream_zone_resolve_handler(ngx_resolver_ctx_t *ctx) now = ngx_time(); + for (i = 0; i < ctx->nsrvs; i++) { + srv = &ctx->srvs[i]; + + if (srv->state) { + ngx_log_error(NGX_LOG_ERR, event->log, 0, + "%V could not be resolved (%i: %s) " + "while resolving service %V of %V", + &srv->name, srv->state, + ngx_resolver_strerror(srv->state), &ctx->service, + &ctx->name); + } + } + if (ctx->state) { - ngx_log_error(NGX_LOG_ERR, event->log, 0, - "%V could not be resolved (%i: %s)", - &ctx->name, ctx->state, - ngx_resolver_strerror(ctx->state)); + if (ctx->service.len) { + ngx_log_error(NGX_LOG_ERR, event->log, 0, + "service %V of %V could not be resolved (%i: %s)", + &ctx->service, &ctx->name, ctx->state, + ngx_resolver_strerror(ctx->state)); + + } else { + ngx_log_error(NGX_LOG_ERR, event->log, 0, + "%V could not be resolved (%i: %s)", + &ctx->name, ctx->state, + ngx_resolver_strerror(ctx->state)); + } if (ctx->state != NGX_RESOLVE_NXDOMAIN) { ngx_http_upstream_rr_peers_unlock(peers); @@ -566,6 +617,13 @@ ngx_http_upstream_zone_resolve_handler(ngx_resolver_ctx_t *ctx) ctx->naddrs = 0; } + backup = 0; + min_priority = 65535; + + for (i = 0; i < ctx->naddrs; i++) { + min_priority = ngx_min(ctx->addrs[i].priority, min_priority); + } + #if (NGX_DEBUG) { u_char text[NGX_SOCKADDR_STRLEN]; @@ -573,14 +631,20 @@ ngx_http_upstream_zone_resolve_handler(ngx_resolver_ctx_t *ctx) for (i = 0; i < ctx->naddrs; i++) { len = ngx_sock_ntop(ctx->addrs[i].sockaddr, ctx->addrs[i].socklen, - text, NGX_SOCKADDR_STRLEN, 0); + text, NGX_SOCKADDR_STRLEN, 1); - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, event->log, 0, - "name %V was resolved to %*s", &host->name, len, text); + ngx_log_debug7(NGX_LOG_DEBUG_HTTP, event->log, 0, + "name %V was resolved to %*s " + "s:\"%V\" n:\"%V\" w:%d %s", + &host->name, len, text, &host->service, + &ctx->addrs[i].name, ctx->addrs[i].weight, + ctx->addrs[i].priority != min_priority ? "backup" : ""); } } #endif +again: + for (peerp = &peers->peer; *peerp; /* void */ ) { peer = *peerp; @@ -592,14 +656,39 @@ ngx_http_upstream_zone_resolve_handler(ngx_resolver_ctx_t *ctx) addr = &ctx->addrs[j]; - if (addr->name.len == 0 - && ngx_cmp_sockaddr(peer->sockaddr, peer->socklen, - addr->sockaddr, addr->socklen, 0) - == NGX_OK) + addr_backup = (addr->priority != min_priority); + if (addr_backup != backup) { + continue; + } + + if (ngx_http_upstream_zone_addr_marked(addr)) { + continue; + } + + if (ngx_cmp_sockaddr(peer->sockaddr, peer->socklen, + addr->sockaddr, addr->socklen, + host->service.len != 0) + != NGX_OK) { - addr->name.len = 1; - goto next; + continue; + } + + if (host->service.len) { + if (addr->name.len != peer->server.len + || ngx_strncmp(addr->name.data, peer->server.data, + addr->name.len)) + { + continue; + } + + if (template->weight == 1 && addr->weight != peer->weight) { + continue; + } } + + ngx_http_upstream_zone_mark_addr(addr); + + goto next; } *peerp = peer->next; @@ -618,8 +707,13 @@ ngx_http_upstream_zone_resolve_handler(ngx_resolver_ctx_t *ctx) addr = &ctx->addrs[i]; - if (addr->name.len == 1) { - addr->name.len = 0; + addr_backup = (addr->priority != min_priority); + if (addr_backup != backup) { + continue; + } + + if (ngx_http_upstream_zone_addr_marked(addr)) { + ngx_http_upstream_zone_unmark_addr(addr); continue; } @@ -631,21 +725,14 @@ ngx_http_upstream_zone_resolve_handler(ngx_resolver_ctx_t *ctx) ngx_log_error(NGX_LOG_ERR, event->log, 0, "cannot add new server to upstream \"%V\", " "memory exhausted", peers->name); - break; + goto done; } ngx_memcpy(peer->sockaddr, addr->sockaddr, addr->socklen); - port = ((struct sockaddr_in *) template->sockaddr)->sin_port; - - switch (peer->sockaddr->sa_family) { -#if (NGX_HAVE_INET6) - case AF_INET6: - ((struct sockaddr_in6 *) peer->sockaddr)->sin6_port = port; - break; -#endif - default: /* AF_INET */ - ((struct sockaddr_in *) peer->sockaddr)->sin_port = port; + if (host->service.len == 0) { + port = ngx_inet_get_port(template->sockaddr); + ngx_inet_set_port(peer->sockaddr, port); } peer->socklen = addr->socklen; @@ -654,9 +741,30 @@ ngx_http_upstream_zone_resolve_handler(ngx_resolver_ctx_t *ctx) peer->name.data, NGX_SOCKADDR_STRLEN, 1); peer->host = template->host; - peer->server = template->server; - peer->weight = template->weight; + server = host->service.len ? &addr->name : &template->server; + + peer->server.data = ngx_slab_alloc(peers->shpool, server->len); + if (peer->server.data == NULL) { + ngx_http_upstream_rr_peer_free(peers, peer); + + ngx_log_error(NGX_LOG_ERR, event->log, 0, + "cannot add new server to upstream \"%V\", " + "memory exhausted", peers->name); + goto done; + } + + peer->server.len = server->len; + ngx_memcpy(peer->server.data, server->data, server->len); + + if (host->service.len == 0) { + peer->weight = template->weight; + + } else { + peer->weight = (template->weight != 1 ? template->weight + : addr->weight); + } + peer->effective_weight = peer->weight; peer->max_conns = template->max_conns; peer->max_fails = template->max_fails; @@ -675,8 +783,25 @@ ngx_http_upstream_zone_resolve_handler(ngx_resolver_ctx_t *ctx) ngx_http_upstream_zone_set_single(uscf); } + if (host->service.len && peers->next) { + ngx_http_upstream_rr_peers_unlock(peers); + + peers = peers->next; + backup = 1; + + ngx_http_upstream_rr_peers_wlock(peers); + + goto again; + } + +done: + ngx_http_upstream_rr_peers_unlock(peers); + while (++i < ctx->naddrs) { + ngx_http_upstream_zone_unmark_addr(&ctx->addrs[i]); + } + timer = (ngx_msec_t) 1000 * (ctx->valid > now ? ctx->valid - now + 1 : 1); ngx_resolve_name_done(ctx); diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c index efe772439..d090d1618 100644 --- a/src/http/ngx_http_upstream.c +++ b/src/http/ngx_http_upstream.c @@ -6306,6 +6306,19 @@ ngx_http_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) resolve = 1; continue; } + + if (ngx_strncmp(value[i].data, "service=", 8) == 0) { + + us->service.len = value[i].len - 8; + us->service.data = &value[i].data[8]; + + if (us->service.len == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "service is empty"); + return NGX_CONF_ERROR; + } + + continue; + } #endif goto invalid; @@ -6321,6 +6334,15 @@ ngx_http_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) /* resolve at run time */ u.no_resolve = 1; } + + if (us->service.len && !resolve) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "service upstream \"%V\" requires " + "\"resolve\" parameter", + &u.url); + return NGX_CONF_ERROR; + } + #endif if (ngx_parse_url(cf->pool, &u) != NGX_OK) { @@ -6336,6 +6358,22 @@ ngx_http_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) #if (NGX_HTTP_UPSTREAM_ZONE) + if (us->service.len && !u.no_port) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "service upstream \"%V\" may not have port", + &us->name); + + return NGX_CONF_ERROR; + } + + if (us->service.len && u.naddrs) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "service upstream \"%V\" requires domain name", + &us->name); + + return NGX_CONF_ERROR; + } + if (resolve && u.naddrs == 0) { ngx_addr_t *addr; diff --git a/src/http/ngx_http_upstream.h b/src/http/ngx_http_upstream.h index 0922021bd..57ee06f8d 100644 --- a/src/http/ngx_http_upstream.h +++ b/src/http/ngx_http_upstream.h @@ -106,9 +106,10 @@ typedef struct { #if (NGX_HTTP_UPSTREAM_ZONE) ngx_str_t host; + ngx_str_t service; #endif - NGX_COMPAT_BEGIN(4) + NGX_COMPAT_BEGIN(2) NGX_COMPAT_END } ngx_http_upstream_server_t; diff --git a/src/http/ngx_http_upstream_round_robin.c b/src/http/ngx_http_upstream_round_robin.c index cc1b6d1a2..5dbd4e626 100644 --- a/src/http/ngx_http_upstream_round_robin.c +++ b/src/http/ngx_http_upstream_round_robin.c @@ -176,6 +176,7 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf, } peer[n].host->name = server[i].host; + peer[n].host->service = server[i].service; peer[n].sockaddr = server[i].addrs[0].sockaddr; peer[n].socklen = server[i].addrs[0].socklen; @@ -245,7 +246,15 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf, } } - if (n + r == 0) { + if (n == 0 +#if (NGX_HTTP_UPSTREAM_ZONE) + && !resolve +#endif + ) { + return NGX_OK; + } + + if (n + r == 0 && !(us->flags & NGX_HTTP_UPSTREAM_BACKUP)) { return NGX_OK; } @@ -293,6 +302,7 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf, } peer[n].host->name = server[i].host; + peer[n].host->service = server[i].service; peer[n].sockaddr = server[i].addrs[0].sockaddr; peer[n].socklen = server[i].addrs[0].socklen; diff --git a/src/http/ngx_http_upstream_round_robin.h b/src/http/ngx_http_upstream_round_robin.h index 06437f405..084b0b886 100644 --- a/src/http/ngx_http_upstream_round_robin.h +++ b/src/http/ngx_http_upstream_round_robin.h @@ -24,6 +24,7 @@ typedef struct { ngx_event_t event; /* must be first */ ngx_uint_t worker; ngx_str_t name; + ngx_str_t service; ngx_http_upstream_rr_peers_t *peers; ngx_http_upstream_rr_peer_t *peer; } ngx_http_upstream_host_t; @@ -150,7 +151,7 @@ ngx_http_upstream_rr_peer_free_locked(ngx_http_upstream_rr_peers_t *peers, ngx_slab_free_locked(peers->shpool, peer->sockaddr); ngx_slab_free_locked(peers->shpool, peer->name.data); - if (peer->server.data && (peer->host == NULL || peer->host->peer == peer)) { + if (peer->server.data) { ngx_slab_free_locked(peers->shpool, peer->server.data); } diff --git a/src/stream/ngx_stream_upstream.c b/src/stream/ngx_stream_upstream.c index a251cca00..be4f13016 100644 --- a/src/stream/ngx_stream_upstream.c +++ b/src/stream/ngx_stream_upstream.c @@ -523,6 +523,19 @@ ngx_stream_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) resolve = 1; continue; } + + if (ngx_strncmp(value[i].data, "service=", 8) == 0) { + + us->service.len = value[i].len - 8; + us->service.data = &value[i].data[8]; + + if (us->service.len == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "service is empty"); + return NGX_CONF_ERROR; + } + + continue; + } #endif goto invalid; @@ -537,6 +550,15 @@ ngx_stream_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) /* resolve at run time */ u.no_resolve = 1; } + + if (us->service.len && !resolve) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "service upstream \"%V\" requires " + "\"resolve\" parameter", + &u.url); + return NGX_CONF_ERROR; + } + #endif if (ngx_parse_url(cf->pool, &u) != NGX_OK) { @@ -548,7 +570,12 @@ ngx_stream_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) return NGX_CONF_ERROR; } - if (u.no_port) { + if (u.no_port +#if (NGX_STREAM_UPSTREAM_ZONE) + && us->service.len == 0 +#endif + ) + { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "no port in upstream \"%V\"", &u.url); return NGX_CONF_ERROR; @@ -558,6 +585,22 @@ ngx_stream_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) #if (NGX_STREAM_UPSTREAM_ZONE) + if (us->service.len && !u.no_port) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "service upstream \"%V\" may not have port", + &us->name); + + return NGX_CONF_ERROR; + } + + if (us->service.len && u.naddrs) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "service upstream \"%V\" requires domain name", + &us->name); + + return NGX_CONF_ERROR; + } + if (resolve && u.naddrs == 0) { ngx_addr_t *addr; diff --git a/src/stream/ngx_stream_upstream.h b/src/stream/ngx_stream_upstream.h index 686e9269c..c581aa0be 100644 --- a/src/stream/ngx_stream_upstream.h +++ b/src/stream/ngx_stream_upstream.h @@ -65,10 +65,8 @@ typedef struct { #if (NGX_STREAM_UPSTREAM_ZONE) ngx_str_t host; + ngx_str_t service; #endif - - NGX_COMPAT_BEGIN(2) - NGX_COMPAT_END } ngx_stream_upstream_server_t; diff --git a/src/stream/ngx_stream_upstream_round_robin.c b/src/stream/ngx_stream_upstream_round_robin.c index a18171a39..e1903b3d2 100644 --- a/src/stream/ngx_stream_upstream_round_robin.c +++ b/src/stream/ngx_stream_upstream_round_robin.c @@ -183,6 +183,7 @@ ngx_stream_upstream_init_round_robin(ngx_conf_t *cf, } peer[n].host->name = server[i].host; + peer[n].host->service = server[i].service; peer[n].sockaddr = server[i].addrs[0].sockaddr; peer[n].socklen = server[i].addrs[0].socklen; @@ -251,7 +252,15 @@ ngx_stream_upstream_init_round_robin(ngx_conf_t *cf, } } - if (n + r == 0) { + if (n == 0 +#if (NGX_STREAM_UPSTREAM_ZONE) + && !resolve +#endif + ) { + return NGX_OK; + } + + if (n + r == 0 && !(us->flags & NGX_STREAM_UPSTREAM_BACKUP)) { return NGX_OK; } @@ -299,6 +308,7 @@ ngx_stream_upstream_init_round_robin(ngx_conf_t *cf, } peer[n].host->name = server[i].host; + peer[n].host->service = server[i].service; peer[n].sockaddr = server[i].addrs[0].sockaddr; peer[n].socklen = server[i].addrs[0].socklen; diff --git a/src/stream/ngx_stream_upstream_round_robin.h b/src/stream/ngx_stream_upstream_round_robin.h index 5002200c2..707a9889d 100644 --- a/src/stream/ngx_stream_upstream_round_robin.h +++ b/src/stream/ngx_stream_upstream_round_robin.h @@ -24,6 +24,7 @@ typedef struct { ngx_event_t event; /* must be first */ ngx_uint_t worker; ngx_str_t name; + ngx_str_t service; ngx_stream_upstream_rr_peers_t *peers; ngx_stream_upstream_rr_peer_t *peer; } ngx_stream_upstream_host_t; @@ -148,7 +149,7 @@ ngx_stream_upstream_rr_peer_free_locked(ngx_stream_upstream_rr_peers_t *peers, ngx_slab_free_locked(peers->shpool, peer->sockaddr); ngx_slab_free_locked(peers->shpool, peer->name.data); - if (peer->server.data && (peer->host == NULL || peer->host->peer == peer)) { + if (peer->server.data) { ngx_slab_free_locked(peers->shpool, peer->server.data); } diff --git a/src/stream/ngx_stream_upstream_zone_module.c b/src/stream/ngx_stream_upstream_zone_module.c index d731baf31..0d6ab89f3 100644 --- a/src/stream/ngx_stream_upstream_zone_module.c +++ b/src/stream/ngx_stream_upstream_zone_module.c @@ -356,6 +356,18 @@ ngx_stream_upstream_zone_copy_peer(ngx_stream_upstream_rr_peers_t *peers, dst->host->name.len = src->host->name.len; ngx_memcpy(dst->host->name.data, src->host->name.data, src->host->name.len); + + if (src->host->service.len) { + dst->host->service.data = ngx_slab_alloc_locked(pool, + src->host->service.len); + if (dst->host->service.data == NULL) { + goto failed; + } + + dst->host->service.len = src->host->service.len; + ngx_memcpy(dst->host->service.data, src->host->service.data, + src->host->service.len); + } } } @@ -364,6 +376,10 @@ ngx_stream_upstream_zone_copy_peer(ngx_stream_upstream_rr_peers_t *peers, failed: if (dst->host) { + if (dst->host->name.data) { + ngx_slab_free_locked(pool, dst->host->name.data); + } + ngx_slab_free_locked(pool, dst->host); } @@ -508,6 +524,7 @@ ngx_stream_upstream_zone_resolve_timer(ngx_event_t *event) ctx->handler = ngx_stream_upstream_zone_resolve_handler; ctx->data = host; ctx->timeout = uscf->resolver_timeout; + ctx->service = host->service; ctx->cancelable = 1; if (ngx_resolve_name(ctx) == NGX_OK) { @@ -520,15 +537,28 @@ retry: } +#define ngx_stream_upstream_zone_addr_marked(addr) \ + ((uintptr_t) (addr)->sockaddr & 1) + +#define ngx_stream_upstream_zone_mark_addr(addr) \ + (addr)->sockaddr = (struct sockaddr *) ((uintptr_t) (addr)->sockaddr | 1) + +#define ngx_stream_upstream_zone_unmark_addr(addr) \ + (addr)->sockaddr = \ + (struct sockaddr *) ((uintptr_t) (addr)->sockaddr & ~((uintptr_t) 1)) + static void ngx_stream_upstream_zone_resolve_handler(ngx_resolver_ctx_t *ctx) { time_t now; + u_short min_priority; in_port_t port; + ngx_str_t *server; ngx_msec_t timer; - ngx_uint_t i, j; + ngx_uint_t i, j, backup, addr_backup; ngx_event_t *event; ngx_resolver_addr_t *addr; + ngx_resolver_srv_name_t *srv; ngx_stream_upstream_host_t *host; ngx_stream_upstream_rr_peer_t *peer, *template, **peerp; ngx_stream_upstream_rr_peers_t *peers; @@ -544,11 +574,32 @@ ngx_stream_upstream_zone_resolve_handler(ngx_resolver_ctx_t *ctx) now = ngx_time(); + for (i = 0; i < ctx->nsrvs; i++) { + srv = &ctx->srvs[i]; + + if (srv->state) { + ngx_log_error(NGX_LOG_ERR, event->log, 0, + "%V could not be resolved (%i: %s) " + "while resolving service %V of %V", + &srv->name, srv->state, + ngx_resolver_strerror(srv->state), &ctx->service, + &ctx->name); + } + } + if (ctx->state) { - ngx_log_error(NGX_LOG_ERR, event->log, 0, - "%V could not be resolved (%i: %s)", - &ctx->name, ctx->state, - ngx_resolver_strerror(ctx->state)); + if (ctx->service.len) { + ngx_log_error(NGX_LOG_ERR, event->log, 0, + "service %V of %V could not be resolved (%i: %s)", + &ctx->service, &ctx->name, ctx->state, + ngx_resolver_strerror(ctx->state)); + + } else { + ngx_log_error(NGX_LOG_ERR, event->log, 0, + "%V could not be resolved (%i: %s)", + &ctx->name, ctx->state, + ngx_resolver_strerror(ctx->state)); + } if (ctx->state != NGX_RESOLVE_NXDOMAIN) { ngx_stream_upstream_rr_peers_unlock(peers); @@ -564,6 +615,13 @@ ngx_stream_upstream_zone_resolve_handler(ngx_resolver_ctx_t *ctx) ctx->naddrs = 0; } + backup = 0; + min_priority = 65535; + + for (i = 0; i < ctx->naddrs; i++) { + min_priority = ngx_min(ctx->addrs[i].priority, min_priority); + } + #if (NGX_DEBUG) { u_char text[NGX_SOCKADDR_STRLEN]; @@ -571,14 +629,20 @@ ngx_stream_upstream_zone_resolve_handler(ngx_resolver_ctx_t *ctx) for (i = 0; i < ctx->naddrs; i++) { len = ngx_sock_ntop(ctx->addrs[i].sockaddr, ctx->addrs[i].socklen, - text, NGX_SOCKADDR_STRLEN, 0); + text, NGX_SOCKADDR_STRLEN, 1); - ngx_log_debug3(NGX_LOG_DEBUG_STREAM, event->log, 0, - "name %V was resolved to %*s", &host->name, len, text); + ngx_log_debug7(NGX_LOG_DEBUG_STREAM, event->log, 0, + "name %V was resolved to %*s " + "s:\"%V\" n:\"%V\" w:%d %s", + &host->name, len, text, &host->service, + &ctx->addrs[i].name, ctx->addrs[i].weight, + ctx->addrs[i].priority != min_priority ? "backup" : ""); } } #endif +again: + for (peerp = &peers->peer; *peerp; /* void */ ) { peer = *peerp; @@ -590,14 +654,39 @@ ngx_stream_upstream_zone_resolve_handler(ngx_resolver_ctx_t *ctx) addr = &ctx->addrs[j]; - if (addr->name.len == 0 - && ngx_cmp_sockaddr(peer->sockaddr, peer->socklen, - addr->sockaddr, addr->socklen, 0) - == NGX_OK) + addr_backup = (addr->priority != min_priority); + if (addr_backup != backup) { + continue; + } + + if (ngx_stream_upstream_zone_addr_marked(addr)) { + continue; + } + + if (ngx_cmp_sockaddr(peer->sockaddr, peer->socklen, + addr->sockaddr, addr->socklen, + host->service.len != 0) + != NGX_OK) { - addr->name.len = 1; - goto next; + continue; } + + if (host->service.len) { + if (addr->name.len != peer->server.len + || ngx_strncmp(addr->name.data, peer->server.data, + addr->name.len)) + { + continue; + } + + if (template->weight == 1 && addr->weight != peer->weight) { + continue; + } + } + + ngx_stream_upstream_zone_mark_addr(addr); + + goto next; } *peerp = peer->next; @@ -616,33 +705,32 @@ ngx_stream_upstream_zone_resolve_handler(ngx_resolver_ctx_t *ctx) addr = &ctx->addrs[i]; - if (addr->name.len == 1) { - addr->name.len = 0; + addr_backup = (addr->priority != min_priority); + if (addr_backup != backup) { + continue; + } + + if (ngx_stream_upstream_zone_addr_marked(addr)) { + ngx_stream_upstream_zone_unmark_addr(addr); continue; } ngx_shmtx_lock(&peers->shpool->mutex); peer = ngx_stream_upstream_zone_copy_peer(peers, NULL); ngx_shmtx_unlock(&peers->shpool->mutex); + if (peer == NULL) { ngx_log_error(NGX_LOG_ERR, event->log, 0, "cannot add new server to upstream \"%V\", " "memory exhausted", peers->name); - break; + goto done; } ngx_memcpy(peer->sockaddr, addr->sockaddr, addr->socklen); - port = ((struct sockaddr_in *) template->sockaddr)->sin_port; - - switch (peer->sockaddr->sa_family) { -#if (NGX_HAVE_INET6) - case AF_INET6: - ((struct sockaddr_in6 *) peer->sockaddr)->sin6_port = port; - break; -#endif - default: /* AF_INET */ - ((struct sockaddr_in *) peer->sockaddr)->sin_port = port; + if (host->service.len == 0) { + port = ngx_inet_get_port(template->sockaddr); + ngx_inet_set_port(peer->sockaddr, port); } peer->socklen = addr->socklen; @@ -651,9 +739,30 @@ ngx_stream_upstream_zone_resolve_handler(ngx_resolver_ctx_t *ctx) peer->name.data, NGX_SOCKADDR_STRLEN, 1); peer->host = template->host; - peer->server = template->server; - peer->weight = template->weight; + server = host->service.len ? &addr->name : &template->server; + + peer->server.data = ngx_slab_alloc(peers->shpool, server->len); + if (peer->server.data == NULL) { + ngx_stream_upstream_rr_peer_free(peers, peer); + + ngx_log_error(NGX_LOG_ERR, event->log, 0, + "cannot add new server to upstream \"%V\", " + "memory exhausted", peers->name); + goto done; + } + + peer->server.len = server->len; + ngx_memcpy(peer->server.data, server->data, server->len); + + if (host->service.len == 0) { + peer->weight = template->weight; + + } else { + peer->weight = (template->weight != 1 ? template->weight + : addr->weight); + } + peer->effective_weight = peer->weight; peer->max_conns = template->max_conns; peer->max_fails = template->max_fails; @@ -672,8 +781,25 @@ ngx_stream_upstream_zone_resolve_handler(ngx_resolver_ctx_t *ctx) ngx_stream_upstream_zone_set_single(uscf); } + if (host->service.len && peers->next) { + ngx_stream_upstream_rr_peers_unlock(peers); + + peers = peers->next; + backup = 1; + + ngx_stream_upstream_rr_peers_wlock(peers); + + goto again; + } + +done: + ngx_stream_upstream_rr_peers_unlock(peers); + while (++i < ctx->naddrs) { + ngx_stream_upstream_zone_unmark_addr(&ctx->addrs[i]); + } + timer = (ngx_msec_t) 1000 * (ctx->valid > now ? ctx->valid - now + 1 : 1); ngx_resolve_name_done(ctx); From noreply at nginx.com Thu Nov 7 15:58:03 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Thu, 7 Nov 2024 15:58:03 +0000 (UTC) Subject: [nginx] Upstream: pre-resolve servers on reload. Message-ID: <20241107155803.3788746C6F@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/5ebe7a4122c9653ed6b06e6577fc68904ad061c4 branches: master commit: 5ebe7a4122c9653ed6b06e6577fc68904ad061c4 user: Ruslan Ermilov date: Fri, 3 Nov 2017 22:22:23 +0300 description: Upstream: pre-resolve servers on reload. After configuration is reloaded, it may take some time for the re-resolvable upstream servers to resolve and become available as peers. During this time, client requests might get dropped. Such servers are now pre-resolved using the "cache" of already resolved peers from the old shared memory zone. --- src/http/modules/ngx_http_upstream_zone_module.c | 201 ++++++++++++++++++++++- src/stream/ngx_stream_upstream_zone_module.c | 201 ++++++++++++++++++++++- 2 files changed, 388 insertions(+), 14 deletions(-) diff --git a/src/http/modules/ngx_http_upstream_zone_module.c b/src/http/modules/ngx_http_upstream_zone_module.c index e3ede9268..c1931a8bb 100644 --- a/src/http/modules/ngx_http_upstream_zone_module.c +++ b/src/http/modules/ngx_http_upstream_zone_module.c @@ -15,9 +15,15 @@ static char *ngx_http_upstream_zone(ngx_conf_t *cf, ngx_command_t *cmd, static ngx_int_t ngx_http_upstream_init_zone(ngx_shm_zone_t *shm_zone, void *data); static ngx_http_upstream_rr_peers_t *ngx_http_upstream_zone_copy_peers( - ngx_slab_pool_t *shpool, ngx_http_upstream_srv_conf_t *uscf); + ngx_slab_pool_t *shpool, ngx_http_upstream_srv_conf_t *uscf, + ngx_http_upstream_srv_conf_t *ouscf); static ngx_http_upstream_rr_peer_t *ngx_http_upstream_zone_copy_peer( ngx_http_upstream_rr_peers_t *peers, ngx_http_upstream_rr_peer_t *src); +static ngx_int_t ngx_http_upstream_zone_preresolve( + ngx_http_upstream_rr_peer_t *resolve, + ngx_http_upstream_rr_peers_t *peers, + ngx_http_upstream_rr_peer_t *oresolve, + ngx_http_upstream_rr_peers_t *opeers); static void ngx_http_upstream_zone_set_single( ngx_http_upstream_srv_conf_t *uscf); static void ngx_http_upstream_zone_remove_peer_locked( @@ -128,11 +134,11 @@ 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_uint_t i, j; ngx_slab_pool_t *shpool; ngx_http_upstream_rr_peers_t *peers, **peersp; - ngx_http_upstream_srv_conf_t *uscf, **uscfp; - ngx_http_upstream_main_conf_t *umcf; + ngx_http_upstream_srv_conf_t *uscf, *ouscf, **uscfp, **ouscfp; + ngx_http_upstream_main_conf_t *umcf, *oumcf; shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; umcf = shm_zone->data; @@ -169,6 +175,7 @@ ngx_http_upstream_init_zone(ngx_shm_zone_t *shm_zone, void *data) /* copy peers to shared memory */ peersp = (ngx_http_upstream_rr_peers_t **) (void *) &shpool->data; + oumcf = data; for (i = 0; i < umcf->upstreams.nelts; i++) { uscf = uscfp[i]; @@ -177,7 +184,38 @@ ngx_http_upstream_init_zone(ngx_shm_zone_t *shm_zone, void *data) continue; } - peers = ngx_http_upstream_zone_copy_peers(shpool, uscf); + ouscf = NULL; + + if (oumcf) { + ouscfp = oumcf->upstreams.elts; + + for (j = 0; j < oumcf->upstreams.nelts; j++) { + + if (ouscfp[j]->shm_zone == NULL) { + continue; + } + + if (ouscfp[j]->shm_zone->shm.name.len != shm_zone->shm.name.len + || ngx_memcmp(ouscfp[j]->shm_zone->shm.name.data, + shm_zone->shm.name.data, + shm_zone->shm.name.len) + != 0) + { + continue; + } + + if (ouscfp[j]->host.len == uscf->host.len + && ngx_memcmp(ouscfp[j]->host.data, uscf->host.data, + uscf->host.len) + == 0) + { + ouscf = ouscfp[j]; + break; + } + } + } + + peers = ngx_http_upstream_zone_copy_peers(shpool, uscf, ouscf); if (peers == NULL) { return NGX_ERROR; } @@ -192,12 +230,14 @@ ngx_http_upstream_init_zone(ngx_shm_zone_t *shm_zone, void *data) static ngx_http_upstream_rr_peers_t * ngx_http_upstream_zone_copy_peers(ngx_slab_pool_t *shpool, - ngx_http_upstream_srv_conf_t *uscf) + ngx_http_upstream_srv_conf_t *uscf, ngx_http_upstream_srv_conf_t *ouscf) { ngx_str_t *name; ngx_uint_t *config; ngx_http_upstream_rr_peer_t *peer, **peerp; - ngx_http_upstream_rr_peers_t *peers, *backup; + ngx_http_upstream_rr_peers_t *peers, *opeers, *backup; + + opeers = (ouscf ? ouscf->peer.data : NULL); config = ngx_slab_calloc(shpool, sizeof(ngx_uint_t)); if (config == NULL) { @@ -250,6 +290,16 @@ ngx_http_upstream_zone_copy_peers(ngx_slab_pool_t *shpool, (*peers->config)++; } + if (opeers) { + + if (ngx_http_upstream_zone_preresolve(peers->resolve, peers, + opeers->resolve, opeers) + != NGX_OK) + { + return NULL; + } + } + if (peers->next == NULL) { goto done; } @@ -289,10 +339,30 @@ ngx_http_upstream_zone_copy_peers(ngx_slab_pool_t *shpool, peers->next = backup; + if (opeers && opeers->next) { + + if (ngx_http_upstream_zone_preresolve(peers->resolve, backup, + opeers->resolve, opeers->next) + != NGX_OK) + { + return NULL; + } + + if (ngx_http_upstream_zone_preresolve(backup->resolve, backup, + opeers->next->resolve, + opeers->next) + != NGX_OK) + { + return NULL; + } + } + done: uscf->peer.data = peers; + ngx_http_upstream_zone_set_single(uscf); + return peers; } @@ -404,6 +474,123 @@ failed: } +static ngx_int_t +ngx_http_upstream_zone_preresolve(ngx_http_upstream_rr_peer_t *resolve, + ngx_http_upstream_rr_peers_t *peers, + ngx_http_upstream_rr_peer_t *oresolve, + ngx_http_upstream_rr_peers_t *opeers) +{ + in_port_t port; + ngx_str_t *server; + ngx_http_upstream_host_t *host; + ngx_http_upstream_rr_peer_t *peer, *template, *opeer, **peerp; + + if (resolve == NULL || oresolve == NULL) { + return NGX_OK; + } + + for (peerp = &peers->peer; *peerp; peerp = &(*peerp)->next) { + /* void */ + } + + ngx_http_upstream_rr_peers_rlock(opeers); + + for (template = resolve; template; template = template->next) { + for (opeer = oresolve; opeer; opeer = opeer->next) { + + if (opeer->host->name.len != template->host->name.len + || ngx_memcmp(opeer->host->name.data, + template->host->name.data, + template->host->name.len) + != 0) + { + continue; + } + + if (opeer->host->service.len != template->host->service.len + || ngx_memcmp(opeer->host->service.data, + template->host->service.data, + template->host->service.len) + != 0) + { + continue; + } + + host = opeer->host; + + for (opeer = opeers->peer; opeer; opeer = opeer->next) { + + if (opeer->host != host) { + continue; + } + + peer = ngx_http_upstream_zone_copy_peer(peers, NULL); + if (peer == NULL) { + ngx_http_upstream_rr_peers_unlock(opeers); + return NGX_ERROR; + } + + ngx_memcpy(peer->sockaddr, opeer->sockaddr, opeer->socklen); + + if (template->host->service.len == 0) { + port = ngx_inet_get_port(template->sockaddr); + ngx_inet_set_port(peer->sockaddr, port); + } + + peer->socklen = opeer->socklen; + + peer->name.len = ngx_sock_ntop(peer->sockaddr, peer->socklen, + peer->name.data, + NGX_SOCKADDR_STRLEN, 1); + + peer->host = template->host; + + server = template->host->service.len ? &opeer->server + : &template->server; + + peer->server.data = ngx_slab_alloc(peers->shpool, server->len); + if (peer->server.data == NULL) { + ngx_http_upstream_rr_peers_unlock(opeers); + return NGX_ERROR; + } + + ngx_memcpy(peer->server.data, server->data, server->len); + peer->server.len = server->len; + + if (host->service.len == 0) { + peer->weight = template->weight; + + } else { + peer->weight = (template->weight != 1 ? template->weight + : opeer->weight); + } + + peer->effective_weight = peer->weight; + peer->max_conns = template->max_conns; + peer->max_fails = template->max_fails; + peer->fail_timeout = template->fail_timeout; + peer->down = template->down; + + (*peers->config)++; + + *peerp = peer; + peerp = &peer->next; + + peers->number++; + peers->tries += (peer->down == 0); + peers->total_weight += peer->weight; + peers->weighted = (peers->total_weight != peers->number); + } + + break; + } + } + + ngx_http_upstream_rr_peers_unlock(opeers); + return NGX_OK; +} + + static void ngx_http_upstream_zone_set_single(ngx_http_upstream_srv_conf_t *uscf) { diff --git a/src/stream/ngx_stream_upstream_zone_module.c b/src/stream/ngx_stream_upstream_zone_module.c index 0d6ab89f3..d92609feb 100644 --- a/src/stream/ngx_stream_upstream_zone_module.c +++ b/src/stream/ngx_stream_upstream_zone_module.c @@ -15,9 +15,15 @@ static char *ngx_stream_upstream_zone(ngx_conf_t *cf, ngx_command_t *cmd, static ngx_int_t ngx_stream_upstream_init_zone(ngx_shm_zone_t *shm_zone, void *data); static ngx_stream_upstream_rr_peers_t *ngx_stream_upstream_zone_copy_peers( - ngx_slab_pool_t *shpool, ngx_stream_upstream_srv_conf_t *uscf); + ngx_slab_pool_t *shpool, ngx_stream_upstream_srv_conf_t *uscf, + ngx_stream_upstream_srv_conf_t *ouscf); static ngx_stream_upstream_rr_peer_t *ngx_stream_upstream_zone_copy_peer( ngx_stream_upstream_rr_peers_t *peers, ngx_stream_upstream_rr_peer_t *src); +static ngx_int_t ngx_stream_upstream_zone_preresolve( + ngx_stream_upstream_rr_peer_t *resolve, + ngx_stream_upstream_rr_peers_t *peers, + ngx_stream_upstream_rr_peer_t *oresolve, + ngx_stream_upstream_rr_peers_t *opeers); static void ngx_stream_upstream_zone_set_single( ngx_stream_upstream_srv_conf_t *uscf); static void ngx_stream_upstream_zone_remove_peer_locked( @@ -125,11 +131,11 @@ 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_uint_t i, j; ngx_slab_pool_t *shpool; ngx_stream_upstream_rr_peers_t *peers, **peersp; - ngx_stream_upstream_srv_conf_t *uscf, **uscfp; - ngx_stream_upstream_main_conf_t *umcf; + ngx_stream_upstream_srv_conf_t *uscf, *ouscf, **uscfp, **ouscfp; + ngx_stream_upstream_main_conf_t *umcf, *oumcf; shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; umcf = shm_zone->data; @@ -166,6 +172,7 @@ ngx_stream_upstream_init_zone(ngx_shm_zone_t *shm_zone, void *data) /* copy peers to shared memory */ peersp = (ngx_stream_upstream_rr_peers_t **) (void *) &shpool->data; + oumcf = data; for (i = 0; i < umcf->upstreams.nelts; i++) { uscf = uscfp[i]; @@ -174,7 +181,38 @@ ngx_stream_upstream_init_zone(ngx_shm_zone_t *shm_zone, void *data) continue; } - peers = ngx_stream_upstream_zone_copy_peers(shpool, uscf); + ouscf = NULL; + + if (oumcf) { + ouscfp = oumcf->upstreams.elts; + + for (j = 0; j < oumcf->upstreams.nelts; j++) { + + if (ouscfp[j]->shm_zone == NULL) { + continue; + } + + if (ouscfp[j]->shm_zone->shm.name.len != shm_zone->shm.name.len + || ngx_memcmp(ouscfp[j]->shm_zone->shm.name.data, + shm_zone->shm.name.data, + shm_zone->shm.name.len) + != 0) + { + continue; + } + + if (ouscfp[j]->host.len == uscf->host.len + && ngx_memcmp(ouscfp[j]->host.data, uscf->host.data, + uscf->host.len) + == 0) + { + ouscf = ouscfp[j]; + break; + } + } + } + + peers = ngx_stream_upstream_zone_copy_peers(shpool, uscf, ouscf); if (peers == NULL) { return NGX_ERROR; } @@ -189,12 +227,14 @@ ngx_stream_upstream_init_zone(ngx_shm_zone_t *shm_zone, void *data) static ngx_stream_upstream_rr_peers_t * ngx_stream_upstream_zone_copy_peers(ngx_slab_pool_t *shpool, - ngx_stream_upstream_srv_conf_t *uscf) + ngx_stream_upstream_srv_conf_t *uscf, ngx_stream_upstream_srv_conf_t *ouscf) { ngx_str_t *name; ngx_uint_t *config; ngx_stream_upstream_rr_peer_t *peer, **peerp; - ngx_stream_upstream_rr_peers_t *peers, *backup; + ngx_stream_upstream_rr_peers_t *peers, *opeers, *backup; + + opeers = (ouscf ? ouscf->peer.data : NULL); config = ngx_slab_calloc(shpool, sizeof(ngx_uint_t)); if (config == NULL) { @@ -247,6 +287,16 @@ ngx_stream_upstream_zone_copy_peers(ngx_slab_pool_t *shpool, (*peers->config)++; } + if (opeers) { + + if (ngx_stream_upstream_zone_preresolve(peers->resolve, peers, + opeers->resolve, opeers) + != NGX_OK) + { + return NULL; + } + } + if (peers->next == NULL) { goto done; } @@ -286,10 +336,30 @@ ngx_stream_upstream_zone_copy_peers(ngx_slab_pool_t *shpool, peers->next = backup; + if (opeers && opeers->next) { + + if (ngx_stream_upstream_zone_preresolve(peers->resolve, backup, + opeers->resolve, opeers->next) + != NGX_OK) + { + return NULL; + } + + if (ngx_stream_upstream_zone_preresolve(backup->resolve, backup, + opeers->next->resolve, + opeers->next) + != NGX_OK) + { + return NULL; + } + } + done: uscf->peer.data = peers; + ngx_stream_upstream_zone_set_single(uscf); + return peers; } @@ -401,6 +471,123 @@ failed: } +static ngx_int_t +ngx_stream_upstream_zone_preresolve(ngx_stream_upstream_rr_peer_t *resolve, + ngx_stream_upstream_rr_peers_t *peers, + ngx_stream_upstream_rr_peer_t *oresolve, + ngx_stream_upstream_rr_peers_t *opeers) +{ + in_port_t port; + ngx_str_t *server; + ngx_stream_upstream_host_t *host; + ngx_stream_upstream_rr_peer_t *peer, *template, *opeer, **peerp; + + if (resolve == NULL || oresolve == NULL) { + return NGX_OK; + } + + for (peerp = &peers->peer; *peerp; peerp = &(*peerp)->next) { + /* void */ + } + + ngx_stream_upstream_rr_peers_rlock(opeers); + + for (template = resolve; template; template = template->next) { + for (opeer = oresolve; opeer; opeer = opeer->next) { + + if (opeer->host->name.len != template->host->name.len + || ngx_memcmp(opeer->host->name.data, + template->host->name.data, + template->host->name.len) + != 0) + { + continue; + } + + if (opeer->host->service.len != template->host->service.len + || ngx_memcmp(opeer->host->service.data, + template->host->service.data, + template->host->service.len) + != 0) + { + continue; + } + + host = opeer->host; + + for (opeer = opeers->peer; opeer; opeer = opeer->next) { + + if (opeer->host != host) { + continue; + } + + peer = ngx_stream_upstream_zone_copy_peer(peers, NULL); + if (peer == NULL) { + ngx_stream_upstream_rr_peers_unlock(opeers); + return NGX_ERROR; + } + + ngx_memcpy(peer->sockaddr, opeer->sockaddr, opeer->socklen); + + if (template->host->service.len == 0) { + port = ngx_inet_get_port(template->sockaddr); + ngx_inet_set_port(peer->sockaddr, port); + } + + peer->socklen = opeer->socklen; + + peer->name.len = ngx_sock_ntop(peer->sockaddr, peer->socklen, + peer->name.data, + NGX_SOCKADDR_STRLEN, 1); + + peer->host = template->host; + + server = template->host->service.len ? &opeer->server + : &template->server; + + peer->server.data = ngx_slab_alloc(peers->shpool, server->len); + if (peer->server.data == NULL) { + ngx_stream_upstream_rr_peers_unlock(opeers); + return NGX_ERROR; + } + + ngx_memcpy(peer->server.data, server->data, server->len); + peer->server.len = server->len; + + if (host->service.len == 0) { + peer->weight = template->weight; + + } else { + peer->weight = (template->weight != 1 ? template->weight + : opeer->weight); + } + + peer->effective_weight = peer->weight; + peer->max_conns = template->max_conns; + peer->max_fails = template->max_fails; + peer->fail_timeout = template->fail_timeout; + peer->down = template->down; + + (*peers->config)++; + + *peerp = peer; + peerp = &peer->next; + + peers->number++; + peers->tries += (peer->down == 0); + peers->total_weight += peer->weight; + peers->weighted = (peers->total_weight != peers->number); + } + + break; + } + } + + ngx_stream_upstream_rr_peers_unlock(opeers); + return NGX_OK; +} + + static void ngx_stream_upstream_zone_set_single(ngx_stream_upstream_srv_conf_t *uscf) { From noreply at nginx.com Thu Nov 7 15:58:03 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Thu, 7 Nov 2024 15:58:03 +0000 (UTC) Subject: [nginx] Upstream: re-resolvable servers. Message-ID: <20241107155803.2696146C6C@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/db6870e06dde7ab249e9a41a0e0a76219f82dd8c branches: master commit: db6870e06dde7ab249e9a41a0e0a76219f82dd8c user: Ruslan Ermilov date: Sat, 15 Feb 2014 15:12:34 +0400 description: Upstream: re-resolvable servers. Specifying the upstream server by a hostname together with the "resolve" parameter will make the hostname to be periodically resolved, and upstream servers added/removed as necessary. This requires a "resolver" at the "http" configuration block. The "resolver_timeout" parameter also affects when the failed DNS requests will be attempted again. Responses with NXDOMAIN will be attempted again in 10 seconds. Upstream has a configuration generation number that is incremented each time servers are added/removed to the primary/backup list. This number is remembered by the peer.init method, and if peer.get detects a change in configuration, it returns NGX_BUSY. Each server has a reference counter. It is incremented by peer.get and decremented by peer.free. When a server is removed, it is removed from the list of servers and is marked as "zombie". The memory allocated by a zombie peer is freed only when its reference count becomes zero. Co-authored-by: Roman Arutyunyan Co-authored-by: Sergey Kandaurov Co-authored-by: Vladimir Homutov --- src/http/modules/ngx_http_upstream_hash_module.c | 88 ++++- .../modules/ngx_http_upstream_ip_hash_module.c | 12 +- .../modules/ngx_http_upstream_least_conn_module.c | 12 + src/http/modules/ngx_http_upstream_random_module.c | 35 +- src/http/modules/ngx_http_upstream_zone_module.c | 362 ++++++++++++++++++++- src/http/ngx_http_upstream.c | 78 +++++ src/http/ngx_http_upstream.h | 9 +- src/http/ngx_http_upstream_round_robin.c | 203 +++++++++++- src/http/ngx_http_upstream_round_robin.h | 86 ++++- src/stream/ngx_stream_proxy_module.c | 19 ++ src/stream/ngx_stream_upstream.c | 58 ++++ src/stream/ngx_stream_upstream.h | 9 +- src/stream/ngx_stream_upstream_hash_module.c | 90 ++++- src/stream/ngx_stream_upstream_least_conn_module.c | 12 + src/stream/ngx_stream_upstream_random_module.c | 35 +- src/stream/ngx_stream_upstream_round_robin.c | 204 +++++++++++- src/stream/ngx_stream_upstream_round_robin.h | 86 ++++- src/stream/ngx_stream_upstream_zone_module.c | 362 ++++++++++++++++++++- 18 files changed, 1704 insertions(+), 56 deletions(-) diff --git a/src/http/modules/ngx_http_upstream_hash_module.c b/src/http/modules/ngx_http_upstream_hash_module.c index e741eb237..2ecc8d3fb 100644 --- a/src/http/modules/ngx_http_upstream_hash_module.c +++ b/src/http/modules/ngx_http_upstream_hash_module.c @@ -24,6 +24,9 @@ typedef struct { typedef struct { ngx_http_complex_value_t key; +#if (NGX_HTTP_UPSTREAM_ZONE) + ngx_uint_t config; +#endif ngx_http_upstream_chash_points_t *points; } ngx_http_upstream_hash_srv_conf_t; @@ -49,6 +52,8 @@ static ngx_int_t ngx_http_upstream_get_hash_peer(ngx_peer_connection_t *pc, static ngx_int_t ngx_http_upstream_init_chash(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us); +static ngx_int_t ngx_http_upstream_update_chash(ngx_pool_t *pool, + ngx_http_upstream_srv_conf_t *us); static int ngx_libc_cdecl ngx_http_upstream_chash_cmp_points(const void *one, const void *two); static ngx_uint_t ngx_http_upstream_find_chash_point( @@ -178,10 +183,17 @@ ngx_http_upstream_get_hash_peer(ngx_peer_connection_t *pc, void *data) ngx_http_upstream_rr_peers_rlock(hp->rrp.peers); - if (hp->tries > 20 || hp->rrp.peers->single || hp->key.len == 0) { + if (hp->tries > 20 || hp->rrp.peers->number < 2 || hp->key.len == 0) { + ngx_http_upstream_rr_peers_unlock(hp->rrp.peers); + return hp->get_rr_peer(pc, &hp->rrp); + } + +#if (NGX_HTTP_UPSTREAM_ZONE) + if (hp->rrp.peers->config && hp->rrp.config != *hp->rrp.peers->config) { ngx_http_upstream_rr_peers_unlock(hp->rrp.peers); return hp->get_rr_peer(pc, &hp->rrp); } +#endif now = ngx_time(); @@ -262,6 +274,7 @@ ngx_http_upstream_get_hash_peer(ngx_peer_connection_t *pc, void *data) } hp->rrp.current = peer; + ngx_http_upstream_rr_peer_ref(hp->rrp.peers, peer); pc->sockaddr = peer->sockaddr; pc->socklen = peer->socklen; @@ -284,6 +297,26 @@ ngx_http_upstream_get_hash_peer(ngx_peer_connection_t *pc, void *data) static ngx_int_t ngx_http_upstream_init_chash(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us) +{ + if (ngx_http_upstream_init_round_robin(cf, us) != NGX_OK) { + return NGX_ERROR; + } + + us->peer.init = ngx_http_upstream_init_chash_peer; + +#if (NGX_HTTP_UPSTREAM_ZONE) + if (us->shm_zone) { + return NGX_OK; + } +#endif + + return ngx_http_upstream_update_chash(cf->pool, us); +} + + +static ngx_int_t +ngx_http_upstream_update_chash(ngx_pool_t *pool, + ngx_http_upstream_srv_conf_t *us) { u_char *host, *port, c; size_t host_len, port_len, size; @@ -299,25 +332,32 @@ ngx_http_upstream_init_chash(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us) u_char byte[4]; } prev_hash; - if (ngx_http_upstream_init_round_robin(cf, us) != NGX_OK) { - return NGX_ERROR; - } + hcf = ngx_http_conf_upstream_srv_conf(us, ngx_http_upstream_hash_module); - us->peer.init = ngx_http_upstream_init_chash_peer; + if (hcf->points) { + ngx_free(hcf->points); + hcf->points = NULL; + } peers = us->peer.data; npoints = peers->total_weight * 160; size = sizeof(ngx_http_upstream_chash_points_t) - + sizeof(ngx_http_upstream_chash_point_t) * (npoints - 1); + - sizeof(ngx_http_upstream_chash_point_t) + + sizeof(ngx_http_upstream_chash_point_t) * npoints; - points = ngx_palloc(cf->pool, size); + points = pool ? ngx_palloc(pool, size) : ngx_alloc(size, ngx_cycle->log); if (points == NULL) { return NGX_ERROR; } points->number = 0; + if (npoints == 0) { + hcf->points = points; + return NGX_OK; + } + for (peer = peers->peer; peer; peer = peer->next) { server = &peer->server; @@ -401,7 +441,6 @@ ngx_http_upstream_init_chash(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us) points->number = i + 1; - hcf = ngx_http_conf_upstream_srv_conf(us, ngx_http_upstream_hash_module); hcf->points = points; return NGX_OK; @@ -481,7 +520,22 @@ ngx_http_upstream_init_chash_peer(ngx_http_request_t *r, ngx_http_upstream_rr_peers_rlock(hp->rrp.peers); - hp->hash = ngx_http_upstream_find_chash_point(hcf->points, hash); +#if (NGX_HTTP_UPSTREAM_ZONE) + if (hp->rrp.peers->config + && (hcf->points == NULL || hcf->config != *hp->rrp.peers->config)) + { + if (ngx_http_upstream_update_chash(NULL, us) != NGX_OK) { + ngx_http_upstream_rr_peers_unlock(hp->rrp.peers); + return NGX_ERROR; + } + + hcf->config = *hp->rrp.peers->config; + } +#endif + + if (hcf->points->number) { + hp->hash = ngx_http_upstream_find_chash_point(hcf->points, hash); + } ngx_http_upstream_rr_peers_unlock(hp->rrp.peers); @@ -517,6 +571,20 @@ ngx_http_upstream_get_chash_peer(ngx_peer_connection_t *pc, void *data) pc->cached = 0; pc->connection = NULL; + if (hp->rrp.peers->number == 0) { + pc->name = hp->rrp.peers->name; + ngx_http_upstream_rr_peers_unlock(hp->rrp.peers); + return NGX_BUSY; + } + +#if (NGX_HTTP_UPSTREAM_ZONE) + if (hp->rrp.peers->config && hp->rrp.config != *hp->rrp.peers->config) { + pc->name = hp->rrp.peers->name; + ngx_http_upstream_rr_peers_unlock(hp->rrp.peers); + return NGX_BUSY; + } +#endif + now = ngx_time(); hcf = hp->conf; @@ -597,6 +665,7 @@ ngx_http_upstream_get_chash_peer(ngx_peer_connection_t *pc, void *data) found: hp->rrp.current = best; + ngx_http_upstream_rr_peer_ref(hp->rrp.peers, best); pc->sockaddr = best->sockaddr; pc->socklen = best->socklen; @@ -664,6 +733,7 @@ ngx_http_upstream_hash(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) } uscf->flags = NGX_HTTP_UPSTREAM_CREATE + |NGX_HTTP_UPSTREAM_MODIFY |NGX_HTTP_UPSTREAM_WEIGHT |NGX_HTTP_UPSTREAM_MAX_CONNS |NGX_HTTP_UPSTREAM_MAX_FAILS 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 1fa01d95a..1c1b41d19 100644 --- a/src/http/modules/ngx_http_upstream_ip_hash_module.c +++ b/src/http/modules/ngx_http_upstream_ip_hash_module.c @@ -163,11 +163,19 @@ ngx_http_upstream_get_ip_hash_peer(ngx_peer_connection_t *pc, void *data) ngx_http_upstream_rr_peers_rlock(iphp->rrp.peers); - if (iphp->tries > 20 || iphp->rrp.peers->single) { + if (iphp->tries > 20 || iphp->rrp.peers->number < 2) { ngx_http_upstream_rr_peers_unlock(iphp->rrp.peers); return iphp->get_rr_peer(pc, &iphp->rrp); } +#if (NGX_HTTP_UPSTREAM_ZONE) + if (iphp->rrp.peers->config && iphp->rrp.config != *iphp->rrp.peers->config) + { + ngx_http_upstream_rr_peers_unlock(iphp->rrp.peers); + return iphp->get_rr_peer(pc, &iphp->rrp); + } +#endif + now = ngx_time(); pc->cached = 0; @@ -232,6 +240,7 @@ ngx_http_upstream_get_ip_hash_peer(ngx_peer_connection_t *pc, void *data) } iphp->rrp.current = peer; + ngx_http_upstream_rr_peer_ref(iphp->rrp.peers, peer); pc->sockaddr = peer->sockaddr; pc->socklen = peer->socklen; @@ -268,6 +277,7 @@ ngx_http_upstream_ip_hash(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) uscf->peer.init_upstream = ngx_http_upstream_init_ip_hash; uscf->flags = NGX_HTTP_UPSTREAM_CREATE + |NGX_HTTP_UPSTREAM_MODIFY |NGX_HTTP_UPSTREAM_WEIGHT |NGX_HTTP_UPSTREAM_MAX_CONNS |NGX_HTTP_UPSTREAM_MAX_FAILS 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 ebe06276d..4df97777c 100644 --- a/src/http/modules/ngx_http_upstream_least_conn_module.c +++ b/src/http/modules/ngx_http_upstream_least_conn_module.c @@ -124,6 +124,12 @@ ngx_http_upstream_get_least_conn_peer(ngx_peer_connection_t *pc, void *data) ngx_http_upstream_rr_peers_wlock(peers); +#if (NGX_HTTP_UPSTREAM_ZONE) + if (peers->config && rrp->config != *peers->config) { + goto busy; + } +#endif + best = NULL; total = 0; @@ -244,6 +250,7 @@ ngx_http_upstream_get_least_conn_peer(ngx_peer_connection_t *pc, void *data) best->conns++; rrp->current = best; + ngx_http_upstream_rr_peer_ref(peers, best); n = p / (8 * sizeof(uintptr_t)); m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t)); @@ -280,6 +287,10 @@ failed: ngx_http_upstream_rr_peers_wlock(peers); } +#if (NGX_HTTP_UPSTREAM_ZONE) +busy: +#endif + ngx_http_upstream_rr_peers_unlock(peers); pc->name = peers->name; @@ -303,6 +314,7 @@ ngx_http_upstream_least_conn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) uscf->peer.init_upstream = ngx_http_upstream_init_least_conn; uscf->flags = NGX_HTTP_UPSTREAM_CREATE + |NGX_HTTP_UPSTREAM_MODIFY |NGX_HTTP_UPSTREAM_WEIGHT |NGX_HTTP_UPSTREAM_MAX_CONNS |NGX_HTTP_UPSTREAM_MAX_FAILS diff --git a/src/http/modules/ngx_http_upstream_random_module.c b/src/http/modules/ngx_http_upstream_random_module.c index 6f169c8f6..74714874b 100644 --- a/src/http/modules/ngx_http_upstream_random_module.c +++ b/src/http/modules/ngx_http_upstream_random_module.c @@ -17,6 +17,9 @@ typedef struct { typedef struct { ngx_uint_t two; +#if (NGX_HTTP_UPSTREAM_ZONE) + ngx_uint_t config; +#endif ngx_http_upstream_random_range_t *ranges; } ngx_http_upstream_random_srv_conf_t; @@ -127,6 +130,11 @@ ngx_http_upstream_update_random(ngx_pool_t *pool, rcf = ngx_http_conf_upstream_srv_conf(us, ngx_http_upstream_random_module); + if (rcf->ranges) { + ngx_free(rcf->ranges); + rcf->ranges = NULL; + } + peers = us->peer.data; size = peers->number * sizeof(ngx_http_upstream_random_range_t); @@ -186,11 +194,15 @@ ngx_http_upstream_init_random_peer(ngx_http_request_t *r, ngx_http_upstream_rr_peers_rlock(rp->rrp.peers); #if (NGX_HTTP_UPSTREAM_ZONE) - if (rp->rrp.peers->shpool && rcf->ranges == NULL) { + if (rp->rrp.peers->config + && (rcf->ranges == NULL || rcf->config != *rp->rrp.peers->config)) + { if (ngx_http_upstream_update_random(NULL, us) != NGX_OK) { ngx_http_upstream_rr_peers_unlock(rp->rrp.peers); return NGX_ERROR; } + + rcf->config = *rp->rrp.peers->config; } #endif @@ -220,11 +232,18 @@ ngx_http_upstream_get_random_peer(ngx_peer_connection_t *pc, void *data) ngx_http_upstream_rr_peers_rlock(peers); - if (rp->tries > 20 || peers->single) { + if (rp->tries > 20 || peers->number < 2) { ngx_http_upstream_rr_peers_unlock(peers); return ngx_http_upstream_get_round_robin_peer(pc, rrp); } +#if (NGX_HTTP_UPSTREAM_ZONE) + if (peers->config && rrp->config != *peers->config) { + ngx_http_upstream_rr_peers_unlock(peers); + return ngx_http_upstream_get_round_robin_peer(pc, rrp); + } +#endif + pc->cached = 0; pc->connection = NULL; @@ -274,6 +293,7 @@ ngx_http_upstream_get_random_peer(ngx_peer_connection_t *pc, void *data) } rrp->current = peer; + ngx_http_upstream_rr_peer_ref(peers, peer); if (now - peer->checked > peer->fail_timeout) { peer->checked = now; @@ -314,10 +334,17 @@ ngx_http_upstream_get_random2_peer(ngx_peer_connection_t *pc, void *data) ngx_http_upstream_rr_peers_wlock(peers); - if (rp->tries > 20 || peers->single) { + if (rp->tries > 20 || peers->number < 2) { + ngx_http_upstream_rr_peers_unlock(peers); + return ngx_http_upstream_get_round_robin_peer(pc, rrp); + } + +#if (NGX_HTTP_UPSTREAM_ZONE) + if (peers->config && rrp->config != *peers->config) { ngx_http_upstream_rr_peers_unlock(peers); return ngx_http_upstream_get_round_robin_peer(pc, rrp); } +#endif pc->cached = 0; pc->connection = NULL; @@ -384,6 +411,7 @@ ngx_http_upstream_get_random2_peer(ngx_peer_connection_t *pc, void *data) } rrp->current = peer; + ngx_http_upstream_rr_peer_ref(peers, peer); if (now - peer->checked > peer->fail_timeout) { peer->checked = now; @@ -467,6 +495,7 @@ ngx_http_upstream_random(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) uscf->peer.init_upstream = ngx_http_upstream_init_random; uscf->flags = NGX_HTTP_UPSTREAM_CREATE + |NGX_HTTP_UPSTREAM_MODIFY |NGX_HTTP_UPSTREAM_WEIGHT |NGX_HTTP_UPSTREAM_MAX_CONNS |NGX_HTTP_UPSTREAM_MAX_FAILS diff --git a/src/http/modules/ngx_http_upstream_zone_module.c b/src/http/modules/ngx_http_upstream_zone_module.c index 3229cfe84..355df39ab 100644 --- a/src/http/modules/ngx_http_upstream_zone_module.c +++ b/src/http/modules/ngx_http_upstream_zone_module.c @@ -18,6 +18,13 @@ static ngx_http_upstream_rr_peers_t *ngx_http_upstream_zone_copy_peers( ngx_slab_pool_t *shpool, ngx_http_upstream_srv_conf_t *uscf); static ngx_http_upstream_rr_peer_t *ngx_http_upstream_zone_copy_peer( ngx_http_upstream_rr_peers_t *peers, ngx_http_upstream_rr_peer_t *src); +static void ngx_http_upstream_zone_set_single( + ngx_http_upstream_srv_conf_t *uscf); +static void ngx_http_upstream_zone_remove_peer_locked( + ngx_http_upstream_rr_peers_t *peers, ngx_http_upstream_rr_peer_t *peer); +static ngx_int_t ngx_http_upstream_zone_init_worker(ngx_cycle_t *cycle); +static void ngx_http_upstream_zone_resolve_timer(ngx_event_t *event); +static void ngx_http_upstream_zone_resolve_handler(ngx_resolver_ctx_t *ctx); static ngx_command_t ngx_http_upstream_zone_commands[] = { @@ -55,7 +62,7 @@ ngx_module_t ngx_http_upstream_zone_module = { NGX_HTTP_MODULE, /* module type */ NULL, /* init master */ NULL, /* init module */ - NULL, /* init process */ + ngx_http_upstream_zone_init_worker, /* init process */ NULL, /* init thread */ NULL, /* exit thread */ NULL, /* exit process */ @@ -188,9 +195,15 @@ ngx_http_upstream_zone_copy_peers(ngx_slab_pool_t *shpool, ngx_http_upstream_srv_conf_t *uscf) { ngx_str_t *name; + ngx_uint_t *config; ngx_http_upstream_rr_peer_t *peer, **peerp; ngx_http_upstream_rr_peers_t *peers, *backup; + config = ngx_slab_calloc(shpool, sizeof(ngx_uint_t)); + if (config == NULL) { + return NULL; + } + peers = ngx_slab_alloc(shpool, sizeof(ngx_http_upstream_rr_peers_t)); if (peers == NULL) { return NULL; @@ -214,6 +227,7 @@ ngx_http_upstream_zone_copy_peers(ngx_slab_pool_t *shpool, peers->name = name; peers->shpool = shpool; + peers->config = config; for (peerp = &peers->peer; *peerp; peerp = &peer->next) { /* pool is unlocked */ @@ -223,6 +237,17 @@ ngx_http_upstream_zone_copy_peers(ngx_slab_pool_t *shpool, } *peerp = peer; + (*peers->config)++; + } + + for (peerp = &peers->resolve; *peerp; peerp = &peer->next) { + peer = ngx_http_upstream_zone_copy_peer(peers, *peerp); + if (peer == NULL) { + return NULL; + } + + *peerp = peer; + (*peers->config)++; } if (peers->next == NULL) { @@ -239,6 +264,7 @@ ngx_http_upstream_zone_copy_peers(ngx_slab_pool_t *shpool, backup->name = name; backup->shpool = shpool; + backup->config = config; for (peerp = &backup->peer; *peerp; peerp = &peer->next) { /* pool is unlocked */ @@ -248,6 +274,17 @@ ngx_http_upstream_zone_copy_peers(ngx_slab_pool_t *shpool, } *peerp = peer; + (*backup->config)++; + } + + for (peerp = &backup->resolve; *peerp; peerp = &peer->next) { + peer = ngx_http_upstream_zone_copy_peer(backup, *peerp); + if (peer == NULL) { + return NULL; + } + + *peerp = peer; + (*backup->config)++; } peers->next = backup; @@ -279,6 +316,7 @@ ngx_http_upstream_zone_copy_peer(ngx_http_upstream_rr_peers_t *peers, dst->sockaddr = NULL; dst->name.data = NULL; dst->server.data = NULL; + dst->host = NULL; } dst->sockaddr = ngx_slab_calloc_locked(pool, sizeof(ngx_sockaddr_t)); @@ -301,12 +339,37 @@ ngx_http_upstream_zone_copy_peer(ngx_http_upstream_rr_peers_t *peers, } ngx_memcpy(dst->server.data, src->server.data, src->server.len); + + if (src->host) { + dst->host = ngx_slab_calloc_locked(pool, + sizeof(ngx_http_upstream_host_t)); + if (dst->host == NULL) { + goto failed; + } + + dst->host->name.data = ngx_slab_alloc_locked(pool, + src->host->name.len); + if (dst->host->name.data == NULL) { + goto failed; + } + + dst->host->peers = peers; + dst->host->peer = dst; + + dst->host->name.len = src->host->name.len; + ngx_memcpy(dst->host->name.data, src->host->name.data, + src->host->name.len); + } } return dst; failed: + if (dst->host) { + ngx_slab_free_locked(pool, dst->host); + } + if (dst->server.data) { ngx_slab_free_locked(pool, dst->server.data); } @@ -323,3 +386,300 @@ failed: return NULL; } + + +static void +ngx_http_upstream_zone_set_single(ngx_http_upstream_srv_conf_t *uscf) +{ + ngx_http_upstream_rr_peers_t *peers; + + peers = uscf->peer.data; + + if (peers->number == 1 + && (peers->next == NULL || peers->next->number == 0)) + { + peers->single = 1; + + } else { + peers->single = 0; + } +} + + +static void +ngx_http_upstream_zone_remove_peer_locked(ngx_http_upstream_rr_peers_t *peers, + ngx_http_upstream_rr_peer_t *peer) +{ + peers->total_weight -= peer->weight; + peers->number--; + peers->tries -= (peer->down == 0); + (*peers->config)++; + peers->weighted = (peers->total_weight != peers->number); + + ngx_http_upstream_rr_peer_free(peers, peer); +} + + +static ngx_int_t +ngx_http_upstream_zone_init_worker(ngx_cycle_t *cycle) +{ + ngx_uint_t i; + ngx_event_t *event; + ngx_http_upstream_rr_peer_t *peer; + ngx_http_upstream_rr_peers_t *peers; + ngx_http_upstream_srv_conf_t *uscf, **uscfp; + ngx_http_upstream_main_conf_t *umcf; + + if (ngx_process != NGX_PROCESS_WORKER + && ngx_process != NGX_PROCESS_SINGLE) + { + return NGX_OK; + } + + umcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_upstream_module); + + if (umcf == NULL) { + return NGX_OK; + } + + uscfp = umcf->upstreams.elts; + + for (i = 0; i < umcf->upstreams.nelts; i++) { + + uscf = uscfp[i]; + + if (uscf->shm_zone == NULL) { + continue; + } + + peers = uscf->peer.data; + + do { + ngx_http_upstream_rr_peers_wlock(peers); + + for (peer = peers->resolve; peer; peer = peer->next) { + + if (peer->host->worker != ngx_worker) { + continue; + } + + event = &peer->host->event; + ngx_memzero(event, sizeof(ngx_event_t)); + + event->data = uscf; + event->handler = ngx_http_upstream_zone_resolve_timer; + event->log = cycle->log; + event->cancelable = 1; + + ngx_add_timer(event, 1); + } + + ngx_http_upstream_rr_peers_unlock(peers); + + peers = peers->next; + + } while (peers); + } + + return NGX_OK; +} + + +static void +ngx_http_upstream_zone_resolve_timer(ngx_event_t *event) +{ + ngx_resolver_ctx_t *ctx; + ngx_http_upstream_host_t *host; + ngx_http_upstream_srv_conf_t *uscf; + + host = (ngx_http_upstream_host_t *) event; + uscf = event->data; + + ctx = ngx_resolve_start(uscf->resolver, NULL); + if (ctx == NULL) { + goto retry; + } + + if (ctx == NGX_NO_RESOLVER) { + ngx_log_error(NGX_LOG_ERR, event->log, 0, + "no resolver defined to resolve %V", &host->name); + return; + } + + ctx->name = host->name; + ctx->handler = ngx_http_upstream_zone_resolve_handler; + ctx->data = host; + ctx->timeout = uscf->resolver_timeout; + ctx->cancelable = 1; + + if (ngx_resolve_name(ctx) == NGX_OK) { + return; + } + +retry: + + ngx_add_timer(event, ngx_max(uscf->resolver_timeout, 1000)); +} + + +static void +ngx_http_upstream_zone_resolve_handler(ngx_resolver_ctx_t *ctx) +{ + time_t now; + in_port_t port; + ngx_msec_t timer; + ngx_uint_t i, j; + ngx_event_t *event; + ngx_resolver_addr_t *addr; + ngx_http_upstream_host_t *host; + ngx_http_upstream_rr_peer_t *peer, *template, **peerp; + ngx_http_upstream_rr_peers_t *peers; + ngx_http_upstream_srv_conf_t *uscf; + + host = ctx->data; + event = &host->event; + uscf = event->data; + peers = host->peers; + template = host->peer; + + ngx_http_upstream_rr_peers_wlock(peers); + + now = ngx_time(); + + if (ctx->state) { + ngx_log_error(NGX_LOG_ERR, event->log, 0, + "%V could not be resolved (%i: %s)", + &ctx->name, ctx->state, + ngx_resolver_strerror(ctx->state)); + + if (ctx->state != NGX_RESOLVE_NXDOMAIN) { + ngx_http_upstream_rr_peers_unlock(peers); + + ngx_resolve_name_done(ctx); + + ngx_add_timer(event, ngx_max(uscf->resolver_timeout, 1000)); + return; + } + + /* NGX_RESOLVE_NXDOMAIN */ + + ctx->naddrs = 0; + } + +#if (NGX_DEBUG) + { + u_char text[NGX_SOCKADDR_STRLEN]; + size_t len; + + for (i = 0; i < ctx->naddrs; i++) { + len = ngx_sock_ntop(ctx->addrs[i].sockaddr, ctx->addrs[i].socklen, + text, NGX_SOCKADDR_STRLEN, 0); + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, event->log, 0, + "name %V was resolved to %*s", &host->name, len, text); + } + } +#endif + + for (peerp = &peers->peer; *peerp; /* void */ ) { + peer = *peerp; + + if (peer->host != host) { + goto next; + } + + for (j = 0; j < ctx->naddrs; j++) { + + addr = &ctx->addrs[j]; + + if (addr->name.len == 0 + && ngx_cmp_sockaddr(peer->sockaddr, peer->socklen, + addr->sockaddr, addr->socklen, 0) + == NGX_OK) + { + addr->name.len = 1; + goto next; + } + } + + *peerp = peer->next; + ngx_http_upstream_zone_remove_peer_locked(peers, peer); + + ngx_http_upstream_zone_set_single(uscf); + + continue; + + next: + + peerp = &peer->next; + } + + for (i = 0; i < ctx->naddrs; i++) { + + addr = &ctx->addrs[i]; + + if (addr->name.len == 1) { + addr->name.len = 0; + continue; + } + + ngx_shmtx_lock(&peers->shpool->mutex); + peer = ngx_http_upstream_zone_copy_peer(peers, NULL); + ngx_shmtx_unlock(&peers->shpool->mutex); + + if (peer == NULL) { + ngx_log_error(NGX_LOG_ERR, event->log, 0, + "cannot add new server to upstream \"%V\", " + "memory exhausted", peers->name); + break; + } + + ngx_memcpy(peer->sockaddr, addr->sockaddr, addr->socklen); + + port = ((struct sockaddr_in *) template->sockaddr)->sin_port; + + switch (peer->sockaddr->sa_family) { +#if (NGX_HAVE_INET6) + case AF_INET6: + ((struct sockaddr_in6 *) peer->sockaddr)->sin6_port = port; + break; +#endif + default: /* AF_INET */ + ((struct sockaddr_in *) peer->sockaddr)->sin_port = port; + } + + peer->socklen = addr->socklen; + + peer->name.len = ngx_sock_ntop(peer->sockaddr, peer->socklen, + peer->name.data, NGX_SOCKADDR_STRLEN, 1); + + peer->host = template->host; + peer->server = template->server; + + peer->weight = template->weight; + peer->effective_weight = peer->weight; + peer->max_conns = template->max_conns; + peer->max_fails = template->max_fails; + peer->fail_timeout = template->fail_timeout; + peer->down = template->down; + + *peerp = peer; + peerp = &peer->next; + + peers->number++; + peers->tries += (peer->down == 0); + peers->total_weight += peer->weight; + peers->weighted = (peers->total_weight != peers->number); + (*peers->config)++; + + ngx_http_upstream_zone_set_single(uscf); + } + + ngx_http_upstream_rr_peers_unlock(peers); + + timer = (ngx_msec_t) 1000 * (ctx->valid > now ? ctx->valid - now + 1 : 1); + + ngx_resolve_name_done(ctx); + + ngx_add_timer(event, timer); +} diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c index d29e503cb..efe772439 100644 --- a/src/http/ngx_http_upstream.c +++ b/src/http/ngx_http_upstream.c @@ -1565,6 +1565,26 @@ ngx_http_upstream_connect(ngx_http_request_t *r, ngx_http_upstream_t *u) u->state->peer = u->peer.name; +#if (NGX_HTTP_UPSTREAM_ZONE) + if (u->upstream && u->upstream->shm_zone + && (u->upstream->flags & NGX_HTTP_UPSTREAM_MODIFY)) + { + u->state->peer = ngx_palloc(r->pool, + sizeof(ngx_str_t) + u->peer.name->len); + if (u->state->peer == NULL) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + u->state->peer->len = u->peer.name->len; + u->state->peer->data = (u_char *) (u->state->peer + 1); + ngx_memcpy(u->state->peer->data, u->peer.name->data, u->peer.name->len); + + u->peer.name = u->state->peer; + } +#endif + if (rc == NGX_BUSY) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "no live upstreams"); ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_NOLIVE); @@ -6066,6 +6086,7 @@ ngx_http_upstream(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy) u.no_port = 1; uscf = ngx_http_upstream_add(cf, &u, NGX_HTTP_UPSTREAM_CREATE + |NGX_HTTP_UPSTREAM_MODIFY |NGX_HTTP_UPSTREAM_WEIGHT |NGX_HTTP_UPSTREAM_MAX_CONNS |NGX_HTTP_UPSTREAM_MAX_FAILS @@ -6171,6 +6192,9 @@ ngx_http_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) ngx_url_t u; ngx_int_t weight, max_conns, max_fails; ngx_uint_t i; +#if (NGX_HTTP_UPSTREAM_ZONE) + ngx_uint_t resolve; +#endif ngx_http_upstream_server_t *us; us = ngx_array_push(uscf->servers); @@ -6186,6 +6210,9 @@ ngx_http_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) max_conns = 0; max_fails = 1; fail_timeout = 10; +#if (NGX_HTTP_UPSTREAM_ZONE) + resolve = 0; +#endif for (i = 2; i < cf->args->nelts; i++) { @@ -6274,6 +6301,13 @@ ngx_http_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) continue; } +#if (NGX_HTTP_UPSTREAM_ZONE) + if (ngx_strcmp(value[i].data, "resolve") == 0) { + resolve = 1; + continue; + } +#endif + goto invalid; } @@ -6282,6 +6316,13 @@ ngx_http_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) u.url = value[1]; u.default_port = 80; +#if (NGX_HTTP_UPSTREAM_ZONE) + if (resolve) { + /* resolve at run time */ + u.no_resolve = 1; + } +#endif + if (ngx_parse_url(cf->pool, &u) != NGX_OK) { if (u.err) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, @@ -6292,8 +6333,45 @@ ngx_http_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) } us->name = u.url; + +#if (NGX_HTTP_UPSTREAM_ZONE) + + if (resolve && u.naddrs == 0) { + ngx_addr_t *addr; + + /* save port */ + + addr = ngx_pcalloc(cf->pool, sizeof(ngx_addr_t)); + if (addr == NULL) { + return NGX_CONF_ERROR; + } + + addr->sockaddr = ngx_palloc(cf->pool, u.socklen); + if (addr->sockaddr == NULL) { + return NGX_CONF_ERROR; + } + + ngx_memcpy(addr->sockaddr, &u.sockaddr, u.socklen); + + addr->socklen = u.socklen; + + us->addrs = addr; + us->naddrs = 1; + + us->host = u.host; + + } else { + us->addrs = u.addrs; + us->naddrs = u.naddrs; + } + +#else + us->addrs = u.addrs; us->naddrs = u.naddrs; + +#endif + us->weight = weight; us->max_conns = max_conns; us->max_fails = max_fails; diff --git a/src/http/ngx_http_upstream.h b/src/http/ngx_http_upstream.h index db0e1424e..0922021bd 100644 --- a/src/http/ngx_http_upstream.h +++ b/src/http/ngx_http_upstream.h @@ -104,7 +104,11 @@ typedef struct { unsigned backup:1; - NGX_COMPAT_BEGIN(6) +#if (NGX_HTTP_UPSTREAM_ZONE) + ngx_str_t host; +#endif + + NGX_COMPAT_BEGIN(4) NGX_COMPAT_END } ngx_http_upstream_server_t; @@ -115,6 +119,7 @@ typedef struct { #define NGX_HTTP_UPSTREAM_FAIL_TIMEOUT 0x0008 #define NGX_HTTP_UPSTREAM_DOWN 0x0010 #define NGX_HTTP_UPSTREAM_BACKUP 0x0020 +#define NGX_HTTP_UPSTREAM_MODIFY 0x0040 #define NGX_HTTP_UPSTREAM_MAX_CONNS 0x0100 @@ -133,6 +138,8 @@ struct ngx_http_upstream_srv_conf_s { #if (NGX_HTTP_UPSTREAM_ZONE) ngx_shm_zone_t *shm_zone; + ngx_resolver_t *resolver; + ngx_msec_t resolver_timeout; #endif }; diff --git a/src/http/ngx_http_upstream_round_robin.c b/src/http/ngx_http_upstream_round_robin.c index 1f15fae50..cc1b6d1a2 100644 --- a/src/http/ngx_http_upstream_round_robin.c +++ b/src/http/ngx_http_upstream_round_robin.c @@ -32,10 +32,15 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us) { ngx_url_t u; - ngx_uint_t i, j, n, w, t; + ngx_uint_t i, j, n, r, w, t; ngx_http_upstream_server_t *server; ngx_http_upstream_rr_peer_t *peer, **peerp; ngx_http_upstream_rr_peers_t *peers, *backup; +#if (NGX_HTTP_UPSTREAM_ZONE) + ngx_uint_t resolve; + ngx_http_core_loc_conf_t *clcf; + ngx_http_upstream_rr_peer_t **rpeerp; +#endif us->peer.init = ngx_http_upstream_init_round_robin_peer; @@ -43,14 +48,33 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf, server = us->servers->elts; n = 0; + r = 0; w = 0; t = 0; +#if (NGX_HTTP_UPSTREAM_ZONE) + resolve = 0; +#endif + for (i = 0; i < us->servers->nelts; i++) { + +#if (NGX_HTTP_UPSTREAM_ZONE) + if (server[i].host.len) { + resolve = 1; + } +#endif + if (server[i].backup) { continue; } +#if (NGX_HTTP_UPSTREAM_ZONE) + if (server[i].host.len) { + r++; + continue; + } +#endif + n += server[i].naddrs; w += server[i].naddrs * server[i].weight; @@ -59,7 +83,53 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf, } } - if (n == 0) { +#if (NGX_HTTP_UPSTREAM_ZONE) + if (us->shm_zone) { + + if (resolve && !(us->flags & NGX_HTTP_UPSTREAM_MODIFY)) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "load balancing method does not support" + " resolving names at run time in" + " upstream \"%V\" in %s:%ui", + &us->host, us->file_name, us->line); + return NGX_ERROR; + } + + clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); + + us->resolver = clcf->resolver; + us->resolver_timeout = clcf->resolver_timeout; + + /* + * Without "resolver_timeout" in http{}, the value is unset. + * Even if we set it in ngx_http_core_merge_loc_conf(), it's + * still dependent on the module order and unreliable. + */ + ngx_conf_init_msec_value(us->resolver_timeout, 30000); + + if (resolve + && (us->resolver == NULL + || us->resolver->connections.nelts == 0)) + { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no resolver defined to resolve names" + " at run time in upstream \"%V\" in %s:%ui", + &us->host, us->file_name, us->line); + return NGX_ERROR; + } + + } else if (resolve) { + + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "resolving names at run time requires" + " upstream \"%V\" in %s:%ui" + " to be in shared memory", + &us->host, us->file_name, us->line); + return NGX_ERROR; + } +#endif + + if (n + r == 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); @@ -71,7 +141,8 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf, return NGX_ERROR; } - peer = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peer_t) * n); + peer = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peer_t) + * (n + r)); if (peer == NULL) { return NGX_ERROR; } @@ -86,11 +157,46 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf, n = 0; peerp = &peers->peer; +#if (NGX_HTTP_UPSTREAM_ZONE) + rpeerp = &peers->resolve; +#endif + for (i = 0; i < us->servers->nelts; i++) { if (server[i].backup) { continue; } +#if (NGX_HTTP_UPSTREAM_ZONE) + if (server[i].host.len) { + + peer[n].host = ngx_pcalloc(cf->pool, + sizeof(ngx_http_upstream_host_t)); + if (peer[n].host == NULL) { + return NGX_ERROR; + } + + peer[n].host->name = server[i].host; + + peer[n].sockaddr = server[i].addrs[0].sockaddr; + peer[n].socklen = server[i].addrs[0].socklen; + peer[n].name = server[i].addrs[0].name; + peer[n].weight = server[i].weight; + peer[n].effective_weight = server[i].weight; + peer[n].current_weight = 0; + peer[n].max_conns = server[i].max_conns; + 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; + + *rpeerp = &peer[n]; + rpeerp = &peer[n].next; + n++; + + continue; + } +#endif + for (j = 0; j < server[i].naddrs; j++) { peer[n].sockaddr = server[i].addrs[j].sockaddr; peer[n].socklen = server[i].addrs[j].socklen; @@ -115,6 +221,7 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf, /* backup servers */ n = 0; + r = 0; w = 0; t = 0; @@ -123,6 +230,13 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf, continue; } +#if (NGX_HTTP_UPSTREAM_ZONE) + if (server[i].host.len) { + r++; + continue; + } +#endif + n += server[i].naddrs; w += server[i].naddrs * server[i].weight; @@ -131,7 +245,7 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf, } } - if (n == 0) { + if (n + r == 0) { return NGX_OK; } @@ -140,12 +254,16 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf, return NGX_ERROR; } - peer = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peer_t) * n); + peer = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peer_t) + * (n + r)); if (peer == NULL) { return NGX_ERROR; } - peers->single = 0; + if (n > 0) { + peers->single = 0; + } + backup->single = 0; backup->number = n; backup->weighted = (w != n); @@ -156,11 +274,46 @@ ngx_http_upstream_init_round_robin(ngx_conf_t *cf, n = 0; peerp = &backup->peer; +#if (NGX_HTTP_UPSTREAM_ZONE) + rpeerp = &backup->resolve; +#endif + for (i = 0; i < us->servers->nelts; i++) { if (!server[i].backup) { continue; } +#if (NGX_HTTP_UPSTREAM_ZONE) + if (server[i].host.len) { + + peer[n].host = ngx_pcalloc(cf->pool, + sizeof(ngx_http_upstream_host_t)); + if (peer[n].host == NULL) { + return NGX_ERROR; + } + + peer[n].host->name = server[i].host; + + peer[n].sockaddr = server[i].addrs[0].sockaddr; + peer[n].socklen = server[i].addrs[0].socklen; + peer[n].name = server[i].addrs[0].name; + peer[n].weight = server[i].weight; + peer[n].effective_weight = server[i].weight; + peer[n].current_weight = 0; + peer[n].max_conns = server[i].max_conns; + 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; + + *rpeerp = &peer[n]; + rpeerp = &peer[n].next; + n++; + + continue; + } +#endif + for (j = 0; j < server[i].naddrs; j++) { peer[n].sockaddr = server[i].addrs[j].sockaddr; peer[n].socklen = server[i].addrs[j].socklen; @@ -273,7 +426,12 @@ ngx_http_upstream_init_round_robin_peer(ngx_http_request_t *r, rrp->peers = us->peer.data; rrp->current = NULL; - rrp->config = 0; + + ngx_http_upstream_rr_peers_rlock(rrp->peers); + +#if (NGX_HTTP_UPSTREAM_ZONE) + rrp->config = rrp->peers->config ? *rrp->peers->config : 0; +#endif n = rrp->peers->number; @@ -281,6 +439,10 @@ ngx_http_upstream_init_round_robin_peer(ngx_http_request_t *r, n = rrp->peers->next->number; } + r->upstream->peer.tries = ngx_http_upstream_tries(rrp->peers); + + ngx_http_upstream_rr_peers_unlock(rrp->peers); + if (n <= 8 * sizeof(uintptr_t)) { rrp->tried = &rrp->data; rrp->data = 0; @@ -296,7 +458,6 @@ ngx_http_upstream_init_round_robin_peer(ngx_http_request_t *r, r->upstream->peer.get = ngx_http_upstream_get_round_robin_peer; r->upstream->peer.free = ngx_http_upstream_free_round_robin_peer; - r->upstream->peer.tries = ngx_http_upstream_tries(rrp->peers); #if (NGX_HTTP_SSL) r->upstream->peer.set_session = ngx_http_upstream_set_round_robin_peer_session; @@ -446,6 +607,12 @@ ngx_http_upstream_get_round_robin_peer(ngx_peer_connection_t *pc, void *data) peers = rrp->peers; ngx_http_upstream_rr_peers_wlock(peers); +#if (NGX_HTTP_UPSTREAM_ZONE) + if (peers->config && rrp->config != *peers->config) { + goto busy; + } +#endif + if (peers->single) { peer = peers->peer; @@ -458,6 +625,7 @@ ngx_http_upstream_get_round_robin_peer(ngx_peer_connection_t *pc, void *data) } rrp->current = peer; + ngx_http_upstream_rr_peer_ref(peers, peer); } else { @@ -510,6 +678,10 @@ failed: ngx_http_upstream_rr_peers_wlock(peers); } +#if (NGX_HTTP_UPSTREAM_ZONE) +busy: +#endif + ngx_http_upstream_rr_peers_unlock(peers); pc->name = peers->name; @@ -580,6 +752,7 @@ ngx_http_upstream_get_peer(ngx_http_upstream_rr_peer_data_t *rrp) } rrp->current = best; + ngx_http_upstream_rr_peer_ref(rrp->peers, best); n = p / (8 * sizeof(uintptr_t)); m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t)); @@ -617,9 +790,16 @@ ngx_http_upstream_free_round_robin_peer(ngx_peer_connection_t *pc, void *data, if (rrp->peers->single) { + if (peer->fails) { + peer->fails = 0; + } + peer->conns--; - ngx_http_upstream_rr_peer_unlock(rrp->peers, peer); + if (ngx_http_upstream_rr_peer_unref(rrp->peers, peer) == NGX_OK) { + ngx_http_upstream_rr_peer_unlock(rrp->peers, peer); + } + ngx_http_upstream_rr_peers_unlock(rrp->peers); pc->tries = 0; @@ -661,7 +841,10 @@ 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); + if (ngx_http_upstream_rr_peer_unref(rrp->peers, peer) == NGX_OK) { + ngx_http_upstream_rr_peer_unlock(rrp->peers, peer); + } + ngx_http_upstream_rr_peers_unlock(rrp->peers); if (pc->tries) { diff --git a/src/http/ngx_http_upstream_round_robin.h b/src/http/ngx_http_upstream_round_robin.h index 922ceaa04..06437f405 100644 --- a/src/http/ngx_http_upstream_round_robin.h +++ b/src/http/ngx_http_upstream_round_robin.h @@ -14,8 +14,23 @@ #include +typedef struct ngx_http_upstream_rr_peers_s ngx_http_upstream_rr_peers_t; typedef struct ngx_http_upstream_rr_peer_s ngx_http_upstream_rr_peer_t; + +#if (NGX_HTTP_UPSTREAM_ZONE) + +typedef struct { + ngx_event_t event; /* must be first */ + ngx_uint_t worker; + ngx_str_t name; + ngx_http_upstream_rr_peers_t *peers; + ngx_http_upstream_rr_peer_t *peer; +} ngx_http_upstream_host_t; + +#endif + + struct ngx_http_upstream_rr_peer_s { struct sockaddr *sockaddr; socklen_t socklen; @@ -46,24 +61,28 @@ struct ngx_http_upstream_rr_peer_s { #endif #if (NGX_HTTP_UPSTREAM_ZONE) + unsigned zombie:1; + ngx_atomic_t lock; + ngx_uint_t refs; + ngx_http_upstream_host_t *host; #endif ngx_http_upstream_rr_peer_t *next; - NGX_COMPAT_BEGIN(32) + NGX_COMPAT_BEGIN(15) NGX_COMPAT_END }; -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; + ngx_uint_t *config; + ngx_http_upstream_rr_peer_t *resolve; ngx_http_upstream_rr_peers_t *zone_next; #endif @@ -114,6 +133,65 @@ struct ngx_http_upstream_rr_peers_s { ngx_rwlock_unlock(&peer->lock); \ } + +#define ngx_http_upstream_rr_peer_ref(peers, peer) \ + (peer)->refs++; + + +static ngx_inline void +ngx_http_upstream_rr_peer_free_locked(ngx_http_upstream_rr_peers_t *peers, + ngx_http_upstream_rr_peer_t *peer) +{ + if (peer->refs) { + peer->zombie = 1; + return; + } + + ngx_slab_free_locked(peers->shpool, peer->sockaddr); + ngx_slab_free_locked(peers->shpool, peer->name.data); + + if (peer->server.data && (peer->host == NULL || peer->host->peer == peer)) { + ngx_slab_free_locked(peers->shpool, peer->server.data); + } + +#if (NGX_HTTP_SSL) + if (peer->ssl_session) { + ngx_slab_free_locked(peers->shpool, peer->ssl_session); + } +#endif + + ngx_slab_free_locked(peers->shpool, peer); +} + + +static ngx_inline void +ngx_http_upstream_rr_peer_free(ngx_http_upstream_rr_peers_t *peers, + ngx_http_upstream_rr_peer_t *peer) +{ + ngx_shmtx_lock(&peers->shpool->mutex); + ngx_http_upstream_rr_peer_free_locked(peers, peer); + ngx_shmtx_unlock(&peers->shpool->mutex); +} + + +static ngx_inline ngx_int_t +ngx_http_upstream_rr_peer_unref(ngx_http_upstream_rr_peers_t *peers, + ngx_http_upstream_rr_peer_t *peer) +{ + peer->refs--; + + if (peers->shpool == NULL) { + return NGX_OK; + } + + if (peer->refs == 0 && peer->zombie) { + ngx_http_upstream_rr_peer_free(peers, peer); + return NGX_DONE; + } + + return NGX_OK; +} + #else #define ngx_http_upstream_rr_peers_rlock(peers) @@ -121,6 +199,8 @@ struct ngx_http_upstream_rr_peers_s { #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) +#define ngx_http_upstream_rr_peer_ref(peers, peer) +#define ngx_http_upstream_rr_peer_unref(peers, peer) NGX_OK #endif diff --git a/src/stream/ngx_stream_proxy_module.c b/src/stream/ngx_stream_proxy_module.c index bbf4f7ec0..0890a04ae 100644 --- a/src/stream/ngx_stream_proxy_module.c +++ b/src/stream/ngx_stream_proxy_module.c @@ -742,6 +742,25 @@ ngx_stream_proxy_connect(ngx_stream_session_t *s) u->state->peer = u->peer.name; +#if (NGX_STREAM_UPSTREAM_ZONE) + if (u->upstream && u->upstream->shm_zone + && (u->upstream->flags & NGX_STREAM_UPSTREAM_MODIFY)) + { + u->state->peer = ngx_palloc(s->connection->pool, + sizeof(ngx_str_t) + u->peer.name->len); + if (u->state->peer == NULL) { + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + u->state->peer->len = u->peer.name->len; + u->state->peer->data = (u_char *) (u->state->peer + 1); + ngx_memcpy(u->state->peer->data, u->peer.name->data, u->peer.name->len); + + u->peer.name = u->state->peer; + } +#endif + if (rc == NGX_BUSY) { ngx_log_error(NGX_LOG_ERR, c->log, 0, "no live upstreams"); ngx_stream_proxy_finalize(s, NGX_STREAM_BAD_GATEWAY); diff --git a/src/stream/ngx_stream_upstream.c b/src/stream/ngx_stream_upstream.c index eadcf9f9a..a251cca00 100644 --- a/src/stream/ngx_stream_upstream.c +++ b/src/stream/ngx_stream_upstream.c @@ -319,6 +319,7 @@ ngx_stream_upstream(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy) u.no_port = 1; uscf = ngx_stream_upstream_add(cf, &u, NGX_STREAM_UPSTREAM_CREATE + |NGX_STREAM_UPSTREAM_MODIFY |NGX_STREAM_UPSTREAM_WEIGHT |NGX_STREAM_UPSTREAM_MAX_CONNS |NGX_STREAM_UPSTREAM_MAX_FAILS @@ -408,6 +409,9 @@ ngx_stream_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) ngx_url_t u; ngx_int_t weight, max_conns, max_fails; ngx_uint_t i; +#if (NGX_STREAM_UPSTREAM_ZONE) + ngx_uint_t resolve; +#endif ngx_stream_upstream_server_t *us; us = ngx_array_push(uscf->servers); @@ -423,6 +427,9 @@ ngx_stream_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) max_conns = 0; max_fails = 1; fail_timeout = 10; +#if (NGX_STREAM_UPSTREAM_ZONE) + resolve = 0; +#endif for (i = 2; i < cf->args->nelts; i++) { @@ -511,6 +518,13 @@ ngx_stream_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) continue; } +#if (NGX_STREAM_UPSTREAM_ZONE) + if (ngx_strcmp(value[i].data, "resolve") == 0) { + resolve = 1; + continue; + } +#endif + goto invalid; } @@ -518,6 +532,13 @@ ngx_stream_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) u.url = value[1]; +#if (NGX_STREAM_UPSTREAM_ZONE) + if (resolve) { + /* resolve at run time */ + u.no_resolve = 1; + } +#endif + if (ngx_parse_url(cf->pool, &u) != NGX_OK) { if (u.err) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, @@ -534,8 +555,45 @@ ngx_stream_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) } us->name = u.url; + +#if (NGX_STREAM_UPSTREAM_ZONE) + + if (resolve && u.naddrs == 0) { + ngx_addr_t *addr; + + /* save port */ + + addr = ngx_pcalloc(cf->pool, sizeof(ngx_addr_t)); + if (addr == NULL) { + return NGX_CONF_ERROR; + } + + addr->sockaddr = ngx_palloc(cf->pool, u.socklen); + if (addr->sockaddr == NULL) { + return NGX_CONF_ERROR; + } + + ngx_memcpy(addr->sockaddr, &u.sockaddr, u.socklen); + + addr->socklen = u.socklen; + + us->addrs = addr; + us->naddrs = 1; + + us->host = u.host; + + } else { + us->addrs = u.addrs; + us->naddrs = u.naddrs; + } + +#else + us->addrs = u.addrs; us->naddrs = u.naddrs; + +#endif + us->weight = weight; us->max_conns = max_conns; us->max_fails = max_fails; diff --git a/src/stream/ngx_stream_upstream.h b/src/stream/ngx_stream_upstream.h index f5617794f..686e9269c 100644 --- a/src/stream/ngx_stream_upstream.h +++ b/src/stream/ngx_stream_upstream.h @@ -21,6 +21,7 @@ #define NGX_STREAM_UPSTREAM_FAIL_TIMEOUT 0x0008 #define NGX_STREAM_UPSTREAM_DOWN 0x0010 #define NGX_STREAM_UPSTREAM_BACKUP 0x0020 +#define NGX_STREAM_UPSTREAM_MODIFY 0x0040 #define NGX_STREAM_UPSTREAM_MAX_CONNS 0x0100 @@ -62,7 +63,11 @@ typedef struct { unsigned backup:1; - NGX_COMPAT_BEGIN(4) +#if (NGX_STREAM_UPSTREAM_ZONE) + ngx_str_t host; +#endif + + NGX_COMPAT_BEGIN(2) NGX_COMPAT_END } ngx_stream_upstream_server_t; @@ -83,6 +88,8 @@ struct ngx_stream_upstream_srv_conf_s { #if (NGX_STREAM_UPSTREAM_ZONE) ngx_shm_zone_t *shm_zone; + ngx_resolver_t *resolver; + ngx_msec_t resolver_timeout; #endif }; diff --git a/src/stream/ngx_stream_upstream_hash_module.c b/src/stream/ngx_stream_upstream_hash_module.c index b764fcbe0..20fca6c0d 100644 --- a/src/stream/ngx_stream_upstream_hash_module.c +++ b/src/stream/ngx_stream_upstream_hash_module.c @@ -23,6 +23,9 @@ typedef struct { typedef struct { +#if (NGX_STREAM_UPSTREAM_ZONE) + ngx_uint_t config; +#endif ngx_stream_complex_value_t key; ngx_stream_upstream_chash_points_t *points; } ngx_stream_upstream_hash_srv_conf_t; @@ -49,6 +52,8 @@ static ngx_int_t ngx_stream_upstream_get_hash_peer(ngx_peer_connection_t *pc, static ngx_int_t ngx_stream_upstream_init_chash(ngx_conf_t *cf, ngx_stream_upstream_srv_conf_t *us); +static ngx_int_t ngx_stream_upstream_update_chash(ngx_pool_t *pool, + 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( @@ -178,10 +183,17 @@ ngx_stream_upstream_get_hash_peer(ngx_peer_connection_t *pc, void *data) ngx_stream_upstream_rr_peers_rlock(hp->rrp.peers); - if (hp->tries > 20 || hp->rrp.peers->single || hp->key.len == 0) { + if (hp->tries > 20 || hp->rrp.peers->number < 2 || hp->key.len == 0) { + ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers); + return hp->get_rr_peer(pc, &hp->rrp); + } + +#if (NGX_STREAM_UPSTREAM_ZONE) + if (hp->rrp.peers->config && hp->rrp.config != *hp->rrp.peers->config) { ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers); return hp->get_rr_peer(pc, &hp->rrp); } +#endif now = ngx_time(); @@ -261,6 +273,7 @@ ngx_stream_upstream_get_hash_peer(ngx_peer_connection_t *pc, void *data) } hp->rrp.current = peer; + ngx_stream_upstream_rr_peer_ref(hp->rrp.peers, peer); pc->sockaddr = peer->sockaddr; pc->socklen = peer->socklen; @@ -284,6 +297,26 @@ 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) +{ + if (ngx_stream_upstream_init_round_robin(cf, us) != NGX_OK) { + return NGX_ERROR; + } + + us->peer.init = ngx_stream_upstream_init_chash_peer; + +#if (NGX_STREAM_UPSTREAM_ZONE) + if (us->shm_zone) { + return NGX_OK; + } +#endif + + return ngx_stream_upstream_update_chash(cf->pool, us); +} + + +static ngx_int_t +ngx_stream_upstream_update_chash(ngx_pool_t *pool, + ngx_stream_upstream_srv_conf_t *us) { u_char *host, *port, c; size_t host_len, port_len, size; @@ -299,25 +332,33 @@ ngx_stream_upstream_init_chash(ngx_conf_t *cf, u_char byte[4]; } prev_hash; - if (ngx_stream_upstream_init_round_robin(cf, us) != NGX_OK) { - return NGX_ERROR; - } + hcf = ngx_stream_conf_upstream_srv_conf(us, + ngx_stream_upstream_hash_module); - us->peer.init = ngx_stream_upstream_init_chash_peer; + if (hcf->points) { + ngx_free(hcf->points); + hcf->points = NULL; + } 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); + - sizeof(ngx_stream_upstream_chash_point_t) + + sizeof(ngx_stream_upstream_chash_point_t) * npoints; - points = ngx_palloc(cf->pool, size); + points = pool ? ngx_palloc(pool, size) : ngx_alloc(size, ngx_cycle->log); if (points == NULL) { return NGX_ERROR; } points->number = 0; + if (npoints == 0) { + hcf->points = points; + return NGX_OK; + } + for (peer = peers->peer; peer; peer = peer->next) { server = &peer->server; @@ -401,8 +442,6 @@ ngx_stream_upstream_init_chash(ngx_conf_t *cf, points->number = i + 1; - hcf = ngx_stream_conf_upstream_srv_conf(us, - ngx_stream_upstream_hash_module); hcf->points = points; return NGX_OK; @@ -483,7 +522,22 @@ ngx_stream_upstream_init_chash_peer(ngx_stream_session_t *s, ngx_stream_upstream_rr_peers_rlock(hp->rrp.peers); - hp->hash = ngx_stream_upstream_find_chash_point(hcf->points, hash); +#if (NGX_STREAM_UPSTREAM_ZONE) + if (hp->rrp.peers->config + && (hcf->points == NULL || hcf->config != *hp->rrp.peers->config)) + { + if (ngx_stream_upstream_update_chash(NULL, us) != NGX_OK) { + ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers); + return NGX_ERROR; + } + + hcf->config = *hp->rrp.peers->config; + } +#endif + + if (hcf->points->number) { + hp->hash = ngx_stream_upstream_find_chash_point(hcf->points, hash); + } ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers); @@ -518,6 +572,20 @@ ngx_stream_upstream_get_chash_peer(ngx_peer_connection_t *pc, void *data) pc->connection = NULL; + if (hp->rrp.peers->number == 0) { + pc->name = hp->rrp.peers->name; + ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers); + return NGX_BUSY; + } + +#if (NGX_STREAM_UPSTREAM_ZONE) + if (hp->rrp.peers->config && hp->rrp.config != *hp->rrp.peers->config) { + pc->name = hp->rrp.peers->name; + ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers); + return NGX_BUSY; + } +#endif + now = ngx_time(); hcf = hp->conf; @@ -596,6 +664,7 @@ ngx_stream_upstream_get_chash_peer(ngx_peer_connection_t *pc, void *data) } hp->rrp.current = best; + ngx_stream_upstream_rr_peer_ref(hp->rrp.peers, best); pc->sockaddr = best->sockaddr; pc->socklen = best->socklen; @@ -663,6 +732,7 @@ ngx_stream_upstream_hash(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) } uscf->flags = NGX_STREAM_UPSTREAM_CREATE + |NGX_STREAM_UPSTREAM_MODIFY |NGX_STREAM_UPSTREAM_WEIGHT |NGX_STREAM_UPSTREAM_MAX_CONNS |NGX_STREAM_UPSTREAM_MAX_FAILS diff --git a/src/stream/ngx_stream_upstream_least_conn_module.c b/src/stream/ngx_stream_upstream_least_conn_module.c index 739b20a9f..6e8360440 100644 --- a/src/stream/ngx_stream_upstream_least_conn_module.c +++ b/src/stream/ngx_stream_upstream_least_conn_module.c @@ -120,6 +120,12 @@ ngx_stream_upstream_get_least_conn_peer(ngx_peer_connection_t *pc, void *data) ngx_stream_upstream_rr_peers_wlock(peers); +#if (NGX_STREAM_UPSTREAM_ZONE) + if (peers->config && rrp->config != *peers->config) { + goto busy; + } +#endif + best = NULL; total = 0; @@ -240,6 +246,7 @@ ngx_stream_upstream_get_least_conn_peer(ngx_peer_connection_t *pc, void *data) best->conns++; rrp->current = best; + ngx_stream_upstream_rr_peer_ref(peers, best); n = p / (8 * sizeof(uintptr_t)); m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t)); @@ -276,6 +283,10 @@ failed: ngx_stream_upstream_rr_peers_wlock(peers); } +#if (NGX_STREAM_UPSTREAM_ZONE) +busy: +#endif + ngx_stream_upstream_rr_peers_unlock(peers); pc->name = peers->name; @@ -299,6 +310,7 @@ ngx_stream_upstream_least_conn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) uscf->peer.init_upstream = ngx_stream_upstream_init_least_conn; uscf->flags = NGX_STREAM_UPSTREAM_CREATE + |NGX_STREAM_UPSTREAM_MODIFY |NGX_STREAM_UPSTREAM_WEIGHT |NGX_STREAM_UPSTREAM_MAX_CONNS |NGX_STREAM_UPSTREAM_MAX_FAILS diff --git a/src/stream/ngx_stream_upstream_random_module.c b/src/stream/ngx_stream_upstream_random_module.c index 402c1b298..71f76f8d8 100644 --- a/src/stream/ngx_stream_upstream_random_module.c +++ b/src/stream/ngx_stream_upstream_random_module.c @@ -17,6 +17,9 @@ typedef struct { typedef struct { ngx_uint_t two; +#if (NGX_STREAM_UPSTREAM_ZONE) + ngx_uint_t config; +#endif ngx_stream_upstream_random_range_t *ranges; } ngx_stream_upstream_random_srv_conf_t; @@ -125,6 +128,11 @@ ngx_stream_upstream_update_random(ngx_pool_t *pool, rcf = ngx_stream_conf_upstream_srv_conf(us, ngx_stream_upstream_random_module); + if (rcf->ranges) { + ngx_free(rcf->ranges); + rcf->ranges = NULL; + } + peers = us->peer.data; size = peers->number * sizeof(ngx_stream_upstream_random_range_t); @@ -186,11 +194,15 @@ ngx_stream_upstream_init_random_peer(ngx_stream_session_t *s, ngx_stream_upstream_rr_peers_rlock(rp->rrp.peers); #if (NGX_STREAM_UPSTREAM_ZONE) - if (rp->rrp.peers->shpool && rcf->ranges == NULL) { + if (rp->rrp.peers->config + && (rcf->ranges == NULL || rcf->config != *rp->rrp.peers->config)) + { if (ngx_stream_upstream_update_random(NULL, us) != NGX_OK) { ngx_stream_upstream_rr_peers_unlock(rp->rrp.peers); return NGX_ERROR; } + + rcf->config = *rp->rrp.peers->config; } #endif @@ -220,11 +232,18 @@ ngx_stream_upstream_get_random_peer(ngx_peer_connection_t *pc, void *data) ngx_stream_upstream_rr_peers_rlock(peers); - if (rp->tries > 20 || peers->single) { + if (rp->tries > 20 || peers->number < 2) { ngx_stream_upstream_rr_peers_unlock(peers); return ngx_stream_upstream_get_round_robin_peer(pc, rrp); } +#if (NGX_STREAM_UPSTREAM_ZONE) + if (peers->config && rrp->config != *peers->config) { + ngx_stream_upstream_rr_peers_unlock(peers); + return ngx_stream_upstream_get_round_robin_peer(pc, rrp); + } +#endif + pc->cached = 0; pc->connection = NULL; @@ -274,6 +293,7 @@ ngx_stream_upstream_get_random_peer(ngx_peer_connection_t *pc, void *data) } rrp->current = peer; + ngx_stream_upstream_rr_peer_ref(peers, peer); if (now - peer->checked > peer->fail_timeout) { peer->checked = now; @@ -314,10 +334,17 @@ ngx_stream_upstream_get_random2_peer(ngx_peer_connection_t *pc, void *data) ngx_stream_upstream_rr_peers_wlock(peers); - if (rp->tries > 20 || peers->single) { + if (rp->tries > 20 || peers->number < 2) { + ngx_stream_upstream_rr_peers_unlock(peers); + return ngx_stream_upstream_get_round_robin_peer(pc, rrp); + } + +#if (NGX_STREAM_UPSTREAM_ZONE) + if (peers->config && rrp->config != *peers->config) { ngx_stream_upstream_rr_peers_unlock(peers); return ngx_stream_upstream_get_round_robin_peer(pc, rrp); } +#endif pc->cached = 0; pc->connection = NULL; @@ -384,6 +411,7 @@ ngx_stream_upstream_get_random2_peer(ngx_peer_connection_t *pc, void *data) } rrp->current = peer; + ngx_stream_upstream_rr_peer_ref(peers, peer); if (now - peer->checked > peer->fail_timeout) { peer->checked = now; @@ -467,6 +495,7 @@ ngx_stream_upstream_random(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) uscf->peer.init_upstream = ngx_stream_upstream_init_random; uscf->flags = NGX_STREAM_UPSTREAM_CREATE + |NGX_STREAM_UPSTREAM_MODIFY |NGX_STREAM_UPSTREAM_WEIGHT |NGX_STREAM_UPSTREAM_MAX_CONNS |NGX_STREAM_UPSTREAM_MAX_FAILS diff --git a/src/stream/ngx_stream_upstream_round_robin.c b/src/stream/ngx_stream_upstream_round_robin.c index ae3bf37a6..a18171a39 100644 --- a/src/stream/ngx_stream_upstream_round_robin.c +++ b/src/stream/ngx_stream_upstream_round_robin.c @@ -38,10 +38,15 @@ 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, t; + ngx_uint_t i, j, n, r, w, t; ngx_stream_upstream_server_t *server; ngx_stream_upstream_rr_peer_t *peer, **peerp; ngx_stream_upstream_rr_peers_t *peers, *backup; +#if (NGX_STREAM_UPSTREAM_ZONE) + ngx_uint_t resolve; + ngx_stream_core_srv_conf_t *cscf; + ngx_stream_upstream_rr_peer_t **rpeerp; +#endif us->peer.init = ngx_stream_upstream_init_round_robin_peer; @@ -49,14 +54,33 @@ ngx_stream_upstream_init_round_robin(ngx_conf_t *cf, server = us->servers->elts; n = 0; + r = 0; w = 0; t = 0; +#if (NGX_STREAM_UPSTREAM_ZONE) + resolve = 0; +#endif + for (i = 0; i < us->servers->nelts; i++) { + +#if (NGX_STREAM_UPSTREAM_ZONE) + if (server[i].host.len) { + resolve = 1; + } +#endif + if (server[i].backup) { continue; } +#if (NGX_STREAM_UPSTREAM_ZONE) + if (server[i].host.len) { + r++; + continue; + } +#endif + n += server[i].naddrs; w += server[i].naddrs * server[i].weight; @@ -65,7 +89,54 @@ ngx_stream_upstream_init_round_robin(ngx_conf_t *cf, } } - if (n == 0) { +#if (NGX_STREAM_UPSTREAM_ZONE) + if (us->shm_zone) { + + if (resolve && !(us->flags & NGX_STREAM_UPSTREAM_MODIFY)) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "load balancing method does not support" + " resolving names at run time in" + " upstream \"%V\" in %s:%ui", + &us->host, us->file_name, us->line); + return NGX_ERROR; + } + + cscf = ngx_stream_conf_get_module_srv_conf(cf, + ngx_stream_core_module); + + us->resolver = cscf->resolver; + us->resolver_timeout = cscf->resolver_timeout; + + /* + * Without "resolver_timeout" in stream{}, the value is unset. + * Even if we set it in ngx_stream_core_merge_srv_conf(), it's + * still dependent on the module order and unreliable. + */ + ngx_conf_init_msec_value(us->resolver_timeout, 30000); + + if (resolve + && (us->resolver == NULL + || us->resolver->connections.nelts == 0)) + { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no resolver defined to resolve names" + " at run time in upstream \"%V\" in %s:%ui", + &us->host, us->file_name, us->line); + return NGX_ERROR; + } + + } else if (resolve) { + + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "resolving names at run time requires" + " upstream \"%V\" in %s:%ui" + " to be in shared memory", + &us->host, us->file_name, us->line); + return NGX_ERROR; + } +#endif + + if (n + r == 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); @@ -77,7 +148,8 @@ ngx_stream_upstream_init_round_robin(ngx_conf_t *cf, return NGX_ERROR; } - peer = ngx_pcalloc(cf->pool, sizeof(ngx_stream_upstream_rr_peer_t) * n); + peer = ngx_pcalloc(cf->pool, sizeof(ngx_stream_upstream_rr_peer_t) + * (n + r)); if (peer == NULL) { return NGX_ERROR; } @@ -92,11 +164,45 @@ ngx_stream_upstream_init_round_robin(ngx_conf_t *cf, n = 0; peerp = &peers->peer; +#if (NGX_STREAM_UPSTREAM_ZONE) + rpeerp = &peers->resolve; +#endif + for (i = 0; i < us->servers->nelts; i++) { if (server[i].backup) { continue; } +#if (NGX_STREAM_UPSTREAM_ZONE) + if (server[i].host.len) { + + peer[n].host = ngx_pcalloc(cf->pool, + sizeof(ngx_stream_upstream_host_t)); + if (peer[n].host == NULL) { + return NGX_ERROR; + } + + peer[n].host->name = server[i].host; + + peer[n].sockaddr = server[i].addrs[0].sockaddr; + peer[n].socklen = server[i].addrs[0].socklen; + peer[n].name = server[i].addrs[0].name; + peer[n].weight = server[i].weight; + peer[n].effective_weight = server[i].weight; + peer[n].current_weight = 0; + peer[n].max_conns = server[i].max_conns; + 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; + *rpeerp = &peer[n]; + rpeerp = &peer[n].next; + n++; + + continue; + } +#endif + for (j = 0; j < server[i].naddrs; j++) { peer[n].sockaddr = server[i].addrs[j].sockaddr; peer[n].socklen = server[i].addrs[j].socklen; @@ -121,6 +227,7 @@ ngx_stream_upstream_init_round_robin(ngx_conf_t *cf, /* backup servers */ n = 0; + r = 0; w = 0; t = 0; @@ -129,6 +236,13 @@ ngx_stream_upstream_init_round_robin(ngx_conf_t *cf, continue; } +#if (NGX_STREAM_UPSTREAM_ZONE) + if (server[i].host.len) { + r++; + continue; + } +#endif + n += server[i].naddrs; w += server[i].naddrs * server[i].weight; @@ -137,7 +251,7 @@ ngx_stream_upstream_init_round_robin(ngx_conf_t *cf, } } - if (n == 0) { + if (n + r == 0) { return NGX_OK; } @@ -146,12 +260,16 @@ ngx_stream_upstream_init_round_robin(ngx_conf_t *cf, return NGX_ERROR; } - peer = ngx_pcalloc(cf->pool, sizeof(ngx_stream_upstream_rr_peer_t) * n); + peer = ngx_pcalloc(cf->pool, sizeof(ngx_stream_upstream_rr_peer_t) + * (n + r)); if (peer == NULL) { return NGX_ERROR; } - peers->single = 0; + if (n > 0) { + peers->single = 0; + } + backup->single = 0; backup->number = n; backup->weighted = (w != n); @@ -162,11 +280,45 @@ ngx_stream_upstream_init_round_robin(ngx_conf_t *cf, n = 0; peerp = &backup->peer; +#if (NGX_STREAM_UPSTREAM_ZONE) + rpeerp = &backup->resolve; +#endif + for (i = 0; i < us->servers->nelts; i++) { if (!server[i].backup) { continue; } +#if (NGX_STREAM_UPSTREAM_ZONE) + if (server[i].host.len) { + + peer[n].host = ngx_pcalloc(cf->pool, + sizeof(ngx_stream_upstream_host_t)); + if (peer[n].host == NULL) { + return NGX_ERROR; + } + + peer[n].host->name = server[i].host; + + peer[n].sockaddr = server[i].addrs[0].sockaddr; + peer[n].socklen = server[i].addrs[0].socklen; + peer[n].name = server[i].addrs[0].name; + peer[n].weight = server[i].weight; + peer[n].effective_weight = server[i].weight; + peer[n].current_weight = 0; + peer[n].max_conns = server[i].max_conns; + 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; + *rpeerp = &peer[n]; + rpeerp = &peer[n].next; + n++; + + continue; + } +#endif + for (j = 0; j < server[i].naddrs; j++) { peer[n].sockaddr = server[i].addrs[j].sockaddr; peer[n].socklen = server[i].addrs[j].socklen; @@ -280,7 +432,12 @@ ngx_stream_upstream_init_round_robin_peer(ngx_stream_session_t *s, rrp->peers = us->peer.data; rrp->current = NULL; - rrp->config = 0; + + ngx_stream_upstream_rr_peers_rlock(rrp->peers); + +#if (NGX_STREAM_UPSTREAM_ZONE) + rrp->config = rrp->peers->config ? *rrp->peers->config : 0; +#endif n = rrp->peers->number; @@ -288,6 +445,10 @@ ngx_stream_upstream_init_round_robin_peer(ngx_stream_session_t *s, n = rrp->peers->next->number; } + s->upstream->peer.tries = ngx_stream_upstream_tries(rrp->peers); + + ngx_stream_upstream_rr_peers_unlock(rrp->peers); + if (n <= 8 * sizeof(uintptr_t)) { rrp->tried = &rrp->data; rrp->data = 0; @@ -304,7 +465,7 @@ ngx_stream_upstream_init_round_robin_peer(ngx_stream_session_t *s, 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.notify = ngx_stream_upstream_notify_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; @@ -455,6 +616,12 @@ ngx_stream_upstream_get_round_robin_peer(ngx_peer_connection_t *pc, void *data) peers = rrp->peers; ngx_stream_upstream_rr_peers_wlock(peers); +#if (NGX_STREAM_UPSTREAM_ZONE) + if (peers->config && rrp->config != *peers->config) { + goto busy; + } +#endif + if (peers->single) { peer = peers->peer; @@ -467,6 +634,7 @@ ngx_stream_upstream_get_round_robin_peer(ngx_peer_connection_t *pc, void *data) } rrp->current = peer; + ngx_stream_upstream_rr_peer_ref(peers, peer); } else { @@ -519,6 +687,10 @@ failed: ngx_stream_upstream_rr_peers_wlock(peers); } +#if (NGX_STREAM_UPSTREAM_ZONE) +busy: +#endif + ngx_stream_upstream_rr_peers_unlock(peers); pc->name = peers->name; @@ -589,6 +761,7 @@ ngx_stream_upstream_get_peer(ngx_stream_upstream_rr_peer_data_t *rrp) } rrp->current = best; + ngx_stream_upstream_rr_peer_ref(rrp->peers, best); n = p / (8 * sizeof(uintptr_t)); m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t)); @@ -623,9 +796,17 @@ ngx_stream_upstream_free_round_robin_peer(ngx_peer_connection_t *pc, void *data, ngx_stream_upstream_rr_peer_lock(rrp->peers, peer); if (rrp->peers->single) { + + if (peer->fails) { + peer->fails = 0; + } + peer->conns--; - ngx_stream_upstream_rr_peer_unlock(rrp->peers, peer); + if (ngx_stream_upstream_rr_peer_unref(rrp->peers, peer) == NGX_OK) { + ngx_stream_upstream_rr_peer_unlock(rrp->peers, peer); + } + ngx_stream_upstream_rr_peers_unlock(rrp->peers); pc->tries = 0; @@ -667,7 +848,10 @@ ngx_stream_upstream_free_round_robin_peer(ngx_peer_connection_t *pc, void *data, peer->conns--; - ngx_stream_upstream_rr_peer_unlock(rrp->peers, peer); + if (ngx_stream_upstream_rr_peer_unref(rrp->peers, peer) == NGX_OK) { + ngx_stream_upstream_rr_peer_unlock(rrp->peers, peer); + } + ngx_stream_upstream_rr_peers_unlock(rrp->peers); if (pc->tries) { diff --git a/src/stream/ngx_stream_upstream_round_robin.h b/src/stream/ngx_stream_upstream_round_robin.h index bd96667ba..5002200c2 100644 --- a/src/stream/ngx_stream_upstream_round_robin.h +++ b/src/stream/ngx_stream_upstream_round_robin.h @@ -14,8 +14,23 @@ #include +typedef struct ngx_stream_upstream_rr_peers_s ngx_stream_upstream_rr_peers_t; typedef struct ngx_stream_upstream_rr_peer_s ngx_stream_upstream_rr_peer_t; + +#if (NGX_STREAM_UPSTREAM_ZONE) + +typedef struct { + ngx_event_t event; /* must be first */ + ngx_uint_t worker; + ngx_str_t name; + ngx_stream_upstream_rr_peers_t *peers; + ngx_stream_upstream_rr_peer_t *peer; +} ngx_stream_upstream_host_t; + +#endif + + struct ngx_stream_upstream_rr_peer_s { struct sockaddr *sockaddr; socklen_t socklen; @@ -44,24 +59,28 @@ struct ngx_stream_upstream_rr_peer_s { int ssl_session_len; #if (NGX_STREAM_UPSTREAM_ZONE) + unsigned zombie:1; + ngx_atomic_t lock; + ngx_uint_t refs; + ngx_stream_upstream_host_t *host; #endif ngx_stream_upstream_rr_peer_t *next; - NGX_COMPAT_BEGIN(25) + NGX_COMPAT_BEGIN(14) NGX_COMPAT_END }; -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; + ngx_uint_t *config; + ngx_stream_upstream_rr_peer_t *resolve; ngx_stream_upstream_rr_peers_t *zone_next; #endif @@ -112,6 +131,65 @@ struct ngx_stream_upstream_rr_peers_s { ngx_rwlock_unlock(&peer->lock); \ } + +#define ngx_stream_upstream_rr_peer_ref(peers, peer) \ + (peer)->refs++; + + +static ngx_inline void +ngx_stream_upstream_rr_peer_free_locked(ngx_stream_upstream_rr_peers_t *peers, + ngx_stream_upstream_rr_peer_t *peer) +{ + if (peer->refs) { + peer->zombie = 1; + return; + } + + ngx_slab_free_locked(peers->shpool, peer->sockaddr); + ngx_slab_free_locked(peers->shpool, peer->name.data); + + if (peer->server.data && (peer->host == NULL || peer->host->peer == peer)) { + ngx_slab_free_locked(peers->shpool, peer->server.data); + } + +#if (NGX_STREAM_SSL) + if (peer->ssl_session) { + ngx_slab_free_locked(peers->shpool, peer->ssl_session); + } +#endif + + ngx_slab_free_locked(peers->shpool, peer); +} + + +static ngx_inline void +ngx_stream_upstream_rr_peer_free(ngx_stream_upstream_rr_peers_t *peers, + ngx_stream_upstream_rr_peer_t *peer) +{ + ngx_shmtx_lock(&peers->shpool->mutex); + ngx_stream_upstream_rr_peer_free_locked(peers, peer); + ngx_shmtx_unlock(&peers->shpool->mutex); +} + + +static ngx_inline ngx_int_t +ngx_stream_upstream_rr_peer_unref(ngx_stream_upstream_rr_peers_t *peers, + ngx_stream_upstream_rr_peer_t *peer) +{ + peer->refs--; + + if (peers->shpool == NULL) { + return NGX_OK; + } + + if (peer->refs == 0 && peer->zombie) { + ngx_stream_upstream_rr_peer_free(peers, peer); + return NGX_DONE; + } + + return NGX_OK; +} + #else #define ngx_stream_upstream_rr_peers_rlock(peers) @@ -119,6 +197,8 @@ struct ngx_stream_upstream_rr_peers_s { #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) +#define ngx_stream_upstream_rr_peer_ref(peers, peer) +#define ngx_stream_upstream_rr_peer_unref(peers, peer) NGX_OK #endif diff --git a/src/stream/ngx_stream_upstream_zone_module.c b/src/stream/ngx_stream_upstream_zone_module.c index 80d42fa00..d731baf31 100644 --- a/src/stream/ngx_stream_upstream_zone_module.c +++ b/src/stream/ngx_stream_upstream_zone_module.c @@ -18,6 +18,13 @@ static ngx_stream_upstream_rr_peers_t *ngx_stream_upstream_zone_copy_peers( ngx_slab_pool_t *shpool, ngx_stream_upstream_srv_conf_t *uscf); static ngx_stream_upstream_rr_peer_t *ngx_stream_upstream_zone_copy_peer( ngx_stream_upstream_rr_peers_t *peers, ngx_stream_upstream_rr_peer_t *src); +static void ngx_stream_upstream_zone_set_single( + ngx_stream_upstream_srv_conf_t *uscf); +static void ngx_stream_upstream_zone_remove_peer_locked( + ngx_stream_upstream_rr_peers_t *peers, ngx_stream_upstream_rr_peer_t *peer); +static ngx_int_t ngx_stream_upstream_zone_init_worker(ngx_cycle_t *cycle); +static void ngx_stream_upstream_zone_resolve_timer(ngx_event_t *event); +static void ngx_stream_upstream_zone_resolve_handler(ngx_resolver_ctx_t *ctx); static ngx_command_t ngx_stream_upstream_zone_commands[] = { @@ -52,7 +59,7 @@ ngx_module_t ngx_stream_upstream_zone_module = { NGX_STREAM_MODULE, /* module type */ NULL, /* init master */ NULL, /* init module */ - NULL, /* init process */ + ngx_stream_upstream_zone_init_worker, /* init process */ NULL, /* init thread */ NULL, /* exit thread */ NULL, /* exit process */ @@ -185,9 +192,15 @@ ngx_stream_upstream_zone_copy_peers(ngx_slab_pool_t *shpool, ngx_stream_upstream_srv_conf_t *uscf) { ngx_str_t *name; + ngx_uint_t *config; ngx_stream_upstream_rr_peer_t *peer, **peerp; ngx_stream_upstream_rr_peers_t *peers, *backup; + config = ngx_slab_calloc(shpool, sizeof(ngx_uint_t)); + if (config == NULL) { + return NULL; + } + peers = ngx_slab_alloc(shpool, sizeof(ngx_stream_upstream_rr_peers_t)); if (peers == NULL) { return NULL; @@ -211,6 +224,7 @@ ngx_stream_upstream_zone_copy_peers(ngx_slab_pool_t *shpool, peers->name = name; peers->shpool = shpool; + peers->config = config; for (peerp = &peers->peer; *peerp; peerp = &peer->next) { /* pool is unlocked */ @@ -220,6 +234,17 @@ ngx_stream_upstream_zone_copy_peers(ngx_slab_pool_t *shpool, } *peerp = peer; + (*peers->config)++; + } + + for (peerp = &peers->resolve; *peerp; peerp = &peer->next) { + peer = ngx_stream_upstream_zone_copy_peer(peers, *peerp); + if (peer == NULL) { + return NULL; + } + + *peerp = peer; + (*peers->config)++; } if (peers->next == NULL) { @@ -236,6 +261,7 @@ ngx_stream_upstream_zone_copy_peers(ngx_slab_pool_t *shpool, backup->name = name; backup->shpool = shpool; + backup->config = config; for (peerp = &backup->peer; *peerp; peerp = &peer->next) { /* pool is unlocked */ @@ -245,6 +271,17 @@ ngx_stream_upstream_zone_copy_peers(ngx_slab_pool_t *shpool, } *peerp = peer; + (*backup->config)++; + } + + for (peerp = &backup->resolve; *peerp; peerp = &peer->next) { + peer = ngx_stream_upstream_zone_copy_peer(backup, *peerp); + if (peer == NULL) { + return NULL; + } + + *peerp = peer; + (*backup->config)++; } peers->next = backup; @@ -276,6 +313,7 @@ ngx_stream_upstream_zone_copy_peer(ngx_stream_upstream_rr_peers_t *peers, dst->sockaddr = NULL; dst->name.data = NULL; dst->server.data = NULL; + dst->host = NULL; } dst->sockaddr = ngx_slab_calloc_locked(pool, sizeof(ngx_sockaddr_t)); @@ -298,12 +336,37 @@ ngx_stream_upstream_zone_copy_peer(ngx_stream_upstream_rr_peers_t *peers, } ngx_memcpy(dst->server.data, src->server.data, src->server.len); + + if (src->host) { + dst->host = ngx_slab_calloc_locked(pool, + sizeof(ngx_stream_upstream_host_t)); + if (dst->host == NULL) { + goto failed; + } + + dst->host->name.data = ngx_slab_alloc_locked(pool, + src->host->name.len); + if (dst->host->name.data == NULL) { + goto failed; + } + + dst->host->peers = peers; + dst->host->peer = dst; + + dst->host->name.len = src->host->name.len; + ngx_memcpy(dst->host->name.data, src->host->name.data, + src->host->name.len); + } } return dst; failed: + if (dst->host) { + ngx_slab_free_locked(pool, dst->host); + } + if (dst->server.data) { ngx_slab_free_locked(pool, dst->server.data); } @@ -320,3 +383,300 @@ failed: return NULL; } + + +static void +ngx_stream_upstream_zone_set_single(ngx_stream_upstream_srv_conf_t *uscf) +{ + ngx_stream_upstream_rr_peers_t *peers; + + peers = uscf->peer.data; + + if (peers->number == 1 + && (peers->next == NULL || peers->next->number == 0)) + { + peers->single = 1; + + } else { + peers->single = 0; + } +} + + +static void +ngx_stream_upstream_zone_remove_peer_locked( + ngx_stream_upstream_rr_peers_t *peers, ngx_stream_upstream_rr_peer_t *peer) +{ + peers->total_weight -= peer->weight; + peers->number--; + peers->tries -= (peer->down == 0); + (*peers->config)++; + peers->weighted = (peers->total_weight != peers->number); + + ngx_stream_upstream_rr_peer_free(peers, peer); +} + + +static ngx_int_t +ngx_stream_upstream_zone_init_worker(ngx_cycle_t *cycle) +{ + ngx_uint_t i; + ngx_event_t *event; + ngx_stream_upstream_rr_peer_t *peer; + ngx_stream_upstream_rr_peers_t *peers; + ngx_stream_upstream_srv_conf_t *uscf, **uscfp; + ngx_stream_upstream_main_conf_t *umcf; + + if (ngx_process != NGX_PROCESS_WORKER + && ngx_process != NGX_PROCESS_SINGLE) + { + return NGX_OK; + } + + umcf = ngx_stream_cycle_get_module_main_conf(cycle, + ngx_stream_upstream_module); + + if (umcf == NULL) { + return NGX_OK; + } + + uscfp = umcf->upstreams.elts; + + for (i = 0; i < umcf->upstreams.nelts; i++) { + + uscf = uscfp[i]; + + if (uscf->shm_zone == NULL) { + continue; + } + + peers = uscf->peer.data; + + do { + ngx_stream_upstream_rr_peers_wlock(peers); + + for (peer = peers->resolve; peer; peer = peer->next) { + + if (peer->host->worker != ngx_worker) { + continue; + } + + event = &peer->host->event; + ngx_memzero(event, sizeof(ngx_event_t)); + + event->data = uscf; + event->handler = ngx_stream_upstream_zone_resolve_timer; + event->log = cycle->log; + event->cancelable = 1; + + ngx_add_timer(event, 1); + } + + ngx_stream_upstream_rr_peers_unlock(peers); + + peers = peers->next; + + } while (peers); + } + + return NGX_OK; +} + + +static void +ngx_stream_upstream_zone_resolve_timer(ngx_event_t *event) +{ + ngx_resolver_ctx_t *ctx; + ngx_stream_upstream_host_t *host; + ngx_stream_upstream_srv_conf_t *uscf; + + host = (ngx_stream_upstream_host_t *) event; + uscf = event->data; + + ctx = ngx_resolve_start(uscf->resolver, NULL); + if (ctx == NULL) { + goto retry; + } + + if (ctx == NGX_NO_RESOLVER) { + ngx_log_error(NGX_LOG_ERR, event->log, 0, + "no resolver defined to resolve %V", &host->name); + return; + } + + ctx->name = host->name; + ctx->handler = ngx_stream_upstream_zone_resolve_handler; + ctx->data = host; + ctx->timeout = uscf->resolver_timeout; + ctx->cancelable = 1; + + if (ngx_resolve_name(ctx) == NGX_OK) { + return; + } + +retry: + + ngx_add_timer(event, ngx_max(uscf->resolver_timeout, 1000)); +} + + +static void +ngx_stream_upstream_zone_resolve_handler(ngx_resolver_ctx_t *ctx) +{ + time_t now; + in_port_t port; + ngx_msec_t timer; + ngx_uint_t i, j; + ngx_event_t *event; + ngx_resolver_addr_t *addr; + ngx_stream_upstream_host_t *host; + ngx_stream_upstream_rr_peer_t *peer, *template, **peerp; + ngx_stream_upstream_rr_peers_t *peers; + ngx_stream_upstream_srv_conf_t *uscf; + + host = ctx->data; + event = &host->event; + uscf = event->data; + peers = host->peers; + template = host->peer; + + ngx_stream_upstream_rr_peers_wlock(peers); + + now = ngx_time(); + + if (ctx->state) { + ngx_log_error(NGX_LOG_ERR, event->log, 0, + "%V could not be resolved (%i: %s)", + &ctx->name, ctx->state, + ngx_resolver_strerror(ctx->state)); + + if (ctx->state != NGX_RESOLVE_NXDOMAIN) { + ngx_stream_upstream_rr_peers_unlock(peers); + + ngx_resolve_name_done(ctx); + + ngx_add_timer(event, ngx_max(uscf->resolver_timeout, 1000)); + return; + } + + /* NGX_RESOLVE_NXDOMAIN */ + + ctx->naddrs = 0; + } + +#if (NGX_DEBUG) + { + u_char text[NGX_SOCKADDR_STRLEN]; + size_t len; + + for (i = 0; i < ctx->naddrs; i++) { + len = ngx_sock_ntop(ctx->addrs[i].sockaddr, ctx->addrs[i].socklen, + text, NGX_SOCKADDR_STRLEN, 0); + + ngx_log_debug3(NGX_LOG_DEBUG_STREAM, event->log, 0, + "name %V was resolved to %*s", &host->name, len, text); + } + } +#endif + + for (peerp = &peers->peer; *peerp; /* void */ ) { + peer = *peerp; + + if (peer->host != host) { + goto next; + } + + for (j = 0; j < ctx->naddrs; j++) { + + addr = &ctx->addrs[j]; + + if (addr->name.len == 0 + && ngx_cmp_sockaddr(peer->sockaddr, peer->socklen, + addr->sockaddr, addr->socklen, 0) + == NGX_OK) + { + addr->name.len = 1; + goto next; + } + } + + *peerp = peer->next; + ngx_stream_upstream_zone_remove_peer_locked(peers, peer); + + ngx_stream_upstream_zone_set_single(uscf); + + continue; + + next: + + peerp = &peer->next; + } + + for (i = 0; i < ctx->naddrs; i++) { + + addr = &ctx->addrs[i]; + + if (addr->name.len == 1) { + addr->name.len = 0; + continue; + } + + ngx_shmtx_lock(&peers->shpool->mutex); + peer = ngx_stream_upstream_zone_copy_peer(peers, NULL); + ngx_shmtx_unlock(&peers->shpool->mutex); + if (peer == NULL) { + ngx_log_error(NGX_LOG_ERR, event->log, 0, + "cannot add new server to upstream \"%V\", " + "memory exhausted", peers->name); + break; + } + + ngx_memcpy(peer->sockaddr, addr->sockaddr, addr->socklen); + + port = ((struct sockaddr_in *) template->sockaddr)->sin_port; + + switch (peer->sockaddr->sa_family) { +#if (NGX_HAVE_INET6) + case AF_INET6: + ((struct sockaddr_in6 *) peer->sockaddr)->sin6_port = port; + break; +#endif + default: /* AF_INET */ + ((struct sockaddr_in *) peer->sockaddr)->sin_port = port; + } + + peer->socklen = addr->socklen; + + peer->name.len = ngx_sock_ntop(peer->sockaddr, peer->socklen, + peer->name.data, NGX_SOCKADDR_STRLEN, 1); + + peer->host = template->host; + peer->server = template->server; + + peer->weight = template->weight; + peer->effective_weight = peer->weight; + peer->max_conns = template->max_conns; + peer->max_fails = template->max_fails; + peer->fail_timeout = template->fail_timeout; + peer->down = template->down; + + *peerp = peer; + peerp = &peer->next; + + peers->number++; + peers->tries += (peer->down == 0); + peers->total_weight += peer->weight; + peers->weighted = (peers->total_weight != peers->number); + (*peers->config)++; + + ngx_stream_upstream_zone_set_single(uscf); + } + + ngx_stream_upstream_rr_peers_unlock(peers); + + timer = (ngx_msec_t) 1000 * (ctx->valid > now ? ctx->valid - now + 1 : 1); + + ngx_resolve_name_done(ctx); + + ngx_add_timer(event, timer); +} From noreply at nginx.com Thu Nov 7 15:58:03 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Thu, 7 Nov 2024 15:58:03 +0000 (UTC) Subject: [nginx] Upstream: copy upstream zone DNS valid time during config reload. Message-ID: <20241107155803.4405446C71@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/29aec5720fdfc74dca8d99d5cf6dc0fcb4e4ce2f branches: master commit: 29aec5720fdfc74dca8d99d5cf6dc0fcb4e4ce2f user: Mini Hawthorne date: Wed, 12 Jul 2023 12:20:45 -0700 description: Upstream: copy upstream zone DNS valid time during config reload. Previously, all upstream DNS entries would be immediately re-resolved on config reload. With a large number of upstreams, this creates a spike of DNS resolution requests. These spikes can overwhelm the DNS server or cause drops on the network. This patch retains the TTL of previous resolutions across reloads by copying each upstream's name's expiry time across configuration cycles. As a result, no additional resolutions are needed. --- src/http/modules/ngx_http_upstream_zone_module.c | 12 +++++++++++- src/http/ngx_http_upstream_round_robin.h | 1 + src/stream/ngx_stream_upstream_round_robin.h | 1 + src/stream/ngx_stream_upstream_zone_module.c | 12 +++++++++++- 4 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/http/modules/ngx_http_upstream_zone_module.c b/src/http/modules/ngx_http_upstream_zone_module.c index c1931a8bb..2ce8a7b5b 100644 --- a/src/http/modules/ngx_http_upstream_zone_module.c +++ b/src/http/modules/ngx_http_upstream_zone_module.c @@ -545,6 +545,8 @@ ngx_http_upstream_zone_preresolve(ngx_http_upstream_rr_peer_t *resolve, peer->host = template->host; + template->host->valid = host->valid; + server = template->host->service.len ? &opeer->server : &template->server; @@ -626,6 +628,8 @@ ngx_http_upstream_zone_remove_peer_locked(ngx_http_upstream_rr_peers_t *peers, static ngx_int_t ngx_http_upstream_zone_init_worker(ngx_cycle_t *cycle) { + time_t now; + ngx_msec_t timer; ngx_uint_t i; ngx_event_t *event; ngx_http_upstream_rr_peer_t *peer; @@ -639,6 +643,7 @@ ngx_http_upstream_zone_init_worker(ngx_cycle_t *cycle) return NGX_OK; } + now = ngx_time(); umcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_upstream_module); if (umcf == NULL) { @@ -674,7 +679,10 @@ ngx_http_upstream_zone_init_worker(ngx_cycle_t *cycle) event->log = cycle->log; event->cancelable = 1; - ngx_add_timer(event, 1); + timer = (peer->host->valid > now) + ? (ngx_msec_t) 1000 * (peer->host->valid - now) : 1; + + ngx_add_timer(event, timer); } ngx_http_upstream_rr_peers_unlock(peers); @@ -983,6 +991,8 @@ again: done: + host->valid = ctx->valid; + ngx_http_upstream_rr_peers_unlock(peers); while (++i < ctx->naddrs) { diff --git a/src/http/ngx_http_upstream_round_robin.h b/src/http/ngx_http_upstream_round_robin.h index 084b0b886..2f0a51cdf 100644 --- a/src/http/ngx_http_upstream_round_robin.h +++ b/src/http/ngx_http_upstream_round_robin.h @@ -25,6 +25,7 @@ typedef struct { ngx_uint_t worker; ngx_str_t name; ngx_str_t service; + time_t valid; ngx_http_upstream_rr_peers_t *peers; ngx_http_upstream_rr_peer_t *peer; } ngx_http_upstream_host_t; diff --git a/src/stream/ngx_stream_upstream_round_robin.h b/src/stream/ngx_stream_upstream_round_robin.h index 707a9889d..c168bfe80 100644 --- a/src/stream/ngx_stream_upstream_round_robin.h +++ b/src/stream/ngx_stream_upstream_round_robin.h @@ -25,6 +25,7 @@ typedef struct { ngx_uint_t worker; ngx_str_t name; ngx_str_t service; + time_t valid; ngx_stream_upstream_rr_peers_t *peers; ngx_stream_upstream_rr_peer_t *peer; } ngx_stream_upstream_host_t; diff --git a/src/stream/ngx_stream_upstream_zone_module.c b/src/stream/ngx_stream_upstream_zone_module.c index d92609feb..a6874dc33 100644 --- a/src/stream/ngx_stream_upstream_zone_module.c +++ b/src/stream/ngx_stream_upstream_zone_module.c @@ -542,6 +542,8 @@ ngx_stream_upstream_zone_preresolve(ngx_stream_upstream_rr_peer_t *resolve, peer->host = template->host; + template->host->valid = host->valid; + server = template->host->service.len ? &opeer->server : &template->server; @@ -623,6 +625,8 @@ ngx_stream_upstream_zone_remove_peer_locked( static ngx_int_t ngx_stream_upstream_zone_init_worker(ngx_cycle_t *cycle) { + time_t now; + ngx_msec_t timer; ngx_uint_t i; ngx_event_t *event; ngx_stream_upstream_rr_peer_t *peer; @@ -636,6 +640,7 @@ ngx_stream_upstream_zone_init_worker(ngx_cycle_t *cycle) return NGX_OK; } + now = ngx_time(); umcf = ngx_stream_cycle_get_module_main_conf(cycle, ngx_stream_upstream_module); @@ -672,7 +677,10 @@ ngx_stream_upstream_zone_init_worker(ngx_cycle_t *cycle) event->log = cycle->log; event->cancelable = 1; - ngx_add_timer(event, 1); + timer = (peer->host->valid > now) + ? (ngx_msec_t) 1000 * (peer->host->valid - now) : 1; + + ngx_add_timer(event, timer); } ngx_stream_upstream_rr_peers_unlock(peers); @@ -981,6 +989,8 @@ again: done: + host->valid = ctx->valid; + ngx_stream_upstream_rr_peers_unlock(peers); while (++i < ctx->naddrs) { From noreply at nginx.com Thu Nov 7 18:52:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Thu, 7 Nov 2024 18:52:02 +0000 (UTC) Subject: [njs] Fixed building by Clang. Message-ID: <20241107185202.10DF346C75@pubserv1.nginx> details: https://github.com/nginx/njs/commit/e845d5e553ec6986ec133972544c4c1e171e4f52 branches: master commit: e845d5e553ec6986ec133972544c4c1e171e4f52 user: Dmitry Volyntsev date: Wed, 6 Nov 2024 15:59:32 -0800 description: Fixed building by Clang. src/qjs.c:347:19: error: variable 'signo' may be uninitialized when used here [-Werror,-Wconditional-uninitialized] 347 | if (kill(pid, signo) < 0) { | ^~~~~ src/qjs.c:294:31: note: initialize the variable 'signo' to silence this warning 294 | int signo, pid; | ^ | = 0 1 error generated. --- src/qjs.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/qjs.c b/src/qjs.c index 44e9fe95..f9615668 100644 --- a/src/qjs.c +++ b/src/qjs.c @@ -301,6 +301,8 @@ qjs_process_kill(JSContext *ctx, JSValueConst this_val, int argc, return JS_EXCEPTION; } + signo = SIGTERM; + if (JS_IsNumber(argv[1])) { if (JS_ToInt32(ctx, &signo, argv[1]) < 0) { return JS_EXCEPTION; From noreply at nginx.com Thu Nov 7 19:05:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Thu, 7 Nov 2024 19:05:02 +0000 (UTC) Subject: [njs] XML: fixed tests with libxml2 2.13 and later. Message-ID: <20241107190502.0F17846C76@pubserv1.nginx> details: https://github.com/nginx/njs/commit/597b1fd1fa34999ec47bbd574da35491f56b8893 branches: master commit: 597b1fd1fa34999ec47bbd574da35491f56b8893 user: Dmitry Volyntsev date: Wed, 6 Nov 2024 22:08:21 -0800 description: XML: fixed tests with libxml2 2.13 and later. This fixes #812 issue on Github. --- external/njs_xml_module.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/external/njs_xml_module.c b/external/njs_xml_module.c index 86c896c0..d9f3bbb5 100644 --- a/external/njs_xml_module.c +++ b/external/njs_xml_module.c @@ -1275,17 +1275,13 @@ njs_xml_node_tags_handler(njs_vm_t *vm, xmlNode *current, njs_str_t *name, /* set or delete. */ - copy = xmlDocCopyNode(current, current->doc, 1); + copy = xmlDocCopyNode(current, current->doc, + 2 /* copy properties and namespaces */); if (njs_slow_path(copy == NULL)) { njs_vm_internal_error(vm, "xmlDocCopyNode() failed"); return NJS_ERROR; } - if (copy->children != NULL) { - xmlFreeNodeList(copy->children); - copy->children = NULL; - } - if (retval == NULL) { /* delete. */ return njs_xml_replace_node(vm, current, copy); From noreply at nginx.com Thu Nov 7 19:05:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Thu, 7 Nov 2024 19:05:02 +0000 (UTC) Subject: [njs] XML: improved XMLNode.$tags handler. Message-ID: <20241107190502.1258746C77@pubserv1.nginx> details: https://github.com/nginx/njs/commit/e476ef9134a58d8967a740d182ea15c075492704 branches: master commit: e476ef9134a58d8967a740d182ea15c075492704 user: Dmitry Volyntsev date: Wed, 6 Nov 2024 22:15:22 -0800 description: XML: improved XMLNode.$tags handler. --- external/njs_xml_module.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/external/njs_xml_module.c b/external/njs_xml_module.c index d9f3bbb5..d5ab9ddd 100644 --- a/external/njs_xml_module.c +++ b/external/njs_xml_module.c @@ -1318,12 +1318,12 @@ njs_xml_node_tags_handler(njs_vm_t *vm, xmlNode *current, njs_str_t *name, xmlFreeNode(node); goto error; } + } - ret = xmlReconciliateNs(current->doc, copy); - if (njs_slow_path(ret == -1)) { - njs_vm_internal_error(vm, "xmlReconciliateNs() failed"); - goto error; - } + ret = xmlReconciliateNs(current->doc, copy); + if (njs_slow_path(ret == -1)) { + njs_vm_internal_error(vm, "xmlReconciliateNs() failed"); + goto error; } njs_value_undefined_set(retval); From noreply at nginx.com Thu Nov 7 23:53:01 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Thu, 7 Nov 2024 23:53:01 +0000 (UTC) Subject: [njs] Added missing steps for building with QuickJS to README. Message-ID: <20241107235301.F052046C7B@pubserv1.nginx> details: https://github.com/nginx/njs/commit/72f0b5db8595bca740ffbfd2f9213e577718a174 branches: master commit: 72f0b5db8595bca740ffbfd2f9213e577718a174 user: Dmitry Volyntsev date: Thu, 7 Nov 2024 15:26:39 -0800 description: Added missing steps for building with QuickJS to README. --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 855d9c0b..10ef0ba7 100644 --- a/README.md +++ b/README.md @@ -258,10 +258,12 @@ sudo apt install gcc make sudo apt install libpcre3-dev zlib1g-dev libssl-dev libxml2-dev libxslt-dev ``` -For building with [QuickJS](https://nginx.org/en/docs/njs/engine.html), you will also need to clone the QuickJS repository: +For building with [QuickJS](https://nginx.org/en/docs/njs/engine.html), you will also need to build the QuickJS library: ```bash git clone https://github.com/bellard/quickjs +cd quickjs +CFLAGS='-fPIC' make libquickjs.a ``` > [!WARNING] From noreply at nginx.com Fri Nov 8 22:34:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Fri, 8 Nov 2024 22:34:02 +0000 (UTC) Subject: [njs] Fixed resolving when Promise is inherited. Message-ID: <20241108223402.B769A46D2A@pubserv1.nginx> details: https://github.com/nginx/njs/commit/5247aac9f4cc6f49ccf4a1dcfb30581ae2837dd4 branches: master commit: 5247aac9f4cc6f49ccf4a1dcfb30581ae2837dd4 user: Dmitry Volyntsev date: Thu, 7 Nov 2024 18:41:41 -0800 description: Fixed resolving when Promise is inherited. Previously, njs_promise_resolve() might return njs_object_t instead of njs_promise_t. Later an instance of njs_object_t was put into a NJS_PROMISE value. Whereas njs_promise_t is always expected to be inside of a NJS_PROMISE value. This closes #813 issue on Github. --- src/njs_promise.c | 37 ++++++++++++++----------------------- src/njs_promise.h | 4 ++-- src/njs_vmcode.c | 6 ++---- test/js/promise_s27.t.js | 34 ++++++++++++++++++++++++++++++++++ 4 files changed, 52 insertions(+), 29 deletions(-) diff --git a/src/njs_promise.c b/src/njs_promise.c index 54da0a02..79b93a11 100644 --- a/src/njs_promise.c +++ b/src/njs_promise.c @@ -675,27 +675,19 @@ static njs_int_t njs_promise_object_resolve(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { - njs_promise_t *promise; - if (njs_slow_path(!njs_is_object(njs_argument(args, 0)))) { njs_type_error(vm, "this value is not an object"); return NJS_ERROR; } - promise = njs_promise_resolve(vm, njs_argument(args, 0), - njs_arg(args, nargs, 1)); - if (njs_slow_path(promise == NULL)) { - return NJS_ERROR; - } - - njs_set_promise(retval, promise); - - return NJS_OK; + return njs_promise_resolve(vm, njs_argument(args, 0), + njs_arg(args, nargs, 1), retval); } -njs_promise_t * -njs_promise_resolve(njs_vm_t *vm, njs_value_t *constructor, njs_value_t *x) +njs_int_t +njs_promise_resolve(njs_vm_t *vm, njs_value_t *constructor, njs_value_t *x, + njs_value_t *retval) { njs_int_t ret; njs_value_t value; @@ -707,26 +699,28 @@ njs_promise_resolve(njs_vm_t *vm, njs_value_t *constructor, njs_value_t *x) ret = njs_value_property(vm, x, njs_value_arg(&string_constructor), &value); if (njs_slow_path(ret == NJS_ERROR)) { - return NULL; + return NJS_ERROR; } if (njs_values_same(&value, constructor)) { - return njs_promise(x); + njs_value_assign(retval, x); + return NJS_OK; } } capability = njs_promise_new_capability(vm, constructor); if (njs_slow_path(capability == NULL)) { - return NULL; + return NJS_ERROR; } ret = njs_function_call(vm, njs_function(&capability->resolve), &njs_value_undefined, x, 1, &value); if (njs_slow_path(ret != NJS_OK)) { - return NULL; + return ret; } - return njs_promise(&capability->promise); + njs_value_assign(retval, &capability->promise); + return NJS_OK; } @@ -1017,7 +1011,6 @@ njs_promise_then_finally_function(njs_vm_t *vm, njs_value_t *args, { njs_int_t ret; njs_value_t value, argument; - njs_promise_t *promise; njs_function_t *function; njs_native_frame_t *frame; njs_promise_context_t *context; @@ -1031,13 +1024,11 @@ njs_promise_then_finally_function(njs_vm_t *vm, njs_value_t *args, return ret; } - promise = njs_promise_resolve(vm, &context->constructor, &value); - if (njs_slow_path(promise == NULL)) { + ret = njs_promise_resolve(vm, &context->constructor, &value, &value); + if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } - njs_set_promise(&value, promise); - function = njs_promise_create_function(vm, sizeof(njs_value_t)); if (njs_slow_path(function == NULL)) { return NJS_ERROR; diff --git a/src/njs_promise.h b/src/njs_promise.h index abf8e1a6..53d179a9 100644 --- a/src/njs_promise.h +++ b/src/njs_promise.h @@ -36,8 +36,8 @@ njs_function_t *njs_promise_create_function(njs_vm_t *vm, size_t context_size); njs_int_t njs_promise_perform_then(njs_vm_t *vm, njs_value_t *value, njs_value_t *fulfilled, njs_value_t *rejected, njs_promise_capability_t *capability, njs_value_t *retval); -njs_promise_t *njs_promise_resolve(njs_vm_t *vm, njs_value_t *constructor, - njs_value_t *x); +njs_int_t njs_promise_resolve(njs_vm_t *vm, njs_value_t *constructor, + njs_value_t *x, njs_value_t *retval); extern const njs_object_type_init_t njs_promise_type_init; diff --git a/src/njs_vmcode.c b/src/njs_vmcode.c index a95171a3..838e2821 100644 --- a/src/njs_vmcode.c +++ b/src/njs_vmcode.c @@ -2637,7 +2637,6 @@ njs_vmcode_await(njs_vm_t *vm, njs_vmcode_await_t *await, njs_int_t ret; njs_frame_t *frame; njs_value_t ctor, val, on_fulfilled, on_rejected, *value, retval; - njs_promise_t *promise; njs_function_t *fulfilled, *rejected; njs_native_frame_t *active; @@ -2651,8 +2650,8 @@ njs_vmcode_await(njs_vm_t *vm, njs_vmcode_await_t *await, njs_set_function(&ctor, &njs_vm_ctor(vm, NJS_OBJ_TYPE_PROMISE)); - promise = njs_promise_resolve(vm, &ctor, value); - if (njs_slow_path(promise == NULL)) { + ret = njs_promise_resolve(vm, &ctor, value, &val); + if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } @@ -2710,7 +2709,6 @@ njs_vmcode_await(njs_vm_t *vm, njs_vmcode_await_t *await, rejected->args_count = 1; rejected->u.native = njs_await_rejected; - njs_set_promise(&val, promise); njs_set_function(&on_fulfilled, fulfilled); njs_set_function(&on_rejected, rejected); diff --git a/test/js/promise_s27.t.js b/test/js/promise_s27.t.js new file mode 100644 index 00000000..7a5ac583 --- /dev/null +++ b/test/js/promise_s27.t.js @@ -0,0 +1,34 @@ +/*--- +includes: [] +flags: [async] +---*/ + +var inherits = (child, parent) => { + child.prototype = Object.create(parent.prototype, { + constructor: { + value: child, + enumerable: false, + writable: true, + configurable: true + } + }); + Object.setPrototypeOf(child, parent); +}; + +function BoxedPromise(executor) { + var context, args; + new Promise(wrappedExecutor); + executor.apply(context, args); + + function wrappedExecutor(resolve, reject) { + context = this; + args = [v => resolve(v),v => reject(v)]; + } +} + +inherits(BoxedPromise, Promise); + +Promise.resolve() +.then(() => BoxedPromise.resolve()) +.catch(e => assert.sameValue(e.constructor, TypeError)) +.then($DONE, $DONE); From noreply at nginx.com Mon Nov 11 18:30:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Mon, 11 Nov 2024 18:30:02 +0000 (UTC) Subject: [nginx] SSL: fixed MSVC compilation after ebd18ec1812b. Message-ID: <20241111183002.60DAF4734F@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/ea15896c1a5b0ce504f85c1437bae21a542cf3e6 branches: master commit: ea15896c1a5b0ce504f85c1437bae21a542cf3e6 user: 蕭澧邦 date: Sun, 3 Nov 2024 14:36:17 +0800 description: SSL: fixed MSVC compilation after ebd18ec1812b. MSVC generates a compilation error in case #if/#endif is used in a macro parameter. --- src/http/modules/ngx_http_grpc_module.c | 8 ++++++-- src/http/modules/ngx_http_proxy_module.c | 8 ++++++-- src/http/modules/ngx_http_ssl_module.c | 8 ++++++-- src/http/modules/ngx_http_uwsgi_module.c | 8 ++++++-- src/mail/ngx_mail_ssl_module.c | 8 ++++++-- src/stream/ngx_stream_proxy_module.c | 8 ++++++-- src/stream/ngx_stream_ssl_module.c | 8 ++++++-- 7 files changed, 42 insertions(+), 14 deletions(-) diff --git a/src/http/modules/ngx_http_grpc_module.c b/src/http/modules/ngx_http_grpc_module.c index d9456843d..0a103ac66 100644 --- a/src/http/modules/ngx_http_grpc_module.c +++ b/src/http/modules/ngx_http_grpc_module.c @@ -4475,12 +4475,16 @@ ngx_http_grpc_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_value(conf->upstream.ssl_session_reuse, prev->upstream.ssl_session_reuse, 1); +#ifndef SSL_OP_NO_TLSv1_2 ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols, (NGX_CONF_BITMASK_SET -#ifndef SSL_OP_NO_TLSv1_2 |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1 -#endif |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); +#else + ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols, + (NGX_CONF_BITMASK_SET + |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); +#endif ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers, "DEFAULT"); diff --git a/src/http/modules/ngx_http_proxy_module.c b/src/http/modules/ngx_http_proxy_module.c index fe1952748..73d8ce2a8 100644 --- a/src/http/modules/ngx_http_proxy_module.c +++ b/src/http/modules/ngx_http_proxy_module.c @@ -3942,12 +3942,16 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_value(conf->upstream.ssl_session_reuse, prev->upstream.ssl_session_reuse, 1); +#ifndef SSL_OP_NO_TLSv1_2 ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols, (NGX_CONF_BITMASK_SET -#ifndef SSL_OP_NO_TLSv1_2 |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1 -#endif |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); +#else + ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols, + (NGX_CONF_BITMASK_SET + |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); +#endif ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers, "DEFAULT"); diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c index 1fb1e6129..2ab9b84b7 100644 --- a/src/http/modules/ngx_http_ssl_module.c +++ b/src/http/modules/ngx_http_ssl_module.c @@ -651,12 +651,16 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_value(conf->early_data, prev->early_data, 0); ngx_conf_merge_value(conf->reject_handshake, prev->reject_handshake, 0); +#ifndef SSL_OP_NO_TLSv1_2 ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols, (NGX_CONF_BITMASK_SET -#ifndef SSL_OP_NO_TLSv1_2 |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1 -#endif |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); +#else + ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols, + (NGX_CONF_BITMASK_SET + |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); +#endif ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size, NGX_SSL_BUFSIZE); diff --git a/src/http/modules/ngx_http_uwsgi_module.c b/src/http/modules/ngx_http_uwsgi_module.c index f2a8dbe6a..2af5a930a 100644 --- a/src/http/modules/ngx_http_uwsgi_module.c +++ b/src/http/modules/ngx_http_uwsgi_module.c @@ -1877,12 +1877,16 @@ ngx_http_uwsgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_value(conf->upstream.ssl_session_reuse, prev->upstream.ssl_session_reuse, 1); +#ifndef SSL_OP_NO_TLSv1_2 ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols, (NGX_CONF_BITMASK_SET -#ifndef SSL_OP_NO_TLSv1_2 |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1 -#endif |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); +#else + ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols, + (NGX_CONF_BITMASK_SET + |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); +#endif ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers, "DEFAULT"); diff --git a/src/mail/ngx_mail_ssl_module.c b/src/mail/ngx_mail_ssl_module.c index 2fee1adb8..4c3f41a58 100644 --- a/src/mail/ngx_mail_ssl_module.c +++ b/src/mail/ngx_mail_ssl_module.c @@ -343,12 +343,16 @@ ngx_mail_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_value(conf->prefer_server_ciphers, prev->prefer_server_ciphers, 0); +#ifndef SSL_OP_NO_TLSv1_2 ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols, (NGX_CONF_BITMASK_SET -#ifndef SSL_OP_NO_TLSv1_2 |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1 -#endif |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); +#else + ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols, + (NGX_CONF_BITMASK_SET + |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); +#endif ngx_conf_merge_uint_value(conf->verify, prev->verify, 0); ngx_conf_merge_uint_value(conf->verify_depth, prev->verify_depth, 1); diff --git a/src/stream/ngx_stream_proxy_module.c b/src/stream/ngx_stream_proxy_module.c index 0890a04ae..2b6f9baaf 100644 --- a/src/stream/ngx_stream_proxy_module.c +++ b/src/stream/ngx_stream_proxy_module.c @@ -2181,12 +2181,16 @@ ngx_stream_proxy_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_value(conf->ssl_session_reuse, prev->ssl_session_reuse, 1); +#ifndef SSL_OP_NO_TLSv1_2 ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols, (NGX_CONF_BITMASK_SET -#ifndef SSL_OP_NO_TLSv1_2 |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1 -#endif |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); +#else + ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols, + (NGX_CONF_BITMASK_SET + |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); +#endif ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers, "DEFAULT"); diff --git a/src/stream/ngx_stream_ssl_module.c b/src/stream/ngx_stream_ssl_module.c index 55bc54a44..8177d580f 100644 --- a/src/stream/ngx_stream_ssl_module.c +++ b/src/stream/ngx_stream_ssl_module.c @@ -882,12 +882,16 @@ ngx_stream_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_value(conf->reject_handshake, prev->reject_handshake, 0); +#ifndef SSL_OP_NO_TLSv1_2 ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols, (NGX_CONF_BITMASK_SET -#ifndef SSL_OP_NO_TLSv1_2 |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1 -#endif |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); +#else + ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols, + (NGX_CONF_BITMASK_SET + |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); +#endif ngx_conf_merge_uint_value(conf->verify, prev->verify, 0); ngx_conf_merge_uint_value(conf->verify_depth, prev->verify_depth, 1); From noreply at nginx.com Tue Nov 12 13:22:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 12 Nov 2024 13:22:02 +0000 (UTC) Subject: [nginx] Uwsgi: added create_loc_conf comments. Message-ID: <20241112132202.B7B374759C@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/d10bf73ebad8866918e1e1002855c3a501b92e0c branches: master commit: d10bf73ebad8866918e1e1002855c3a501b92e0c user: Sergey Kandaurov date: Tue, 8 Oct 2024 18:24:34 +0400 description: Uwsgi: added create_loc_conf comments. --- src/http/modules/ngx_http_uwsgi_module.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/http/modules/ngx_http_uwsgi_module.c b/src/http/modules/ngx_http_uwsgi_module.c index 2af5a930a..9e9682bc3 100644 --- a/src/http/modules/ngx_http_uwsgi_module.c +++ b/src/http/modules/ngx_http_uwsgi_module.c @@ -1511,6 +1511,28 @@ ngx_http_uwsgi_create_loc_conf(ngx_conf_t *cf) return NULL; } + /* + * set by ngx_pcalloc(): + * + * conf->upstream.bufs.num = 0; + * conf->upstream.ignore_headers = 0; + * conf->upstream.next_upstream = 0; + * conf->upstream.cache_zone = NULL; + * conf->upstream.cache_use_stale = 0; + * conf->upstream.cache_methods = 0; + * conf->upstream.temp_path = NULL; + * conf->upstream.hide_headers_hash = { NULL, 0 }; + * conf->upstream.store_lengths = NULL; + * conf->upstream.store_values = NULL; + * + * conf->uwsgi_string = { 0, NULL }; + * conf->ssl = 0; + * conf->ssl_protocols = 0; + * conf->ssl_ciphers = { 0, NULL }; + * conf->ssl_trusted_certificate = { 0, NULL }; + * conf->ssl_crl = { 0, NULL }; + */ + conf->modifier1 = NGX_CONF_UNSET_UINT; conf->modifier2 = NGX_CONF_UNSET_UINT; From noreply at nginx.com Tue Nov 12 13:22:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 12 Nov 2024 13:22:02 +0000 (UTC) Subject: [nginx] FastCGI: fixed create_loc_conf comments after 05b1a8f1e. Message-ID: <20241112132202.AEA5847591@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/a5e152b3d9addf4ae35f40ca17ab4d62bdcbe69b branches: master commit: a5e152b3d9addf4ae35f40ca17ab4d62bdcbe69b user: Sergey Kandaurov date: Tue, 8 Oct 2024 17:48:15 +0400 description: FastCGI: fixed create_loc_conf comments after 05b1a8f1e. --- src/http/modules/ngx_http_fastcgi_module.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/http/modules/ngx_http_fastcgi_module.c b/src/http/modules/ngx_http_fastcgi_module.c index 46a56f54e..743fe0c0c 100644 --- a/src/http/modules/ngx_http_fastcgi_module.c +++ b/src/http/modules/ngx_http_fastcgi_module.c @@ -2877,7 +2877,7 @@ ngx_http_fastcgi_create_loc_conf(ngx_conf_t *cf) * conf->upstream.store_lengths = NULL; * conf->upstream.store_values = NULL; * - * conf->index.len = { 0, NULL }; + * conf->index = { 0, NULL }; */ conf->upstream.store = NGX_CONF_UNSET; From noreply at nginx.com Tue Nov 12 13:22:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 12 Nov 2024 13:22:02 +0000 (UTC) Subject: [nginx] SCGI: added create_loc_conf comments. Message-ID: <20241112132202.B2D244759B@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/1ac6a18585eab226277c5d41e90b8f336153c4e0 branches: master commit: 1ac6a18585eab226277c5d41e90b8f336153c4e0 user: Sergey Kandaurov date: Tue, 8 Oct 2024 18:24:00 +0400 description: SCGI: added create_loc_conf comments. --- src/http/modules/ngx_http_scgi_module.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/http/modules/ngx_http_scgi_module.c b/src/http/modules/ngx_http_scgi_module.c index f818fc4b0..671cd2cb7 100644 --- a/src/http/modules/ngx_http_scgi_module.c +++ b/src/http/modules/ngx_http_scgi_module.c @@ -1283,6 +1283,21 @@ ngx_http_scgi_create_loc_conf(ngx_conf_t *cf) return NULL; } + /* + * set by ngx_pcalloc(): + * + * conf->upstream.bufs.num = 0; + * conf->upstream.ignore_headers = 0; + * conf->upstream.next_upstream = 0; + * conf->upstream.cache_zone = NULL; + * conf->upstream.cache_use_stale = 0; + * conf->upstream.cache_methods = 0; + * conf->upstream.temp_path = NULL; + * conf->upstream.hide_headers_hash = { NULL, 0 }; + * conf->upstream.store_lengths = NULL; + * conf->upstream.store_values = NULL; + */ + conf->upstream.store = NGX_CONF_UNSET; conf->upstream.store_access = NGX_CONF_UNSET_UINT; conf->upstream.next_upstream_tries = NGX_CONF_UNSET_UINT; From noreply at nginx.com Tue Nov 12 15:31:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 12 Nov 2024 15:31:02 +0000 (UTC) Subject: [nginx] Fixed link to contributing guidelines. Message-ID: <20241112153102.D53214759C@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/36ca44f26f9e5658e880399c95969cca507dfd69 branches: master commit: 36ca44f26f9e5658e880399c95969cca507dfd69 user: Dan Callahan date: Fri, 6 Sep 2024 16:57:50 +0100 description: Fixed link to contributing guidelines. Absolute paths in links end up being rooted at github.com. The contributing guidelines link is broken unless we use the full URL. Also, remove superfluous "monospace formatting" for the link. --- .github/pull_request_template.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 0ee1fc364..14ac4639b 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -6,5 +6,5 @@ If this pull request addresses an issue on GitHub, make sure to reference that issue using one of the [supported keywords](https://docs.github.com/en/github/managing-your-work-on-github/linking-a-pull-request-to-an-issue). -Before creating a pull request, make sure to comply with -[`Contributing Guidelines`](/CONTRIBUTING.md). +Before creating a pull request, make sure to comply with the +[Contributing Guidelines](https://github.com/nginx/nginx/blob/master/CONTRIBUTING.md). From noreply at nginx.com Mon Nov 18 20:31:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Mon, 18 Nov 2024 20:31:02 +0000 (UTC) Subject: [nginx] On DragonFly BSD 5.8+, TCP_KEEPIDLE and TCP_KEEPINTVL are in secs. Message-ID: <20241118203102.17F41475DE@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/7cd60cd475901016bf3e8b22b7394b136b80a0c8 branches: master commit: 7cd60cd475901016bf3e8b22b7394b136b80a0c8 user: Andy Pan date: Tue, 15 Oct 2024 17:20:20 +0800 description: On DragonFly BSD 5.8+, TCP_KEEPIDLE and TCP_KEEPINTVL are in secs. --- src/os/unix/ngx_freebsd_config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/os/unix/ngx_freebsd_config.h b/src/os/unix/ngx_freebsd_config.h index c641108b7..815342974 100644 --- a/src/os/unix/ngx_freebsd_config.h +++ b/src/os/unix/ngx_freebsd_config.h @@ -103,7 +103,7 @@ typedef struct aiocb ngx_aiocb_t; #define NGX_LISTEN_BACKLOG -1 -#ifdef __DragonFly__ +#if (defined __DragonFly__ && __DragonFly_version < 500702) #define NGX_KEEPALIVE_FACTOR 1000 #endif From noreply at nginx.com Tue Nov 19 17:38:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 19 Nov 2024 17:38:02 +0000 (UTC) Subject: [nginx] SSL: error message default in object caching API. Message-ID: <20241119173802.20EEF475F3@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/0ebc3242d99d3b9d00891b3cddda11ff9c2e86c4 branches: master commit: 0ebc3242d99d3b9d00891b3cddda11ff9c2e86c4 user: Sergey Kandaurov date: Tue, 29 Oct 2024 00:50:40 +0400 description: SSL: error message default in object caching API. This change initializes the "err" variable, used to produce a meaningful diagnostics on error path, to a good safe value. --- src/event/ngx_event_openssl_cache.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/event/ngx_event_openssl_cache.c b/src/event/ngx_event_openssl_cache.c index f43bdb5e7..8829e2879 100644 --- a/src/event/ngx_event_openssl_cache.c +++ b/src/event/ngx_event_openssl_cache.c @@ -138,6 +138,8 @@ ngx_ssl_cache_fetch(ngx_conf_t *cf, ngx_uint_t index, char **err, ngx_ssl_cache_type_t *type; ngx_ssl_cache_node_t *cn; + *err = NULL; + if (ngx_ssl_cache_init_key(cf->pool, index, path, &id) != NGX_OK) { return NULL; } @@ -183,6 +185,8 @@ ngx_ssl_cache_connection_fetch(ngx_pool_t *pool, ngx_uint_t index, char **err, { ngx_ssl_cache_key_t id; + *err = NULL; + if (ngx_ssl_cache_init_key(pool, index, path, &id) != NGX_OK) { return NULL; } From noreply at nginx.com Wed Nov 20 11:31:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Wed, 20 Nov 2024 11:31:02 +0000 (UTC) Subject: [nginx] Fixed missing double quote. Message-ID: <20241120113102.EA25B47756@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/cb1857407bec54804191cfc5ac8173df44f0c661 branches: master commit: cb1857407bec54804191cfc5ac8173df44f0c661 user: Nathan Mentze date: Tue, 19 Nov 2024 18:13:52 -0600 description: Fixed missing double quote. --- src/event/ngx_event_openssl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c index 2b1d107df..35e9f3c88 100644 --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -1347,7 +1347,7 @@ ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file) if (SSL_CTX_set0_tmp_dh_pkey(ssl->ctx, dh) != 1) { ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, - "SSL_CTX_set0_tmp_dh_pkey(\%s\") failed", file->data); + "SSL_CTX_set0_tmp_dh_pkey(\"%s\") failed", file->data); #if (OPENSSL_VERSION_NUMBER >= 0x3000001fL) EVP_PKEY_free(dh); #endif From noreply at nginx.com Thu Nov 21 12:09:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Thu, 21 Nov 2024 12:09:02 +0000 (UTC) Subject: [nginx] Mp4: unordered stsc chunks error for the final chunk. Message-ID: <20241121120902.21830478E6@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/d1a02451c3c5767b5d0f23e138db98a9f7801335 branches: master commit: d1a02451c3c5767b5d0f23e138db98a9f7801335 user: Roman Arutyunyan date: Wed, 2 Oct 2024 16:22:15 +0400 description: Mp4: unordered stsc chunks error for the final chunk. Currently an error is triggered if any of the chunk runs in stsc are unordered. This however does not include the final chunk run, which ends with trak->chunks + 1. The previous chunk index can be larger leading to a 32-bit overflow. This could allow to skip the validity check "if (start_sample > n)". This could later lead to a large trak->start_chunk/trak->end_chunk, which would be caught later in ngx_http_mp4_update_stco_atom() or ngx_http_mp4_update_co64_atom(). While there are no implications of the validity check being avoided, the change still adds a check to ensure the final chunk run is ordered, to produce a meaningful error and avoid a potential integer overflow. --- src/http/modules/ngx_http_mp4_module.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/http/modules/ngx_http_mp4_module.c b/src/http/modules/ngx_http_mp4_module.c index 2ca059136..49b0999cf 100644 --- a/src/http/modules/ngx_http_mp4_module.c +++ b/src/http/modules/ngx_http_mp4_module.c @@ -3189,6 +3189,13 @@ ngx_http_mp4_crop_stsc_data(ngx_http_mp4_file_t *mp4, next_chunk = trak->chunks + 1; + if (next_chunk < chunk) { + ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, + "unordered mp4 stsc chunks in \"%s\"", + mp4->file.name.data); + return NGX_ERROR; + } + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "sample:%uD, chunk:%uD, chunks:%uD, samples:%uD", start_sample, chunk, next_chunk - chunk, samples); From noreply at nginx.com Thu Nov 21 12:09:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Thu, 21 Nov 2024 12:09:02 +0000 (UTC) Subject: [nginx] Mp4: fixed handling an empty run of chunks in stsc atom. Message-ID: <20241121120902.1DA81478E3@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/6ec099a3786f2ddbe007009d5526ff2ec9316d23 branches: master commit: 6ec099a3786f2ddbe007009d5526ff2ec9316d23 user: Roman Arutyunyan date: Mon, 23 Sep 2024 15:51:30 +0400 description: Mp4: fixed handling an empty run of chunks in stsc atom. A specially crafted mp4 file with an empty run of chunks in the stsc atom and a large value for samples per chunk for that run, combined with a specially crafted request, allowed to store that large value in prev_samples and later in trak->end_chunk_samples while in ngx_http_mp4_crop_stsc_data(). Later in ngx_http_mp4_update_stsz_atom() this could result in buffer overread while calculating trak->end_chunk_samples_size. Now the value of samples per chunk specified for an empty run is ignored. --- src/http/modules/ngx_http_mp4_module.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/http/modules/ngx_http_mp4_module.c b/src/http/modules/ngx_http_mp4_module.c index 041ad263b..2ca059136 100644 --- a/src/http/modules/ngx_http_mp4_module.c +++ b/src/http/modules/ngx_http_mp4_module.c @@ -3176,7 +3176,10 @@ ngx_http_mp4_crop_stsc_data(ngx_http_mp4_file_t *mp4, start_sample -= n; - prev_samples = samples; + if (next_chunk > chunk) { + prev_samples = samples; + } + chunk = next_chunk; samples = ngx_mp4_get_32value(entry->samples); id = ngx_mp4_get_32value(entry->id); From noreply at nginx.com Thu Nov 21 12:09:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Thu, 21 Nov 2024 12:09:02 +0000 (UTC) Subject: [nginx] Mp4: prevent chunk index underflow. Message-ID: <20241121120902.27A7B478E7@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/569948aa12409773f27572fca3d2c8e18c9c657f branches: master commit: 569948aa12409773f27572fca3d2c8e18c9c657f user: Roman Arutyunyan date: Tue, 22 Oct 2024 18:34:13 +0400 description: Mp4: prevent chunk index underflow. When cropping stsc atom, it's assumed that chunk index is never 0. Based on this assumption, start_chunk and end_chunk are calculated by subtracting 1 from it. If chunk index is zero, start_chunk or end_chunk may underflow, which will later trigger "start/end time is out mp4 stco chunks" error. The change adds an explicit check for zero chunk index to avoid underflow and report a proper error. Zero chunk index is explicitly banned in ISO/IEC 14496-12, 8.7.4 Sample To Chunk Box. It's also implicitly banned in QuickTime File Format specification. Description of chunk offset table references "Chunk 1" as the first table element. --- src/http/modules/ngx_http_mp4_module.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/http/modules/ngx_http_mp4_module.c b/src/http/modules/ngx_http_mp4_module.c index 49b0999cf..b7bd192df 100644 --- a/src/http/modules/ngx_http_mp4_module.c +++ b/src/http/modules/ngx_http_mp4_module.c @@ -3221,6 +3221,12 @@ found: return NGX_ERROR; } + if (chunk == 0) { + ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, + "zero chunk in \"%s\"", mp4->file.name.data); + return NGX_ERROR; + } + target_chunk = chunk - 1; target_chunk += start_sample / samples; chunk_samples = start_sample % samples; From noreply at nginx.com Fri Nov 22 09:48:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Fri, 22 Nov 2024 09:48:02 +0000 (UTC) Subject: [nginx] SSL: a new macro to set default protocol versions. Message-ID: <20241122094802.5B9DF478E7@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/476d6526b2e8297025c608425f4cad07b4f65990 branches: master commit: 476d6526b2e8297025c608425f4cad07b4f65990 user: Sergey Kandaurov date: Mon, 18 Nov 2024 13:39:13 +0400 description: SSL: a new macro to set default protocol versions. This simplifies merging protocol values after ea15896 and ebd18ec. Further, as outlined in ebd18ec18, for libraries preceeding TLSv1.2+ support, only meaningful versions TLSv1 and TLSv1.1 are set by default. While here, fixed indentation. --- src/event/ngx_event_openssl.h | 7 +++++++ src/http/modules/ngx_http_grpc_module.c | 10 +--------- src/http/modules/ngx_http_proxy_module.c | 10 +--------- src/http/modules/ngx_http_ssl_module.c | 10 +--------- src/http/modules/ngx_http_uwsgi_module.c | 10 +--------- src/mail/ngx_mail_ssl_module.c | 10 +--------- src/stream/ngx_stream_proxy_module.c | 10 +--------- src/stream/ngx_stream_ssl_module.c | 10 +--------- 8 files changed, 14 insertions(+), 63 deletions(-) diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h index 6d171229c..2147205d6 100644 --- a/src/event/ngx_event_openssl.h +++ b/src/event/ngx_event_openssl.h @@ -187,6 +187,13 @@ typedef struct { #define NGX_SSL_TLSv1_3 0x0040 +#if (defined SSL_OP_NO_TLSv1_2 || defined SSL_OP_NO_TLSv1_3) +#define NGX_SSL_DEFAULT_PROTOCOLS (NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3) +#else +#define NGX_SSL_DEFAULT_PROTOCOLS (NGX_SSL_TLSv1|NGX_SSL_TLSv1_1) +#endif + + #define NGX_SSL_BUFFER 1 #define NGX_SSL_CLIENT 2 diff --git a/src/http/modules/ngx_http_grpc_module.c b/src/http/modules/ngx_http_grpc_module.c index 0a103ac66..326720447 100644 --- a/src/http/modules/ngx_http_grpc_module.c +++ b/src/http/modules/ngx_http_grpc_module.c @@ -4475,16 +4475,8 @@ ngx_http_grpc_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_value(conf->upstream.ssl_session_reuse, prev->upstream.ssl_session_reuse, 1); -#ifndef SSL_OP_NO_TLSv1_2 ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols, - (NGX_CONF_BITMASK_SET - |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1 - |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); -#else - ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols, - (NGX_CONF_BITMASK_SET - |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); -#endif + (NGX_CONF_BITMASK_SET|NGX_SSL_DEFAULT_PROTOCOLS)); ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers, "DEFAULT"); diff --git a/src/http/modules/ngx_http_proxy_module.c b/src/http/modules/ngx_http_proxy_module.c index 73d8ce2a8..25fa92bae 100644 --- a/src/http/modules/ngx_http_proxy_module.c +++ b/src/http/modules/ngx_http_proxy_module.c @@ -3942,16 +3942,8 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_value(conf->upstream.ssl_session_reuse, prev->upstream.ssl_session_reuse, 1); -#ifndef SSL_OP_NO_TLSv1_2 ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols, - (NGX_CONF_BITMASK_SET - |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1 - |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); -#else - ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols, - (NGX_CONF_BITMASK_SET - |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); -#endif + (NGX_CONF_BITMASK_SET|NGX_SSL_DEFAULT_PROTOCOLS)); ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers, "DEFAULT"); diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c index 2ab9b84b7..0e892b04d 100644 --- a/src/http/modules/ngx_http_ssl_module.c +++ b/src/http/modules/ngx_http_ssl_module.c @@ -651,16 +651,8 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_value(conf->early_data, prev->early_data, 0); ngx_conf_merge_value(conf->reject_handshake, prev->reject_handshake, 0); -#ifndef SSL_OP_NO_TLSv1_2 ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols, - (NGX_CONF_BITMASK_SET - |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1 - |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); -#else - ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols, - (NGX_CONF_BITMASK_SET - |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); -#endif + (NGX_CONF_BITMASK_SET|NGX_SSL_DEFAULT_PROTOCOLS)); ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size, NGX_SSL_BUFSIZE); diff --git a/src/http/modules/ngx_http_uwsgi_module.c b/src/http/modules/ngx_http_uwsgi_module.c index 9e9682bc3..f42ae706a 100644 --- a/src/http/modules/ngx_http_uwsgi_module.c +++ b/src/http/modules/ngx_http_uwsgi_module.c @@ -1899,16 +1899,8 @@ ngx_http_uwsgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_value(conf->upstream.ssl_session_reuse, prev->upstream.ssl_session_reuse, 1); -#ifndef SSL_OP_NO_TLSv1_2 ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols, - (NGX_CONF_BITMASK_SET - |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1 - |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); -#else - ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols, - (NGX_CONF_BITMASK_SET - |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); -#endif + (NGX_CONF_BITMASK_SET|NGX_SSL_DEFAULT_PROTOCOLS)); ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers, "DEFAULT"); diff --git a/src/mail/ngx_mail_ssl_module.c b/src/mail/ngx_mail_ssl_module.c index 4c3f41a58..176e9c624 100644 --- a/src/mail/ngx_mail_ssl_module.c +++ b/src/mail/ngx_mail_ssl_module.c @@ -343,16 +343,8 @@ ngx_mail_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_value(conf->prefer_server_ciphers, prev->prefer_server_ciphers, 0); -#ifndef SSL_OP_NO_TLSv1_2 ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols, - (NGX_CONF_BITMASK_SET - |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1 - |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); -#else - ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols, - (NGX_CONF_BITMASK_SET - |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); -#endif + (NGX_CONF_BITMASK_SET|NGX_SSL_DEFAULT_PROTOCOLS)); ngx_conf_merge_uint_value(conf->verify, prev->verify, 0); ngx_conf_merge_uint_value(conf->verify_depth, prev->verify_depth, 1); diff --git a/src/stream/ngx_stream_proxy_module.c b/src/stream/ngx_stream_proxy_module.c index 2b6f9baaf..e978056ef 100644 --- a/src/stream/ngx_stream_proxy_module.c +++ b/src/stream/ngx_stream_proxy_module.c @@ -2181,16 +2181,8 @@ ngx_stream_proxy_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_value(conf->ssl_session_reuse, prev->ssl_session_reuse, 1); -#ifndef SSL_OP_NO_TLSv1_2 ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols, - (NGX_CONF_BITMASK_SET - |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1 - |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); -#else - ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols, - (NGX_CONF_BITMASK_SET - |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); -#endif + (NGX_CONF_BITMASK_SET|NGX_SSL_DEFAULT_PROTOCOLS)); ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers, "DEFAULT"); diff --git a/src/stream/ngx_stream_ssl_module.c b/src/stream/ngx_stream_ssl_module.c index 8177d580f..dfbaa0e2f 100644 --- a/src/stream/ngx_stream_ssl_module.c +++ b/src/stream/ngx_stream_ssl_module.c @@ -882,16 +882,8 @@ ngx_stream_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_value(conf->reject_handshake, prev->reject_handshake, 0); -#ifndef SSL_OP_NO_TLSv1_2 ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols, - (NGX_CONF_BITMASK_SET - |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1 - |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); -#else - ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols, - (NGX_CONF_BITMASK_SET - |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)); -#endif + (NGX_CONF_BITMASK_SET|NGX_SSL_DEFAULT_PROTOCOLS)); ngx_conf_merge_uint_value(conf->verify, prev->verify, 0); ngx_conf_merge_uint_value(conf->verify_depth, prev->verify_depth, 1); From noreply at nginx.com Fri Nov 22 18:54:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Fri, 22 Nov 2024 18:54:02 +0000 (UTC) Subject: [njs] Throwing exception when prototype is not found. Message-ID: <20241122185402.EA2B1478ED@pubserv1.nginx> details: https://github.com/nginx/njs/commit/983b397b016cc0854268394559098ae030d36eb4 branches: master commit: 983b397b016cc0854268394559098ae030d36eb4 user: Dmitry Volyntsev date: Tue, 12 Nov 2024 22:57:48 -0800 description: Throwing exception when prototype is not found. --- src/njs_object.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/njs_object.c b/src/njs_object.c index f2aa46a3..65e80945 100644 --- a/src/njs_object.c +++ b/src/njs_object.c @@ -2159,6 +2159,8 @@ njs_object_prototype_create_constructor(njs_vm_t *vm, njs_object_prop_t *prop, } while (object != NULL); + njs_internal_error(vm, "prototype not found"); + return NJS_ERROR; } else { From noreply at nginx.com Fri Nov 22 18:54:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Fri, 22 Nov 2024 18:54:02 +0000 (UTC) Subject: [njs] Fixed absolute scope in cloned VMs. Message-ID: <20241122185402.EDB2B478EE@pubserv1.nginx> details: https://github.com/nginx/njs/commit/11c36e925bc63633f8ca39ee2f237cc13fe5519d branches: master commit: 11c36e925bc63633f8ca39ee2f237cc13fe5519d user: Dmitry Volyntsev date: Wed, 13 Nov 2024 21:14:34 -0800 description: Fixed absolute scope in cloned VMs. --- src/njs_vm.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/njs_vm.c b/src/njs_vm.c index 908c40c8..90428cb4 100644 --- a/src/njs_vm.c +++ b/src/njs_vm.c @@ -384,7 +384,7 @@ njs_vm_clone(njs_vm_t *vm, njs_external_ptr_t external) njs_mp_t *nmp; njs_vm_t *nvm; njs_int_t ret; - njs_value_t **global; + njs_value_t **global, **value; njs_thread_log_debug("CLONE:"); @@ -423,6 +423,24 @@ njs_vm_clone(njs_vm_t *vm, njs_external_ptr_t external) goto fail; } + if (nvm->options.unsafe) { + nvm->scope_absolute = njs_arr_create(nvm->mem_pool, + vm->scope_absolute->items, + sizeof(njs_value_t *)); + if (njs_slow_path(nvm->scope_absolute == NULL)) { + goto fail; + } + + value = njs_arr_add_multiple(nvm->scope_absolute, + vm->scope_absolute->items); + if (njs_slow_path(value == NULL)) { + goto fail; + } + + memcpy(value, vm->scope_absolute->start, + vm->scope_absolute->items * sizeof(njs_value_t *)); + } + nvm->levels[NJS_LEVEL_GLOBAL] = global; /* globalThis and this */ From noreply at nginx.com Fri Nov 22 18:54:03 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Fri, 22 Nov 2024 18:54:03 +0000 (UTC) Subject: [njs] Modules: refactored preloading. Message-ID: <20241122185403.10B1C478F1@pubserv1.nginx> details: https://github.com/nginx/njs/commit/283282f3656f2d710eb03e64d73dff4256479868 branches: master commit: 283282f3656f2d710eb03e64d73dff4256479868 user: Dmitry Volyntsev date: Wed, 13 Nov 2024 22:03:14 -0800 description: Modules: refactored preloading. --- nginx/ngx_js.c | 109 +++++++++++++++++---------------------------------------- nginx/ngx_js.h | 2 -- 2 files changed, 33 insertions(+), 78 deletions(-) diff --git a/nginx/ngx_js.c b/nginx/ngx_js.c index 20c8b2f3..05f6aa7e 100644 --- a/nginx/ngx_js.c +++ b/nginx/ngx_js.c @@ -57,6 +57,7 @@ static ngx_int_t ngx_engine_njs_string(ngx_engine_t *e, njs_opaque_value_t *value, ngx_str_t *str); static void ngx_engine_njs_destroy(ngx_engine_t *e, ngx_js_ctx_t *ctx, ngx_js_loc_conf_t *conf); +static ngx_int_t ngx_js_init_preload_vm(njs_vm_t *vm, ngx_js_loc_conf_t *conf); #if (NJS_HAVE_QUICKJS) static ngx_int_t ngx_engine_qjs_init(ngx_engine_t *engine, @@ -547,8 +548,8 @@ static ngx_int_t ngx_engine_njs_init(ngx_engine_t *engine, ngx_engine_opts_t *opts) { njs_vm_t *vm; - ngx_int_t rc; - njs_vm_opt_t vm_options; + ngx_int_t rc; + njs_vm_opt_t vm_options; njs_vm_opt_init(&vm_options); @@ -558,6 +559,7 @@ ngx_engine_njs_init(ngx_engine_t *engine, ngx_engine_opts_t *opts) vm_options.file = opts->file; vm_options.argv = ngx_argv; vm_options.argc = ngx_argc; + vm_options.init = 1; vm = njs_vm_create(&vm_options); if (vm == NULL) { @@ -596,6 +598,15 @@ ngx_engine_njs_compile(ngx_js_loc_conf_t *conf, ngx_log_t *log, u_char *start, static const njs_str_t file_name_key = njs_str("fileName"); vm = conf->engine->u.njs.vm; + + if (conf->preload_objects != NGX_CONF_UNSET_PTR) { + if (ngx_js_init_preload_vm(vm, conf) != NGX_OK) { + ngx_log_error(NGX_LOG_EMERG, log, 0, + "failed to initialize preload objects"); + return NGX_ERROR; + } + } + end = start + size; rc = njs_vm_compile(vm, &start, end); @@ -641,14 +652,10 @@ ngx_engine_njs_compile(ngx_js_loc_conf_t *conf, ngx_log_t *log, u_char *start, ngx_engine_t * ngx_njs_clone(ngx_js_ctx_t *ctx, ngx_js_loc_conf_t *cf, void *external) { - njs_vm_t *vm; - njs_int_t rc; - njs_str_t key; - ngx_str_t exception; - ngx_uint_t i; - ngx_engine_t *engine; - njs_opaque_value_t retval; - ngx_js_named_path_t *preload; + njs_vm_t *vm; + ngx_str_t exception; + ngx_engine_t *engine; + njs_opaque_value_t retval; vm = njs_vm_clone(cf->engine->u.njs.vm, external); if (vm == NULL) { @@ -664,27 +671,6 @@ ngx_njs_clone(ngx_js_ctx_t *ctx, ngx_js_loc_conf_t *cf, void *external) engine->pool = njs_vm_memory_pool(vm); engine->u.njs.vm = vm; - /* bind objects from preload vm */ - - if (cf->preload_objects != NGX_CONF_UNSET_PTR) { - preload = cf->preload_objects->elts; - - for (i = 0; i < cf->preload_objects->nelts; i++) { - key.start = preload[i].name.data; - key.length = preload[i].name.len; - - rc = njs_vm_value(cf->preload_vm, &key, njs_value_arg(&retval)); - if (rc != NJS_OK) { - return NULL; - } - - rc = njs_vm_bind(vm, &key, njs_value_arg(&retval), 0); - if (rc != NJS_OK) { - return NULL; - } - } - } - if (njs_vm_start(vm, njs_value_arg(&retval)) == NJS_ERROR) { ngx_js_exception(vm, &exception); @@ -978,9 +964,6 @@ ngx_qjs_clone(ngx_js_ctx_t *ctx, ngx_js_loc_conf_t *cf, void *external) JS_SetHostPromiseRejectionTracker(rt, ngx_qjs_rejection_tracker, ctx); - - /* TODO: bind objects from preload vm */ - rv = JS_UNDEFINED; pc = engine->precompiled->start; length = engine->precompiled->items; @@ -3271,32 +3254,20 @@ ngx_js_preload_object(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) } -ngx_int_t -ngx_js_init_preload_vm(ngx_conf_t *cf, ngx_js_loc_conf_t *conf) +static ngx_int_t +ngx_js_init_preload_vm(njs_vm_t *vm, ngx_js_loc_conf_t *conf) { u_char *p, *start; size_t size; - njs_vm_t *vm; njs_int_t ret; ngx_uint_t i; - njs_vm_opt_t options; njs_opaque_value_t retval; ngx_js_named_path_t *preload; - njs_vm_opt_init(&options); - - options.init = 1; - options.addons = njs_js_addon_modules_shared; - - vm = njs_vm_create(&options); - if (vm == NULL) { - goto error; - } - njs_str_t str = njs_str( - "import fs from 'fs';" + "import __fs from 'fs';" - "let g = (function (np, no, nf, nsp, r) {" + "{ let g = (function (np, no, nf, nsp, r) {" "return function (n, p) {" "p = (p[0] == '/') ? p : ngx.conf_prefix + p;" "let o = r(p);" @@ -3312,7 +3283,7 @@ ngx_js_init_preload_vm(ngx_conf_t *cf, ngx_js_loc_conf_t *conf) "return;" "}" "})(JSON.parse,Object,Object.freeze," - "Object.setPrototypeOf,fs.readFileSync);\n" + "Object.setPrototypeOf,__fs.readFileSync);\n" ); size = str.length; @@ -3323,7 +3294,9 @@ ngx_js_init_preload_vm(ngx_conf_t *cf, ngx_js_loc_conf_t *conf) + preload[i].path.len; } - start = ngx_pnalloc(cf->pool, size); + size += sizeof("}\n") - 1; + + start = njs_mp_alloc(njs_vm_memory_pool(vm), size); if (start == NULL) { return NGX_ERROR; } @@ -3339,27 +3312,24 @@ ngx_js_init_preload_vm(ngx_conf_t *cf, ngx_js_loc_conf_t *conf) p = ngx_cpymem(p, "');\n", sizeof("');\n") - 1); } + p = ngx_cpymem(p, "}\n", sizeof("}\n") - 1); + ret = njs_vm_compile(vm, &start, start + size); if (ret != NJS_OK) { - goto error; + return NGX_ERROR; } ret = njs_vm_start(vm, njs_value_arg(&retval)); if (ret != NJS_OK) { - goto error; + return NGX_ERROR; } - conf->preload_vm = vm; - - return NGX_OK; - -error: - - if (vm != NULL) { - njs_vm_destroy(vm); + ret = njs_vm_reuse(vm); + if (ret != NJS_OK) { + return NGX_ERROR; } - return NGX_ERROR; + return NGX_OK; } @@ -3384,9 +3354,6 @@ ngx_js_merge_vm(ngx_conf_t *cf, ngx_js_loc_conf_t *conf, conf->type = prev->type; conf->paths = prev->paths; conf->engine = prev->engine; - - conf->preload_vm = prev->preload_vm; - return NGX_OK; } } @@ -3836,12 +3803,6 @@ ngx_js_init_conf_vm(ngx_conf_t *cf, ngx_js_loc_conf_t *conf, return NGX_ERROR; } - if (conf->preload_objects != NGX_CONF_UNSET_PTR) { - if (ngx_js_init_preload_vm(cf, (ngx_js_loc_conf_t *)conf) != NGX_OK) { - return NGX_ERROR; - } - } - size = 0; import = conf->imports->elts; @@ -3950,10 +3911,6 @@ ngx_js_cleanup_vm(void *data) ngx_js_loc_conf_t *jscf = data; jscf->engine->destroy(jscf->engine, NULL, NULL); - - if (jscf->preload_objects != NGX_CONF_UNSET_PTR) { - njs_vm_destroy(jscf->preload_vm); - } } diff --git a/nginx/ngx_js.h b/nginx/ngx_js.h index 3a51aef0..11dba9ed 100644 --- a/nginx/ngx_js.h +++ b/nginx/ngx_js.h @@ -126,7 +126,6 @@ typedef struct { ngx_array_t *imports; \ ngx_array_t *paths; \ \ - njs_vm_t *preload_vm; \ ngx_array_t *preload_objects; \ \ size_t buffer_size; \ @@ -393,7 +392,6 @@ void ngx_js_logger(ngx_connection_t *c, ngx_uint_t level, char * ngx_js_import(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); char * ngx_js_engine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); char * ngx_js_preload_object(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); -ngx_int_t ngx_js_init_preload_vm(ngx_conf_t *cf, ngx_js_loc_conf_t *conf); ngx_int_t ngx_js_merge_vm(ngx_conf_t *cf, ngx_js_loc_conf_t *conf, ngx_js_loc_conf_t *prev, ngx_int_t (*init_vm)(ngx_conf_t *cf, ngx_js_loc_conf_t *conf)); From noreply at nginx.com Fri Nov 22 18:54:03 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Fri, 22 Nov 2024 18:54:03 +0000 (UTC) Subject: [njs] Allow to execute some code before cloning. Message-ID: <20241122185403.09B62478F0@pubserv1.nginx> details: https://github.com/nginx/njs/commit/16f42c87dead030a26675b4c8eeea0b15b1e15f5 branches: master commit: 16f42c87dead030a26675b4c8eeea0b15b1e15f5 user: Dmitry Volyntsev date: Wed, 13 Nov 2024 14:50:20 -0800 description: Allow to execute some code before cloning. --- src/njs.h | 2 + src/njs_builtin.c | 4 + src/njs_object.c | 221 ++++++++++++++++++++++++++++++++++++++++++++++- src/njs_object.h | 1 + src/njs_object_prop.c | 1 + src/njs_vm.c | 31 +++++-- src/njs_vmcode.c | 2 + src/test/njs_unit_test.c | 63 +++++++++++++- 8 files changed, 310 insertions(+), 15 deletions(-) diff --git a/src/njs.h b/src/njs.h index 8809e295..06eb770c 100644 --- a/src/njs.h +++ b/src/njs.h @@ -139,6 +139,7 @@ typedef enum { NJS_ENUM_STRING = 8, NJS_ENUM_SYMBOL = 16, NJS_ENUM_ENUMERABLE_ONLY = 32, + NJS_ENUM_NON_SHARED_ONLY = 64, } njs_object_enum_t; @@ -302,6 +303,7 @@ NJS_EXPORT njs_mod_t *njs_vm_add_module(njs_vm_t *vm, njs_str_t *name, njs_value_t *value); NJS_EXPORT njs_mod_t *njs_vm_compile_module(njs_vm_t *vm, njs_str_t *name, u_char **start, u_char *end); +NJS_EXPORT njs_int_t njs_vm_reuse(njs_vm_t *vm); NJS_EXPORT njs_vm_t *njs_vm_clone(njs_vm_t *vm, njs_external_ptr_t external); NJS_EXPORT njs_int_t njs_vm_enqueue_job(njs_vm_t *vm, njs_function_t *function, diff --git a/src/njs_builtin.c b/src/njs_builtin.c index 812f18d2..279c2158 100644 --- a/src/njs_builtin.c +++ b/src/njs_builtin.c @@ -873,6 +873,8 @@ njs_top_level_object(njs_vm_t *vm, njs_object_prop_t *self, if (njs_slow_path(object == NULL)) { return NJS_ERROR; } + + object->__proto__ = njs_vm_proto(vm, NJS_OBJ_TYPE_OBJECT); } prop = njs_object_prop_alloc(vm, &self->name, retval, 1); @@ -920,6 +922,8 @@ njs_top_level_constructor(njs_vm_t *vm, njs_object_prop_t *self, ctor = &njs_vm_ctor(vm, njs_prop_magic16(self)); njs_set_function(retval, ctor); + + return NJS_OK; } prop = njs_object_prop_alloc(vm, &self->name, retval, 1); diff --git a/src/njs_object.c b/src/njs_object.c index 65e80945..b3eae9a8 100644 --- a/src/njs_object.c +++ b/src/njs_object.c @@ -65,7 +65,7 @@ njs_object_t * njs_object_value_copy(njs_vm_t *vm, njs_value_t *value) { size_t size; - njs_object_t *object; + njs_object_t *object, *proto; object = njs_object(value); @@ -73,13 +73,37 @@ njs_object_value_copy(njs_vm_t *vm, njs_value_t *value) return object; } - size = njs_is_object_value(value) ? sizeof(njs_object_value_t) - : sizeof(njs_object_t); + switch (object->type) { + case NJS_OBJECT: + size = sizeof(njs_object_t); + proto = (object->__proto__ != NULL) + ? njs_vm_proto(vm, NJS_OBJ_TYPE_OBJECT) + : NULL; + break; + case NJS_ARRAY: + size = sizeof(njs_array_t); + njs_assert_msg(!object->fast_array, + "shared fast_array is not supported"); + proto = (object->__proto__ != NULL) + ? njs_vm_proto(vm, NJS_OBJ_TYPE_ARRAY) + : NULL; + break; + case NJS_OBJECT_VALUE: + size = sizeof(njs_object_value_t); + proto = (object->__proto__ != NULL) + ? njs_vm_proto(vm, NJS_OBJ_TYPE_OBJECT) + : NULL; + break; + default: + njs_internal_error(vm, "unexpected object type to copy"); + return NULL; + } + object = njs_mp_alloc(vm->mem_pool, size); if (njs_fast_path(object != NULL)) { memcpy(object, njs_object(value), size); - object->__proto__ = njs_vm_proto(vm, NJS_OBJ_TYPE_OBJECT); + object->__proto__ = proto; object->shared = 0; value->data.u.object = object; return object; @@ -917,6 +941,10 @@ njs_get_own_ordered_keys(njs_vm_t *vm, const njs_object_t *object, njs_lvlhsh_each_init(&lhe, &njs_object_hash_proto); hash = &object->shared_hash; + if (flags & NJS_ENUM_NON_SHARED_ONLY) { + goto local_hash; + } + for ( ;; ) { prop = njs_lvlhsh_each(hash, &lhe); @@ -984,6 +1012,8 @@ njs_get_own_ordered_keys(njs_vm_t *vm, const njs_object_t *object, } } +local_hash: + njs_lvlhsh_each_init(&lhe, &njs_object_hash_proto); hash = &object->hash; @@ -1187,6 +1217,189 @@ njs_traverse_visited(njs_arr_t *list, const njs_value_t *value) } +static njs_int_t +njs_object_copy_shared_hash(njs_vm_t *vm, njs_object_t *object) +{ + njs_int_t ret; + njs_flathsh_t new_hash, *shared_hash; + njs_object_prop_t *prop; + njs_flathsh_each_t fhe; + njs_flathsh_query_t fhq; + + fhq.replace = 0; + fhq.proto = &njs_object_hash_proto; + fhq.pool = vm->mem_pool; + + njs_flathsh_init(&new_hash); + shared_hash = &object->shared_hash; + + njs_flathsh_each_init(&fhe, &njs_object_hash_proto); + + for ( ;; ) { + prop = njs_flathsh_each(shared_hash, &fhe); + + if (prop == NULL) { + break; + } + + if (njs_is_symbol(&prop->name)) { + fhq.key_hash = njs_symbol_key(&prop->name); + fhq.key.start = NULL; + + } else { + njs_string_get(&prop->name, &fhq.key); + fhq.key_hash = njs_djb_hash(fhq.key.start, fhq.key.length); + } + + fhq.value = prop; + + ret = njs_flathsh_insert(&new_hash, &fhq); + if (njs_slow_path(ret != NJS_OK)) { + njs_internal_error(vm, "flathsh insert failed"); + return NJS_ERROR; + } + } + + object->shared_hash = new_hash; + + return NJS_OK; +} + + +njs_int_t +njs_object_make_shared(njs_vm_t *vm, njs_object_t *object) +{ + njs_int_t ret; + njs_arr_t visited; + njs_object_t **start; + njs_value_t value, *key; + njs_traverse_t *s; + njs_object_prop_t *prop; + njs_property_query_t pq; + njs_traverse_t state[NJS_TRAVERSE_MAX_DEPTH]; + + s = &state[0]; + s->parent = NULL; + s->index = 0; + njs_set_object(&s->value, object); + + s->keys = njs_value_own_enumerate(vm, &s->value, NJS_ENUM_KEYS + | NJS_ENUM_STRING + | NJS_ENUM_NON_SHARED_ONLY); + if (njs_slow_path(s->keys == NULL)) { + return NJS_ERROR; + } + + if (s->keys->length != 0 + && !njs_flathsh_is_empty(&object->shared_hash)) + { + /* + * object->shared_hash can be shared with other objects + * and we do not want to modify other objects. + */ + + ret = njs_object_copy_shared_hash(vm, object); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } + } + + start = njs_arr_init(vm->mem_pool, &visited, NULL, 8, sizeof(void *)); + if (njs_slow_path(start == NULL)) { + return NJS_ERROR; + } + + (void) njs_traverse_visit(&visited, &s->value); + + pq.lhq.replace = 0; + pq.lhq.pool = vm->mem_pool; + + for ( ;; ) { + + if (s->index >= s->keys->length) { + njs_flathsh_init(&njs_object(&s->value)->hash); + njs_object(&s->value)->shared = 1; + njs_array_destroy(vm, s->keys); + s->keys = NULL; + + if (s == &state[0]) { + goto done; + } + + s--; + continue; + } + + + njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 0, 0); + key = &s->keys->start[s->index++]; + + ret = njs_property_query(vm, &pq, &s->value, key); + if (njs_slow_path(ret != NJS_OK)) { + if (ret == NJS_DECLINED) { + continue; + } + + return NJS_ERROR; + } + + + prop = pq.lhq.value; + + ret = njs_flathsh_insert(&njs_object(&s->value)->shared_hash, &pq.lhq); + if (njs_slow_path(ret != NJS_OK)) { + njs_internal_error(vm, "flathsh insert failed"); + return NJS_ERROR; + } + + njs_value_assign(&value, njs_prop_value(prop)); + + if (njs_is_object(&value) + && !njs_object(&value)->shared + && !njs_traverse_visited(&visited, &value)) + { + ret = njs_traverse_visit(&visited, &value); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } + + if (s == &state[NJS_TRAVERSE_MAX_DEPTH - 1]) { + njs_type_error(vm, "njs_object_traverse() recursion limit:%d", + NJS_TRAVERSE_MAX_DEPTH); + return NJS_ERROR; + } + + s++; + s->prop = NULL; + s->parent = &s[-1]; + s->index = 0; + njs_value_assign(&s->value, &value); + s->keys = njs_value_own_enumerate(vm, &s->value, NJS_ENUM_KEYS + | NJS_ENUM_STRING + | NJS_ENUM_NON_SHARED_ONLY); + if (njs_slow_path(s->keys == NULL)) { + return NJS_ERROR; + } + + if (s->keys->length != 0 + && !njs_flathsh_is_empty(&njs_object(&s->value)->shared_hash)) + { + ret = njs_object_copy_shared_hash(vm, njs_object(&s->value)); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } + } + } + } + +done: + + njs_arr_destroy(&visited); + + return NJS_OK; +} + + njs_int_t njs_object_traverse(njs_vm_t *vm, njs_object_t *object, void *ctx, njs_object_traverse_cb_t cb) diff --git a/src/njs_object.h b/src/njs_object.h index c6ae14b8..e3f58fdd 100644 --- a/src/njs_object.h +++ b/src/njs_object.h @@ -72,6 +72,7 @@ njs_array_t *njs_object_own_enumerate(njs_vm_t *vm, const njs_object_t *object, uint32_t flags); njs_int_t njs_object_traverse(njs_vm_t *vm, njs_object_t *object, void *ctx, njs_object_traverse_cb_t cb); +njs_int_t njs_object_make_shared(njs_vm_t *vm, njs_object_t *object); njs_int_t njs_object_hash_create(njs_vm_t *vm, njs_lvlhsh_t *hash, const njs_object_prop_t *prop, njs_uint_t n); njs_int_t njs_primitive_prototype_get_proto(njs_vm_t *vm, diff --git a/src/njs_object_prop.c b/src/njs_object_prop.c index c9147bba..a24af753 100644 --- a/src/njs_object_prop.c +++ b/src/njs_object_prop.c @@ -694,6 +694,7 @@ njs_prop_private_copy(njs_vm_t *vm, njs_property_query_t *pq, switch (value->type) { case NJS_OBJECT: + case NJS_ARRAY: case NJS_OBJECT_VALUE: object = njs_object_value_copy(vm, value); if (njs_slow_path(object == NULL)) { diff --git a/src/njs_vm.c b/src/njs_vm.c index 90428cb4..dbeffa51 100644 --- a/src/njs_vm.c +++ b/src/njs_vm.c @@ -378,6 +378,17 @@ njs_vm_compile_module(njs_vm_t *vm, njs_str_t *name, u_char **start, } +njs_int_t +njs_vm_reuse(njs_vm_t *vm) +{ + vm->active_frame = NULL; + vm->top_frame = NULL; + vm->modules = NULL; + + return njs_object_make_shared(vm, njs_object(&vm->global_value)); +} + + njs_vm_t * njs_vm_clone(njs_vm_t *vm, njs_external_ptr_t external) { @@ -464,17 +475,19 @@ njs_vm_runtime_init(njs_vm_t *vm) njs_int_t ret; njs_frame_t *frame; - frame = (njs_frame_t *) njs_function_frame_alloc(vm, NJS_FRAME_SIZE); - if (njs_slow_path(frame == NULL)) { - njs_memory_error(vm); - return NJS_ERROR; - } + if (vm->active_frame == NULL) { + frame = (njs_frame_t *) njs_function_frame_alloc(vm, NJS_FRAME_SIZE); + if (njs_slow_path(frame == NULL)) { + njs_memory_error(vm); + return NJS_ERROR; + } - frame->exception.catch = NULL; - frame->exception.next = NULL; - frame->previous_active_frame = NULL; + frame->exception.catch = NULL; + frame->exception.next = NULL; + frame->previous_active_frame = NULL; - vm->active_frame = frame; + vm->active_frame = frame; + } ret = njs_regexp_init(vm); if (njs_slow_path(ret != NJS_OK)) { diff --git a/src/njs_vmcode.c b/src/njs_vmcode.c index 838e2821..dbf7b116 100644 --- a/src/njs_vmcode.c +++ b/src/njs_vmcode.c @@ -2600,6 +2600,8 @@ njs_vmcode_import(njs_vm_t *vm, njs_mod_t *module, njs_value_t *retval) njs_memzero(m->start, m->items * sizeof(njs_value_t)); } + njs_assert(module->index < vm->modules->items); + value = njs_arr_item(vm->modules, module->index); if (!njs_is_null(value)) { diff --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c index c4dc6dde..d6ae4ed8 100644 --- a/src/test/njs_unit_test.c +++ b/src/test/njs_unit_test.c @@ -21863,6 +21863,27 @@ static njs_unit_test_t njs_shared_test[] = "cr.abc = 1; cr.abc"), njs_str("1") }, #endif + + { njs_str("JSON.stringify(preload)"), + njs_str("{\"a\":[1,{\"b\":2,\"c\":3}]}") }, + + { njs_str("preload.a.prop = 1"), + njs_str("TypeError: Cannot add property \"prop\", object is not extensible\n" + " at main (:1)\n") }, + + { njs_str("preload.a[0] = 2"), + njs_str("TypeError: Cannot assign to read-only property \"0\" of array\n" + " at main (:1)\n") }, + + { njs_str("preload.a.push(2)"), + njs_str("TypeError: (intermediate value)[\"push\"] is not a function\n" + " at main (:1)\n") }, + + { njs_str("Array.prototype.push.call(preload.a, 'waka')"), + njs_str("TypeError: Cannot add property \"2\", object is not extensible\n" + " at Array.prototype.push (native)\n" + " at Function.prototype.call (native)\n" + " at main (:1)\n") }, }; @@ -22400,6 +22421,7 @@ typedef struct { njs_bool_t backtrace; njs_bool_t handler; njs_bool_t async; + njs_bool_t preload; unsigned seed; } njs_opts_t; @@ -22701,8 +22723,21 @@ njs_unit_test(njs_unit_test_t tests[], size_t num, njs_str_t *name, njs_stat_t prev; njs_vm_opt_t options; njs_runtime_t *rt; + njs_opaque_value_t retval; njs_external_state_t *state; + njs_str_t preload = njs_str( + "globalThis.preload = JSON.parse(" + "'{\"a\": [1, {\"b\": 2, \"c\": 3}]}'," + "function (k, v) {" + "if (v instanceof Object) {" + "Object.freeze(Object.setPrototypeOf(v, null));" + "}" + "return v;" + "}" + ");" + ); + vm = NULL; rt = NULL; @@ -22718,6 +22753,7 @@ njs_unit_test(njs_unit_test_t tests[], size_t num, njs_str_t *name, njs_vm_opt_init(&options); + options.init = opts->preload; options.module = opts->module; options.unsafe = opts->unsafe; options.backtrace = opts->backtrace; @@ -22730,6 +22766,29 @@ njs_unit_test(njs_unit_test_t tests[], size_t num, njs_str_t *name, goto done; } + if (opts->preload) { + start = preload.start; + end = start + preload.length; + + ret = njs_vm_compile(vm, &start, end); + if (ret != NJS_OK) { + njs_printf("njs_vm_compile() preload failed\n"); + goto done; + } + + ret = njs_vm_start(vm, njs_value_arg(&retval)); + if (ret != NJS_OK) { + njs_printf("njs_vm_start() preload failed\n"); + goto done; + } + + ret = njs_vm_reuse(vm); + if (ret != NJS_OK) { + njs_printf("njs_vm_reuse() failed\n"); + goto done; + } + } + start = tests[i].script.start; end = start + tests[i].script.length; @@ -23953,7 +24012,7 @@ njs_disabled_denormals_tests(njs_unit_test_t tests[], size_t num, static njs_test_suite_t njs_suites[] = { { njs_str("script"), - { .repeat = 1, .unsafe = 1 }, + { .repeat = 1, .unsafe = 1, .preload = 1 }, njs_test, njs_nitems(njs_test), njs_unit_test }, @@ -24040,7 +24099,7 @@ static njs_test_suite_t njs_suites[] = njs_unit_test }, { njs_str("shared"), - { .externals = 1, .repeat = 128, .seed = 42, .unsafe = 1, .backtrace = 1 }, + { .externals = 1, .repeat = 128, .seed = 42, .unsafe = 1, .preload = 1, .backtrace = 1 }, njs_shared_test, njs_nitems(njs_shared_test), njs_unit_test }, From noreply at nginx.com Mon Nov 25 12:23:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Mon, 25 Nov 2024 12:23:02 +0000 (UTC) Subject: [nginx] QUIC: prevented BIO leak in case of error. Message-ID: <20241125122302.14CDD478ED@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/0864cca4d74e215acdcab20a68e025c6e3ee9efa branches: master commit: 0864cca4d74e215acdcab20a68e025c6e3ee9efa user: Roman Arutyunyan date: Fri, 22 Nov 2024 11:38:06 +0400 description: QUIC: prevented BIO leak in case of error. --- src/event/quic/ngx_event_quic_openssl_compat.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/event/quic/ngx_event_quic_openssl_compat.c b/src/event/quic/ngx_event_quic_openssl_compat.c index c7412e82b..6052bc683 100644 --- a/src/event/quic/ngx_event_quic_openssl_compat.c +++ b/src/event/quic/ngx_event_quic_openssl_compat.c @@ -391,6 +391,7 @@ SSL_set_quic_method(SSL *ssl, const SSL_QUIC_METHOD *quic_method) wbio = BIO_new(BIO_s_null()); if (wbio == NULL) { + BIO_free(rbio); return 0; } From noreply at nginx.com Mon Nov 25 13:38:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Mon, 25 Nov 2024 13:38:02 +0000 (UTC) Subject: [nginx] Upstream: disallow empty path in proxy_store and friends. Message-ID: <20241125133802.B511C478F3@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/a448dd52ee27ec3a550cb7d03fd27153f4799f0c branches: master commit: a448dd52ee27ec3a550cb7d03fd27153f4799f0c user: Sergey Kandaurov date: Thu, 21 Nov 2024 12:35:50 +0400 description: Upstream: disallow empty path in proxy_store and friends. Renaming a temporary file to an empty path ("") returns NGX_ENOPATH with a subsequent ngx_create_full_path() to create the full path. This function skips initial bytes as part of path separator lookup, which causes out of bounds access on short strings. The fix is to avoid renaming a temporary file to an obviously invalid path, as well as explicitly forbid such syntax for literal values. Although Coverity reports about potential type underflow, it is not actually possible because the terminating '\0' is always included. Notably, the run-time check is sufficient enough for Win32 as well. Other short invalid values result either in NGX_ENOENT or NGX_EEXIST and "MoveFile() .. failed" critical log messages, which involves a separate error handling. Prodded by Coverity (CID 1605485). --- src/http/modules/ngx_http_fastcgi_module.c | 5 +++++ src/http/modules/ngx_http_proxy_module.c | 5 +++++ src/http/modules/ngx_http_scgi_module.c | 5 +++++ src/http/modules/ngx_http_uwsgi_module.c | 5 +++++ src/http/ngx_http_upstream.c | 4 ++++ 5 files changed, 24 insertions(+) diff --git a/src/http/modules/ngx_http_fastcgi_module.c b/src/http/modules/ngx_http_fastcgi_module.c index 743fe0c0c..a41b496dc 100644 --- a/src/http/modules/ngx_http_fastcgi_module.c +++ b/src/http/modules/ngx_http_fastcgi_module.c @@ -3781,6 +3781,11 @@ ngx_http_fastcgi_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) return NGX_CONF_OK; } + if (value[1].len == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "empty path"); + return NGX_CONF_ERROR; + } + #if (NGX_HTTP_CACHE) if (flcf->upstream.cache > 0) { return "is incompatible with \"fastcgi_cache\""; diff --git a/src/http/modules/ngx_http_proxy_module.c b/src/http/modules/ngx_http_proxy_module.c index 25fa92bae..855ef523d 100644 --- a/src/http/modules/ngx_http_proxy_module.c +++ b/src/http/modules/ngx_http_proxy_module.c @@ -4943,6 +4943,11 @@ ngx_http_proxy_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) return NGX_CONF_OK; } + if (value[1].len == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "empty path"); + return NGX_CONF_ERROR; + } + #if (NGX_HTTP_CACHE) if (plcf->upstream.cache > 0) { return "is incompatible with \"proxy_cache\""; diff --git a/src/http/modules/ngx_http_scgi_module.c b/src/http/modules/ngx_http_scgi_module.c index 671cd2cb7..9023a36e4 100644 --- a/src/http/modules/ngx_http_scgi_module.c +++ b/src/http/modules/ngx_http_scgi_module.c @@ -1995,6 +1995,11 @@ ngx_http_scgi_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) return NGX_CONF_OK; } + if (value[1].len == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "empty path"); + return NGX_CONF_ERROR; + } + #if (NGX_HTTP_CACHE) if (scf->upstream.cache > 0) { return "is incompatible with \"scgi_cache\""; diff --git a/src/http/modules/ngx_http_uwsgi_module.c b/src/http/modules/ngx_http_uwsgi_module.c index f42ae706a..7988cc589 100644 --- a/src/http/modules/ngx_http_uwsgi_module.c +++ b/src/http/modules/ngx_http_uwsgi_module.c @@ -2322,6 +2322,11 @@ ngx_http_uwsgi_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) return NGX_CONF_OK; } + if (value[1].len == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "empty path"); + return NGX_CONF_ERROR; + } + #if (NGX_HTTP_CACHE) if (uwcf->upstream.cache > 0) { diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c index 82a230024..d95662c56 100644 --- a/src/http/ngx_http_upstream.c +++ b/src/http/ngx_http_upstream.c @@ -4357,6 +4357,10 @@ ngx_http_upstream_store(ngx_http_request_t *r, ngx_http_upstream_t *u) "upstream stores \"%s\" to \"%s\"", tf->file.name.data, path.data); + if (path.len == 0) { + return; + } + (void) ngx_ext_rename_file(&tf->file.name, &path, &ext); u->store = 0; From noreply at nginx.com Tue Nov 26 13:42:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 26 Nov 2024 13:42:02 +0000 (UTC) Subject: [nginx] QUIC: got rid of memory copy when initializing constant values. Message-ID: <20241126134202.4320D478FA@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/3f755b5a9e7145d5ce6b897d2298d5f6c544acf7 branches: master commit: 3f755b5a9e7145d5ce6b897d2298d5f6c544acf7 user: Sergey Kandaurov date: Mon, 7 Oct 2024 18:43:48 +0400 description: QUIC: got rid of memory copy when initializing constant values. --- src/event/quic/ngx_event_quic_protection.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/event/quic/ngx_event_quic_protection.c b/src/event/quic/ngx_event_quic_protection.c index 57492825d..3f249b36a 100644 --- a/src/event/quic/ngx_event_quic_protection.c +++ b/src/event/quic/ngx_event_quic_protection.c @@ -18,6 +18,9 @@ #define NGX_QUIC_INITIAL_CIPHER TLS1_3_CK_AES_128_GCM_SHA256 +#define ngx_quic_md(str) { sizeof(str) - 1, str } + + static ngx_int_t ngx_hkdf_expand(u_char *out_key, size_t out_len, const EVP_MD *digest, const u_char *prk, size_t prk_len, const u_char *info, size_t info_len); @@ -606,7 +609,8 @@ ngx_quic_crypto_hp(ngx_quic_secret_t *s, u_char *out, u_char *in, { int outlen; EVP_CIPHER_CTX *ctx; - u_char zero[NGX_QUIC_HP_LEN] = {0}; + + static const u_char zero[NGX_QUIC_HP_LEN]; ctx = s->hp_ctx; @@ -948,16 +952,15 @@ ngx_quic_create_retry_packet(ngx_quic_header_t *pkt, ngx_str_t *res) { u_char *start; ngx_str_t ad, itag; - ngx_quic_md_t key; ngx_quic_secret_t secret; ngx_quic_ciphers_t ciphers; /* 5.8. Retry Packet Integrity */ - static u_char key_data[16] = - "\xbe\x0c\x69\x0b\x9f\x66\x57\x5a\x1d\x76\x6b\x54\xe3\x68\xc8\x4e"; - static u_char nonce[NGX_QUIC_IV_LEN] = + static ngx_quic_md_t key = ngx_quic_md( + "\xbe\x0c\x69\x0b\x9f\x66\x57\x5a\x1d\x76\x6b\x54\xe3\x68\xc8\x4e"); + static const u_char nonce[NGX_QUIC_IV_LEN] = "\x46\x15\x99\xd3\x5d\x63\x2b\xf2\x23\x98\x25\xbb"; - static ngx_str_t in = ngx_string(""); + static ngx_str_t in = ngx_string(""); ad.data = res->data; ad.len = ngx_quic_create_retry_itag(pkt, ad.data, &start); @@ -974,8 +977,6 @@ ngx_quic_create_retry_packet(ngx_quic_header_t *pkt, ngx_str_t *res) return NGX_ERROR; } - key.len = sizeof(key_data); - ngx_memcpy(key.data, key_data, sizeof(key_data)); secret.iv.len = NGX_QUIC_IV_LEN; if (ngx_quic_crypto_init(ciphers.c, &secret, &key, 1, pkt->log) From noreply at nginx.com Tue Nov 26 13:42:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 26 Nov 2024 13:42:02 +0000 (UTC) Subject: [nginx] QUIC: constified nonce parameter of crypto functions. Message-ID: <20241126134202.3FEBA478F9@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/9a025219f661fbe2148659cad490c06d5e3283df branches: master commit: 9a025219f661fbe2148659cad490c06d5e3283df user: Sergey Kandaurov date: Mon, 7 Oct 2024 18:19:24 +0400 description: QUIC: constified nonce parameter of crypto functions. This follows OpenSSL and BoringSSL API, and gives a hint to compiler that this parameter may not be modified. --- src/event/quic/ngx_event_quic_protection.c | 12 ++++++------ src/event/quic/ngx_event_quic_protection.h | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/event/quic/ngx_event_quic_protection.c b/src/event/quic/ngx_event_quic_protection.c index 55f0f6fd7..57492825d 100644 --- a/src/event/quic/ngx_event_quic_protection.c +++ b/src/event/quic/ngx_event_quic_protection.c @@ -29,10 +29,10 @@ static uint64_t ngx_quic_parse_pn(u_char **pos, ngx_int_t len, u_char *mask, uint64_t *largest_pn); static ngx_int_t ngx_quic_crypto_open(ngx_quic_secret_t *s, ngx_str_t *out, - u_char *nonce, ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log); + const u_char *nonce, ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log); #ifndef OPENSSL_IS_BORINGSSL static ngx_int_t ngx_quic_crypto_common(ngx_quic_secret_t *s, ngx_str_t *out, - u_char *nonce, ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log); + const u_char *nonce, ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log); #endif static ngx_int_t ngx_quic_crypto_hp_init(const EVP_CIPHER *cipher, @@ -441,7 +441,7 @@ ngx_quic_crypto_init(const ngx_quic_cipher_t *cipher, ngx_quic_secret_t *s, static ngx_int_t -ngx_quic_crypto_open(ngx_quic_secret_t *s, ngx_str_t *out, u_char *nonce, +ngx_quic_crypto_open(ngx_quic_secret_t *s, ngx_str_t *out, const u_char *nonce, ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log) { #ifdef OPENSSL_IS_BORINGSSL @@ -461,7 +461,7 @@ ngx_quic_crypto_open(ngx_quic_secret_t *s, ngx_str_t *out, u_char *nonce, ngx_int_t -ngx_quic_crypto_seal(ngx_quic_secret_t *s, ngx_str_t *out, u_char *nonce, +ngx_quic_crypto_seal(ngx_quic_secret_t *s, ngx_str_t *out, const u_char *nonce, ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log) { #ifdef OPENSSL_IS_BORINGSSL @@ -483,8 +483,8 @@ ngx_quic_crypto_seal(ngx_quic_secret_t *s, ngx_str_t *out, u_char *nonce, #ifndef OPENSSL_IS_BORINGSSL static ngx_int_t -ngx_quic_crypto_common(ngx_quic_secret_t *s, ngx_str_t *out, u_char *nonce, - ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log) +ngx_quic_crypto_common(ngx_quic_secret_t *s, ngx_str_t *out, + const u_char *nonce, ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log) { int len, enc; ngx_quic_crypto_ctx_t *ctx; diff --git a/src/event/quic/ngx_event_quic_protection.h b/src/event/quic/ngx_event_quic_protection.h index 34cfee61b..c09456f53 100644 --- a/src/event/quic/ngx_event_quic_protection.h +++ b/src/event/quic/ngx_event_quic_protection.h @@ -111,7 +111,7 @@ ngx_int_t ngx_quic_ciphers(ngx_uint_t id, ngx_quic_ciphers_t *ciphers); ngx_int_t ngx_quic_crypto_init(const ngx_quic_cipher_t *cipher, ngx_quic_secret_t *s, ngx_quic_md_t *key, ngx_int_t enc, ngx_log_t *log); ngx_int_t ngx_quic_crypto_seal(ngx_quic_secret_t *s, ngx_str_t *out, - u_char *nonce, ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log); + const u_char *nonce, ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log); void ngx_quic_crypto_cleanup(ngx_quic_secret_t *s); ngx_int_t ngx_quic_hkdf_expand(ngx_quic_hkdf_t *hkdf, const EVP_MD *digest, ngx_log_t *log); From noreply at nginx.com Tue Nov 26 14:28:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 26 Nov 2024 14:28:02 +0000 (UTC) Subject: [nginx] Realip: allowed square brackets with portless IPv6 address. Message-ID: <20241126142802.99AF5478FA@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/b2a67d261496555a46b8931935bf822ce9938294 branches: master commit: b2a67d261496555a46b8931935bf822ce9938294 user: Roman Arutyunyan date: Mon, 11 Nov 2024 22:28:30 +0400 description: Realip: allowed square brackets with portless IPv6 address. When client address is received, IPv6 address could be specified without square brackets and without port, as well as both with the brackets and port. The change allows IPv6 in square brackets and no port, which was previously considered an error. This format conforms to RFC 3986. The change also affects proxy_bind and friends. --- src/core/ngx_inet.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/core/ngx_inet.c b/src/core/ngx_inet.c index acb2ef48a..2233e617b 100644 --- a/src/core/ngx_inet.c +++ b/src/core/ngx_inet.c @@ -639,7 +639,11 @@ ngx_parse_addr_port(ngx_pool_t *pool, ngx_addr_t *addr, u_char *text, p = ngx_strlchr(text, last, ']'); - if (p == NULL || p == last - 1 || *++p != ':') { + if (p == last - 1) { + return ngx_parse_addr(pool, addr, text + 1, len - 2); + } + + if (p == NULL || *++p != ':') { return NGX_DECLINED; } From noreply at nginx.com Tue Nov 26 15:08:01 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 26 Nov 2024 15:08:01 +0000 (UTC) Subject: [nginx] Mail: handling of LOGIN IMAP command untagged response. Message-ID: <20241126150801.DFD3F478FB@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/ce88b171236de50843dba2c427a8b3e42778f2ca branches: master commit: ce88b171236de50843dba2c427a8b3e42778f2ca user: Sergey Kandaurov date: Thu, 24 Oct 2024 00:52:21 +0400 description: Mail: handling of LOGIN IMAP command untagged response. In particular, an untagged CAPABILITY response as described in the interim RFC 3501 internet drafts was seen in various IMAP servers. Previously resulted in a broken connection, now an untagged response is proxied to client. --- src/mail/ngx_mail_proxy_module.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/mail/ngx_mail_proxy_module.c b/src/mail/ngx_mail_proxy_module.c index efed9ab3e..1c6d0372e 100644 --- a/src/mail/ngx_mail_proxy_module.c +++ b/src/mail/ngx_mail_proxy_module.c @@ -1019,12 +1019,36 @@ ngx_mail_proxy_read_response(ngx_mail_session_t *s, ngx_uint_t state) break; case ngx_imap_passwd: + + /* + * untagged CAPABILITY response (draft-crispin-imapv-16), + * known to be sent by SmarterMail and Gmail + */ + + if (p[0] == '*' && p[1] == ' ') { + p += 2; + + while (p < b->last - 1) { + if (p[0] == CR && p[1] == LF) { + p += 2; + break; + } + + p++; + } + + if (b->last - p < 4) { + return NGX_AGAIN; + } + } + if (ngx_strncmp(p, s->tag.data, s->tag.len) == 0) { p += s->tag.len; if (p[0] == 'O' && p[1] == 'K') { return NGX_OK; } } + break; } From noreply at nginx.com Tue Nov 26 15:56:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 26 Nov 2024 15:56:02 +0000 (UTC) Subject: [nginx] nginx-1.27.3-RELEASE Message-ID: <20241126155602.65C2F478FC@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/e7bd2557458c26839da89e694067017eeb214348 branches: master commit: e7bd2557458c26839da89e694067017eeb214348 user: Sergey Kandaurov date: Tue, 26 Nov 2024 15:36:52 +0400 description: nginx-1.27.3-RELEASE --- docs/xml/nginx/changes.xml | 93 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/docs/xml/nginx/changes.xml b/docs/xml/nginx/changes.xml index c18dedff4..b46961704 100644 --- a/docs/xml/nginx/changes.xml +++ b/docs/xml/nginx/changes.xml @@ -5,6 +5,99 @@ + + + + +директива server в блоке upstream поддерживает +параметр resolve. + + +the "server" directive in the "upstream" block supports +the "resolve" parameter. + + + + + +директивы resolver и resolver_timeout в блоке upstream. + + +the "resolver" and "resolver_timeout" directives in the "upstream" block. + + + + + +поддержка SmarterMail-специфичного режима +IMAP LOGIN с нетегированным ответом CAPABILITY +в почтовом прокси-сервере. + + +SmarterMail specific mode support +for IMAP LOGIN with untagged CAPABILITY response +in the mail proxy module. + + + + + +теперь протоколы TLSv1 и TLSv1.1 по умолчанию запрещены. + + +now TLSv1 and TLSv1.1 protocols are disabled by default. + + + + + +IPv6-адрес в квадратных скобках без порта теперь можно указывать +в директивах proxy_bind, fastcgi_bind, grpc_bind, memcached_bind, +scgi_bind и uwsgi_bind, +а также как адрес клиента в модуле ngx_http_realip_module. + + +an IPv6 address in square brackets and no port can be specified +in the "proxy_bind", "fastcgi_bind", "grpc_bind", "memcached_bind", +"scgi_bind", and "uwsgi_bind" directives, +and as client address in ngx_http_realip_module. + + + + + +в модуле ngx_http_mp4_module.
+Спасибо Nils Bars. +
+ +in the ngx_http_mp4_module.
+Thanks to Nils Bars. +
+
+ + + +параметр so_keepalive директивы listen +мог работать некорректно на DragonFly BSD. + + +the "so_keepalive" parameter of the "listen" directive +might be handled incorrectly on DragonFly BSD. + + + + + +в директиве proxy_store. + + +in the "proxy_store" directive. + + + +
+ + From noreply at nginx.com Tue Nov 26 15:59:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 26 Nov 2024 15:59:02 +0000 (UTC) Subject: [nginx] Annotated tag created: release-1.27.3 Message-ID: <20241126155902.61130478FD@pubserv1.nginx> details: https://github.com/nginx/nginx/releases/tag/release-1.27.3 branches: commit: e7bd2557458c26839da89e694067017eeb214348 user: Sergey Kandaurov date: Tue Nov 26 19:57:51 2024 +0400 description: release-1.27.3 tag From noreply at nginx.com Wed Nov 27 16:08:02 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Wed, 27 Nov 2024 16:08:02 +0000 (UTC) Subject: [nginx] Version bump. Message-ID: <20241127160802.0564447CA0@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/e28ef42b97d5352e514f019cd3b9a6c07a87c580 branches: master commit: e28ef42b97d5352e514f019cd3b9a6c07a87c580 user: Roman Arutyunyan date: Wed, 27 Nov 2024 20:01:26 +0400 description: Version bump. --- src/core/nginx.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/nginx.h b/src/core/nginx.h index fb6fa4737..29f2c949f 100644 --- a/src/core/nginx.h +++ b/src/core/nginx.h @@ -9,8 +9,8 @@ #define _NGINX_H_INCLUDED_ -#define nginx_version 1027003 -#define NGINX_VERSION "1.27.3" +#define nginx_version 1027004 +#define NGINX_VERSION "1.27.4" #define NGINX_VER "nginx/" NGINX_VERSION #ifdef NGX_BUILD From noreply at nginx.com Wed Nov 27 22:46:01 2024 From: noreply at nginx.com (noreply at nginx.com) Date: Wed, 27 Nov 2024 22:46:01 +0000 (UTC) Subject: [njs] HTTP: fixed limit rated output. Message-ID: <20241127224601.F2F5547CA1@pubserv1.nginx> details: https://github.com/nginx/njs/commit/b300a93311a4ffeb37ca137eecda5f4cd92b4caf branches: master commit: b300a93311a4ffeb37ca137eecda5f4cd92b4caf user: Dmitry Volyntsev date: Mon, 25 Nov 2024 21:43:44 -0800 description: HTTP: fixed limit rated output. Previously, when r.return(code, body) was called from a subrequest handler with a body size larger than the sendfile_max_chunk value connection hanging might occur. --- nginx/ngx_http_js_module.c | 7 +++++-- nginx/t/js_return.t | 22 ++++++++++++++++++++-- nginx/t/js_subrequests.t | 21 +++++++++++++++++++-- 3 files changed, 44 insertions(+), 6 deletions(-) diff --git a/nginx/ngx_http_js_module.c b/nginx/ngx_http_js_module.c index 3e43ac7d..e900b716 100644 --- a/nginx/ngx_http_js_module.c +++ b/nginx/ngx_http_js_module.c @@ -1230,14 +1230,17 @@ ngx_http_js_content_write_event_handler(ngx_http_request_t *r) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http js content write event handler"); + c = r->connection; ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); if (!ngx_js_ctx_pending(ctx)) { ngx_http_js_content_finalize(r, ctx); - return; + + if (!c->buffered) { + return; + } } - c = r->connection; wev = c->write; if (wev->timedout) { diff --git a/nginx/t/js_return.t b/nginx/t/js_return.t index 6f8c4a93..1ecf5a50 100644 --- a/nginx/t/js_return.t +++ b/nginx/t/js_return.t @@ -47,6 +47,11 @@ http { js_content test.returnf; } + location /limit { + sendfile_max_chunk 5; + js_content test.returnf; + } + location /njs { js_content test.njs; } @@ -61,14 +66,19 @@ $t->write_file('test.js', <try_run('no njs return')->plan(6); +$t->try_run('no njs return')->plan(7); ############################################################################### @@ -85,6 +95,14 @@ unlike(http_get('/?c=404&t='), qr/Not.*html/s, 'return empty body'); } +TODO: { +local $TODO = 'not yet' unless has_version('0.8.8'); + +like(http_get('/limit?c=200&t=X&repeat=50'), qr/200 OK.*X{50}/s, + 'return limited'); + +} + ############################################################################### sub has_version { diff --git a/nginx/t/js_subrequests.t b/nginx/t/js_subrequests.t index d38573ba..bebc7c51 100644 --- a/nginx/t/js_subrequests.t +++ b/nginx/t/js_subrequests.t @@ -139,6 +139,11 @@ http { js_content test.sr_cache; } + location /sr_limit { + sendfile_max_chunk 5; + js_content test.sr_limit; + } + location /sr_unavail { js_content test.sr_unavail; @@ -385,6 +390,12 @@ $t->write_file('test.js', <write_file('test.js', <write_file('t', '["SEE-THIS"]'); -$t->try_run('no njs available')->plan(33); +$t->try_run('no njs available')->plan(34); $t->run_daemon(\&http_daemon); ############################################################################### @@ -597,6 +608,12 @@ ok(index($t->read_file('error.log'), 'subrequest can only be created for') > 0, } +TODO: { +local $TODO = 'not yet' unless has_version('0.8.8'); + +like(http_get('/sr_limit'), qr/x{100}/, 'sr_limit'); +} + $t->stop(); ok(index($t->read_file('error.log'), 'callback is not a function') > 0,