From fd3558456edc752d43b58a0c0e1b99e5a45edae9 Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Wed, 2 Jun 2021 16:14:22 +0300 Subject: Node.js: packaging new loader.js and loader.mjs. The files loader.js and loader.mjs (introduced in f85b85094541 and 3c551b9721df) were added to the packaged files list. --- src/nodejs/unit-http/package.json | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/nodejs/unit-http/package.json b/src/nodejs/unit-http/package.json index 81f06bd4..f0afe41c 100644 --- a/src/nodejs/unit-http/package.json +++ b/src/nodejs/unit-http/package.json @@ -10,6 +10,8 @@ "unit.cpp", "http.js", "http_server.js", + "loader.js", + "loader.mjs", "nxt_napi.h", "package.json", "socket.js", -- cgit From 1e3f7808b1091d76b80369a93f0361453247f368 Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Tue, 15 Jun 2021 10:35:15 +0300 Subject: Node.js: improving and test packaging. The patch removes the "files" section from package.json to avoid future issues with missing files. For package testing purposes, 'npm pack' is used instead of plain 'tar' to simulate packaging more accurately. --- src/nodejs/unit-http/package.json | 22 ---------------------- 1 file changed, 22 deletions(-) (limited to 'src') diff --git a/src/nodejs/unit-http/package.json b/src/nodejs/unit-http/package.json index f0afe41c..debbd492 100644 --- a/src/nodejs/unit-http/package.json +++ b/src/nodejs/unit-http/package.json @@ -3,28 +3,6 @@ "version": "%%VERSION%%", "description": "HTTP module for NGINX Unit", "main": "http.js", - "files": [ - "unit.h", - "version.h", - "addon.cpp", - "unit.cpp", - "http.js", - "http_server.js", - "loader.js", - "loader.mjs", - "nxt_napi.h", - "package.json", - "socket.js", - "binding.gyp", - "README.md", - "websocket.js", - "websocket_connection.js", - "websocket_frame.js", - "websocket_request.js", - "websocket_router.js", - "websocket_router_request.js", - "websocket_server.js" - ], "scripts": { "clean": "node-gyp clean", "configure": "node-gyp configure", -- cgit From c16123e7493118dad698ccac7e56bb475bac7def Mon Sep 17 00:00:00 2001 From: Zhidao HONG Date: Mon, 24 May 2021 16:15:42 +0800 Subject: Router: split nxt_http_return_conf_t from nxt_http_action_t. No functional changes. --- src/nxt_http.h | 20 ++++++-- src/nxt_http_return.c | 83 +++++++++++++++++++++++++++----- src/nxt_http_route.c | 130 +++++++++++++++++--------------------------------- 3 files changed, 132 insertions(+), 101 deletions(-) (limited to 'src') diff --git a/src/nxt_http.h b/src/nxt_http.h index f82d837e..18cf8680 100644 --- a/src/nxt_http.h +++ b/src/nxt_http.h @@ -201,15 +201,29 @@ typedef struct nxt_http_route_s nxt_http_route_t; typedef struct nxt_http_route_rule_s nxt_http_route_rule_t; +typedef struct { + nxt_conf_value_t *pass; + nxt_conf_value_t *ret; + nxt_str_t location; + nxt_conf_value_t *proxy; + nxt_conf_value_t *share; + nxt_str_t chroot; + nxt_conf_value_t *follow_symlinks; + nxt_conf_value_t *traverse_mounts; + nxt_conf_value_t *types; + nxt_conf_value_t *fallback; +} nxt_http_action_conf_t; + + struct nxt_http_action_s { nxt_http_action_t *(*handler)(nxt_task_t *task, nxt_http_request_t *r, nxt_http_action_t *action); union { + void *conf; nxt_http_route_t *route; nxt_upstream_t *upstream; uint32_t upstream_number; - nxt_http_status_t return_code; nxt_var_t *var; struct { @@ -319,8 +333,8 @@ nxt_int_t nxt_upstreams_joint_create(nxt_router_temp_conf_t *tmcf, void nxt_http_request_action(nxt_task_t *task, nxt_http_request_t *r, nxt_http_action_t *action); -nxt_http_action_t *nxt_http_return_handler(nxt_task_t *task, - nxt_http_request_t *r, nxt_http_action_t *action); +nxt_int_t nxt_http_return_init(nxt_mp_t *mp, nxt_http_action_t *action, + nxt_http_action_conf_t *acf); nxt_http_action_t *nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r, nxt_http_action_t *action); diff --git a/src/nxt_http_return.c b/src/nxt_http_return.c index c466cc25..18fd490d 100644 --- a/src/nxt_http_return.c +++ b/src/nxt_http_return.c @@ -7,29 +7,90 @@ #include +typedef struct { + nxt_http_status_t status; + nxt_str_t location; +} nxt_http_return_conf_t; + + +static nxt_http_action_t *nxt_http_return(nxt_task_t *task, + nxt_http_request_t *r, nxt_http_action_t *action); + + static const nxt_http_request_state_t nxt_http_return_send_state; +nxt_int_t +nxt_http_return_init(nxt_mp_t *mp, nxt_http_action_t *action, + nxt_http_action_conf_t *acf) +{ + nxt_str_t *loc; + nxt_uint_t encode; + nxt_http_return_conf_t *conf; + + conf = nxt_mp_zget(mp, sizeof(nxt_http_return_conf_t)); + if (nxt_slow_path(conf == NULL)) { + return NXT_ERROR; + } + + action->handler = nxt_http_return; + action->u.conf = conf; + + conf->status = nxt_conf_get_number(acf->ret); + + if (acf->location.length > 0) { + if (nxt_is_complex_uri_encoded(acf->location.start, + acf->location.length)) + { + loc = nxt_str_dup(mp, &conf->location, &acf->location); + if (nxt_slow_path(loc == NULL)) { + return NXT_ERROR; + } + + } else { + loc = &conf->location; + + encode = nxt_encode_complex_uri(NULL, acf->location.start, + acf->location.length); + loc->length = acf->location.length + encode * 2; + + loc->start = nxt_mp_nget(mp, loc->length); + if (nxt_slow_path(loc->start == NULL)) { + return NXT_ERROR; + } + + nxt_encode_complex_uri(loc->start, acf->location.start, + acf->location.length); + } + } + + return NXT_OK; +} + + nxt_http_action_t * -nxt_http_return_handler(nxt_task_t *task, nxt_http_request_t *r, +nxt_http_return(nxt_task_t *task, nxt_http_request_t *r, nxt_http_action_t *action) { - nxt_http_field_t *field; - nxt_http_status_t status; + nxt_http_field_t *field; + nxt_http_return_conf_t *conf; + + conf = action->u.conf; - status = action->u.return_code; + nxt_debug(task, "http return: %d (loc: \"%V\")", + conf->status, &conf->location); - if (status >= NXT_HTTP_BAD_REQUEST - && status <= NXT_HTTP_SERVER_ERROR_MAX) + if (conf->status >= NXT_HTTP_BAD_REQUEST + && conf->status <= NXT_HTTP_SERVER_ERROR_MAX) { - nxt_http_request_error(task, r, status); + nxt_http_request_error(task, r, conf->status); return NULL; } - r->status = status; + r->status = conf->status; r->resp.content_length_n = 0; - if (action->name.length > 0) { + if (conf->location.length > 0) { field = nxt_list_zero_add(r->resp.fields); if (nxt_slow_path(field == NULL)) { nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR); @@ -38,8 +99,8 @@ nxt_http_return_handler(nxt_task_t *task, nxt_http_request_t *r, nxt_http_field_name_set(field, "Location"); - field->value = action->name.start; - field->value_length = action->name.length; + field->value = conf->location.start; + field->value_length = conf->location.length; } r->state = &nxt_http_return_send_state; diff --git a/src/nxt_http_route.c b/src/nxt_http_route.c index 15b85544..46c9b536 100644 --- a/src/nxt_http_route.c +++ b/src/nxt_http_route.c @@ -46,20 +46,6 @@ typedef enum { } nxt_http_route_encoding_t; -typedef struct { - nxt_conf_value_t *pass; - nxt_conf_value_t *ret; - nxt_str_t location; - nxt_conf_value_t *proxy; - nxt_conf_value_t *share; - nxt_str_t chroot; - nxt_conf_value_t *follow_symlinks; - nxt_conf_value_t *traverse_mounts; - nxt_conf_value_t *types; - nxt_conf_value_t *fallback; -} nxt_http_route_action_conf_t; - - typedef struct { nxt_conf_value_t *host; nxt_conf_value_t *uri; @@ -199,7 +185,7 @@ static nxt_http_route_t *nxt_http_route_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *cv); static nxt_http_route_match_t *nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *cv); -static nxt_int_t nxt_http_route_action_create(nxt_task_t *task, +static nxt_int_t nxt_http_action_init(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *cv, nxt_http_action_t *action); static nxt_http_route_table_t *nxt_http_route_table_create(nxt_task_t *task, @@ -476,7 +462,7 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, return NULL; } - ret = nxt_http_route_action_create(task, tmcf, action_conf, &match->action); + ret = nxt_http_action_init(task, tmcf, action_conf, &match->action); if (nxt_slow_path(ret != NXT_OK)) { return NULL; } @@ -617,77 +603,76 @@ static nxt_conf_map_t nxt_http_route_action_conf[] = { { nxt_string("pass"), NXT_CONF_MAP_PTR, - offsetof(nxt_http_route_action_conf_t, pass) + offsetof(nxt_http_action_conf_t, pass) }, { nxt_string("return"), NXT_CONF_MAP_PTR, - offsetof(nxt_http_route_action_conf_t, ret) + offsetof(nxt_http_action_conf_t, ret) }, { nxt_string("location"), NXT_CONF_MAP_STR, - offsetof(nxt_http_route_action_conf_t, location) + offsetof(nxt_http_action_conf_t, location) }, { nxt_string("proxy"), NXT_CONF_MAP_PTR, - offsetof(nxt_http_route_action_conf_t, proxy) + offsetof(nxt_http_action_conf_t, proxy) }, { nxt_string("share"), NXT_CONF_MAP_PTR, - offsetof(nxt_http_route_action_conf_t, share) + offsetof(nxt_http_action_conf_t, share) }, { nxt_string("chroot"), NXT_CONF_MAP_STR, - offsetof(nxt_http_route_action_conf_t, chroot) + offsetof(nxt_http_action_conf_t, chroot) }, { nxt_string("follow_symlinks"), NXT_CONF_MAP_PTR, - offsetof(nxt_http_route_action_conf_t, follow_symlinks) + offsetof(nxt_http_action_conf_t, follow_symlinks) }, { nxt_string("traverse_mounts"), NXT_CONF_MAP_PTR, - offsetof(nxt_http_route_action_conf_t, traverse_mounts) + offsetof(nxt_http_action_conf_t, traverse_mounts) }, { nxt_string("types"), NXT_CONF_MAP_PTR, - offsetof(nxt_http_route_action_conf_t, types) + offsetof(nxt_http_action_conf_t, types) }, { nxt_string("fallback"), NXT_CONF_MAP_PTR, - offsetof(nxt_http_route_action_conf_t, fallback) + offsetof(nxt_http_action_conf_t, fallback) }, }; static nxt_int_t -nxt_http_route_action_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, +nxt_http_action_init(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *cv, nxt_http_action_t *action) { #if (NXT_HAVE_OPENAT2) - u_char *p; - uint8_t slash; - nxt_str_t *chroot; + u_char *p; + uint8_t slash; + nxt_str_t *chroot; #endif - nxt_mp_t *mp; - nxt_int_t ret; - nxt_str_t name, *string; - nxt_uint_t encode; - nxt_conf_value_t *conf; - nxt_http_route_rule_t *rule; - nxt_http_route_action_conf_t accf; + nxt_mp_t *mp; + nxt_int_t ret; + nxt_str_t name, *string; + nxt_conf_value_t *conf; + nxt_http_route_rule_t *rule; + nxt_http_action_conf_t acf; - nxt_memzero(&accf, sizeof(accf)); + nxt_memzero(&acf, sizeof(acf)); ret = nxt_conf_map_object(tmcf->mem_pool, cv, nxt_http_route_action_conf, - nxt_nitems(nxt_http_route_action_conf), &accf); + nxt_nitems(nxt_http_route_action_conf), &acf); if (ret != NXT_OK) { return ret; } @@ -696,47 +681,18 @@ nxt_http_route_action_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, mp = tmcf->router_conf->mem_pool; - if (accf.ret != NULL) { - action->handler = nxt_http_return_handler; - action->u.return_code = nxt_conf_get_number(accf.ret); - - if (accf.location.length > 0) { - if (nxt_is_complex_uri_encoded(accf.location.start, - accf.location.length)) - { - string = nxt_str_dup(mp, &action->name, &accf.location); - if (nxt_slow_path(string == NULL)) { - return NXT_ERROR; - } - - } else { - string = &action->name; - - encode = nxt_encode_complex_uri(NULL, accf.location.start, - accf.location.length); - string->length = accf.location.length + encode * 2; - - string->start = nxt_mp_nget(mp, string->length); - if (nxt_slow_path(string->start == NULL)) { - return NXT_ERROR; - } - - nxt_encode_complex_uri(string->start, accf.location.start, - accf.location.length); - } - } - - return NXT_OK; + if (acf.ret != NULL) { + return nxt_http_return_init(mp, action, &acf); } - if (accf.share != NULL) { - conf = accf.share; + if (acf.share != NULL) { + conf = acf.share; - } else if (accf.proxy != NULL) { - conf = accf.proxy; + } else if (acf.proxy != NULL) { + conf = acf.proxy; } else { - conf = accf.pass; + conf = acf.pass; } nxt_conf_get_string(conf, &name); @@ -746,11 +702,11 @@ nxt_http_route_action_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, return NXT_ERROR; } - if (accf.share != NULL) { + if (acf.share != NULL) { action->handler = nxt_http_static_handler; #if (NXT_HAVE_OPENAT2) - string = &accf.chroot; + string = &acf.chroot; chroot = &action->u.share.chroot; if (string->length > 0) { @@ -774,21 +730,21 @@ nxt_http_route_action_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, *p = '\0'; } - if (accf.follow_symlinks != NULL - && !nxt_conf_get_boolean(accf.follow_symlinks)) + if (acf.follow_symlinks != NULL + && !nxt_conf_get_boolean(acf.follow_symlinks)) { action->u.share.resolve |= RESOLVE_NO_SYMLINKS; } - if (accf.traverse_mounts != NULL - && !nxt_conf_get_boolean(accf.traverse_mounts)) + if (acf.traverse_mounts != NULL + && !nxt_conf_get_boolean(acf.traverse_mounts)) { action->u.share.resolve |= RESOLVE_NO_XDEV; } #endif - if (accf.types != NULL) { - rule = nxt_http_route_rule_create(task, mp, accf.types, 0, + if (acf.types != NULL) { + rule = nxt_http_route_rule_create(task, mp, acf.types, 0, NXT_HTTP_ROUTE_PATTERN_LOWCASE, NXT_HTTP_ROUTE_ENCODING_NONE); if (nxt_slow_path(rule == NULL)) { @@ -798,21 +754,21 @@ nxt_http_route_action_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, action->u.share.types = rule; } - if (accf.fallback != NULL) { + if (acf.fallback != NULL) { action->u.share.fallback = nxt_mp_alloc(mp, sizeof(nxt_http_action_t)); if (nxt_slow_path(action->u.share.fallback == NULL)) { return NXT_ERROR; } - return nxt_http_route_action_create(task, tmcf, accf.fallback, - action->u.share.fallback); + return nxt_http_action_init(task, tmcf, acf.fallback, + action->u.share.fallback); } return NXT_OK; } - if (accf.proxy != NULL) { + if (acf.proxy != NULL) { return nxt_http_proxy_create(mp, action); } -- cgit From cfba69781a18407d5c2020c4e3f3d4fc175a6127 Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Thu, 1 Jul 2021 13:56:40 +0300 Subject: Fixing multiple TLS-enabled listeners initialization. Because of the incorrect 'last' field assignment, multiple listeners with a TLS certificate did not initialize properly, which caused a router crash while establishing a connection. Test with multiple TLS listeners added. The issue was introduced in the c548e46fe516 commit. This closes #561 issue on GitHub. --- src/nxt_router.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/nxt_router.c b/src/nxt_router.c index 015ae226..26b846b0 100644 --- a/src/nxt_router.c +++ b/src/nxt_router.c @@ -124,7 +124,7 @@ static void nxt_router_tls_rpc_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg, void *data); static nxt_int_t nxt_router_conf_tls_insert(nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *value, nxt_socket_conf_t *skcf, - nxt_conf_value_t * conf_cmds); + nxt_conf_value_t * conf_cmds, nxt_bool_t last); #endif static void nxt_router_app_rpc_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_app_t *app); @@ -956,8 +956,6 @@ nxt_router_conf_apply(nxt_task_t *task, void *obj, void *data) tls = nxt_queue_link_data(qlk, nxt_router_tlssock_t, link); - tls->last = nxt_queue_is_empty(&tmcf->tls); - nxt_cert_store_get(task, &tls->name, tmcf->mem_pool, nxt_router_tls_rpc_handler, tls); return; @@ -1752,7 +1750,7 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_assert(value != NULL); ret = nxt_router_conf_tls_insert(tmcf, value, skcf, - conf_cmds); + conf_cmds, i == 0); if (nxt_slow_path(ret != NXT_OK)) { goto fail; } @@ -1761,7 +1759,7 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, } else { /* NXT_CONF_STRING */ ret = nxt_router_conf_tls_insert(tmcf, certificate, skcf, - conf_cmds); + conf_cmds, 1); if (nxt_slow_path(ret != NXT_OK)) { goto fail; } @@ -1856,7 +1854,7 @@ fail: static nxt_int_t nxt_router_conf_tls_insert(nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *value, nxt_socket_conf_t *skcf, - nxt_conf_value_t *conf_cmds) + nxt_conf_value_t *conf_cmds, nxt_bool_t last) { nxt_router_tlssock_t *tls; @@ -1868,6 +1866,7 @@ nxt_router_conf_tls_insert(nxt_router_temp_conf_t *tmcf, tls->socket_conf = skcf; tls->conf_cmds = conf_cmds; tls->temp_conf = tmcf; + tls->last = last; nxt_conf_get_string(value, &tls->name); nxt_queue_insert_tail(&tmcf->tls, &tls->link); -- cgit From 830729a6c5a59615d611575c22516b0fb9dcab3d Mon Sep 17 00:00:00 2001 From: Oisin Canty Date: Thu, 1 Jul 2021 11:16:43 +0000 Subject: Ruby: improved logging of exceptions without backtraces. If an exception was raised with a backtrace of zero length, the nxt_ruby_exception_log() routine would return without logging the exception class and message. This commit fixes the issue. --- src/ruby/nxt_ruby.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/ruby/nxt_ruby.c b/src/ruby/nxt_ruby.c index ca14af5b..10935528 100644 --- a/src/ruby/nxt_ruby.c +++ b/src/ruby/nxt_ruby.c @@ -1069,14 +1069,18 @@ nxt_ruby_exception_log(nxt_unit_request_info_t *req, uint32_t level, return; } + eclass = rb_class_name(rb_class_of(err)); + + msg = rb_funcall(err, rb_intern("message"), 0); ary = rb_funcall(err, rb_intern("backtrace"), 0); - if (nxt_slow_path(RARRAY_LEN(ary) == 0)) { + + if (RARRAY_LEN(ary) == 0) { + nxt_unit_req_log(req, level, "Ruby: %s (%s)", RSTRING_PTR(msg), + RSTRING_PTR(eclass)); + return; } - eclass = rb_class_name(rb_class_of(err)); - msg = rb_funcall(err, rb_intern("message"), 0); - nxt_unit_req_log(req, level, "Ruby: %s: %s (%s)", RSTRING_PTR(RARRAY_PTR(ary)[0]), RSTRING_PTR(msg), RSTRING_PTR(eclass)); -- cgit From 2ac9c627aa6aa736f3df9f426e741642694ac911 Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Thu, 1 Jul 2021 16:23:51 +0300 Subject: Fixing memory and descriptor leakage in case of port send failure. In rare cases, when the destination process had finished running but no notification of this was received yet, send could fail with an error, and the send message structure with file descriptors could leak. The leakage was periodically reproduced by respawn tests on FreeBSD 12. --- src/nxt_port_socket.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'src') diff --git a/src/nxt_port_socket.c b/src/nxt_port_socket.c index 3cf2e79a..843728a4 100644 --- a/src/nxt_port_socket.c +++ b/src/nxt_port_socket.c @@ -524,6 +524,24 @@ next_fragment: } else { if (nxt_slow_path(n == NXT_ERROR)) { + if (msg->link.next == NULL) { + if (msg->close_fd) { + if (msg->fd[0] != -1) { + nxt_fd_close(msg->fd[0]); + + msg->fd[0] = -1; + } + + if (msg->fd[1] != -1) { + nxt_fd_close(msg->fd[1]); + + msg->fd[1] = -1; + } + } + + nxt_port_release_send_msg(msg); + } + goto fail; } -- cgit From 54bf3e19122ca89ef79abe9cbdd2a039c7d6db3b Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Thu, 1 Jul 2021 16:23:56 +0300 Subject: Deduplicating code for closing fds in nxt_port_send_msg_t. --- src/nxt_port_socket.c | 64 ++++++++++++++++++++------------------------------- 1 file changed, 25 insertions(+), 39 deletions(-) (limited to 'src') diff --git a/src/nxt_port_socket.c b/src/nxt_port_socket.c index 843728a4..ba1b7081 100644 --- a/src/nxt_port_socket.c +++ b/src/nxt_port_socket.c @@ -21,6 +21,7 @@ static nxt_int_t nxt_port_msg_chk_insert(nxt_task_t *task, nxt_port_t *port, static nxt_port_send_msg_t *nxt_port_msg_alloc(nxt_port_send_msg_t *m); static void nxt_port_write_handler(nxt_task_t *task, void *obj, void *data); static nxt_port_send_msg_t *nxt_port_msg_first(nxt_port_t *port); +nxt_inline void nxt_port_msg_close_fd(nxt_port_send_msg_t *msg); static nxt_buf_t *nxt_port_buf_completion(nxt_task_t *task, nxt_work_queue_t *wq, nxt_buf_t *b, size_t sent, nxt_bool_t mmap_mode); static nxt_port_send_msg_t *nxt_port_msg_insert_tail(nxt_port_t *port, @@ -449,19 +450,7 @@ next_fragment: goto fail; } - if (msg->close_fd) { - if (msg->fd[0] != -1) { - nxt_fd_close(msg->fd[0]); - - msg->fd[0] = -1; - } - - if (msg->fd[1] != -1) { - nxt_fd_close(msg->fd[1]); - - msg->fd[1] = -1; - } - } + nxt_port_msg_close_fd(msg); msg->buf = nxt_port_buf_completion(task, wq, msg->buf, plain_size, m == NXT_PORT_METHOD_MMAP); @@ -525,19 +514,7 @@ next_fragment: } else { if (nxt_slow_path(n == NXT_ERROR)) { if (msg->link.next == NULL) { - if (msg->close_fd) { - if (msg->fd[0] != -1) { - nxt_fd_close(msg->fd[0]); - - msg->fd[0] = -1; - } - - if (msg->fd[1] != -1) { - nxt_fd_close(msg->fd[1]); - - msg->fd[1] = -1; - } - } + nxt_port_msg_close_fd(msg); nxt_port_release_send_msg(msg); } @@ -609,6 +586,27 @@ nxt_port_msg_first(nxt_port_t *port) } +nxt_inline void +nxt_port_msg_close_fd(nxt_port_send_msg_t *msg) +{ + if (!msg->close_fd) { + return; + } + + if (msg->fd[0] != -1) { + nxt_fd_close(msg->fd[0]); + + msg->fd[0] = -1; + } + + if (msg->fd[1] != -1) { + nxt_fd_close(msg->fd[1]); + + msg->fd[1] = -1; + } +} + + static nxt_buf_t * nxt_port_buf_completion(nxt_task_t *task, nxt_work_queue_t *wq, nxt_buf_t *b, size_t sent, nxt_bool_t mmap_mode) @@ -1333,19 +1331,7 @@ nxt_port_error_handler(nxt_task_t *task, void *obj, void *data) nxt_queue_each(msg, &port->messages, nxt_port_send_msg_t, link) { - if (msg->close_fd) { - if (msg->fd[0] != -1) { - nxt_fd_close(msg->fd[0]); - - msg->fd[0] = -1; - } - - if (msg->fd[1] != -1) { - nxt_fd_close(msg->fd[1]); - - msg->fd[1] = -1; - } - } + nxt_port_msg_close_fd(msg); for (b = msg->buf; b != NULL; b = next) { next = b->next; -- cgit From 7d2bc04e391f9216fb4e0464cb43c9c438f7e034 Mon Sep 17 00:00:00 2001 From: Oisin Canty Date: Fri, 2 Jul 2021 10:55:13 +0000 Subject: Fixing crash during IPv6 text address generation. When the textual representation of an IPv6 nxt_sockaddr_t was being generated, a crash would occur if the address had a full IPv6 form: f607:7403:1e4b:6c66:33b2:843f:2517:da27 This was caused by a variable that tracks the location of a collapsed group ("::") that was not set to a sane default. When the address was generated, a group would be inserted when it was not necessary, thus causing an overflow. This closes #481 issue on GitHub. --- src/nxt_sockaddr.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nxt_sockaddr.c b/src/nxt_sockaddr.c index af696a6b..47ee165f 100644 --- a/src/nxt_sockaddr.c +++ b/src/nxt_sockaddr.c @@ -525,9 +525,9 @@ nxt_inet6_ntop(u_char *addr, u_char *buf, u_char *end) return buf; } - zero_start = 8; + zero_start = 16; zero_groups = 0; - last_zero_start = 8; + last_zero_start = 16; last_zero_groups = 0; for (i = 0; i < 16; i += 2) { -- cgit From 655e321075c0beebe14eba83deeac1ba4c9e0b29 Mon Sep 17 00:00:00 2001 From: Oisin Canty Date: Fri, 2 Jul 2021 12:57:55 +0000 Subject: Ruby: process and thread lifecycle hooks. This feature allows one to specify blocks of code that are called when certain lifecycle events occur. A user configures a "hooks" property on the app configuration that points to a script. This script will be evaluated on boot and should contain blocks of code that will be called on specific events. An example of configuration: { "type": "ruby", "processes": 2, "threads": 2, "user": "vagrant", "group": "vagrant", "script": "config.ru", "hooks": "hooks.rb", "working_directory": "/home/vagrant/unit/rbhooks", "environment": { "GEM_HOME": "/home/vagrant/.ruby" } } An example of a valid "hooks.rb" file follows: File.write("./hooks.#{Process.pid}", "hooks evaluated") on_worker_boot do File.write("./worker_boot.#{Process.pid}", "worker booted") end on_thread_boot do File.write("./thread_boot.#{Process.pid}.#{Thread.current.object_id}", "thread booted") end on_thread_shutdown do File.write("./thread_shutdown.#{Process.pid}.#{Thread.current.object_id}", "thread shutdown") end on_worker_shutdown do File.write("./worker_shutdown.#{Process.pid}", "worker shutdown") end This closes issue #535 on GitHub. --- src/nxt_application.h | 1 + src/nxt_conf_validation.c | 3 + src/nxt_main_process.c | 5 ++ src/ruby/nxt_ruby.c | 143 +++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 151 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nxt_application.h b/src/nxt_application.h index 45e7fa48..6fbdc4be 100644 --- a/src/nxt_application.h +++ b/src/nxt_application.h @@ -74,6 +74,7 @@ typedef struct { typedef struct { nxt_str_t script; uint32_t threads; + nxt_str_t hooks; } nxt_ruby_app_conf_t; diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index 06ae2847..a16c955c 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -732,6 +732,9 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_ruby_members[] = { .name = nxt_string("threads"), .type = NXT_CONF_VLDT_INTEGER, .validator = nxt_conf_vldt_threads, + }, { + .name = nxt_string("hooks"), + .type = NXT_CONF_VLDT_STRING }, NXT_CONF_VLDT_NEXT(nxt_conf_vldt_common_members) diff --git a/src/nxt_main_process.c b/src/nxt_main_process.c index 00f336f6..10bd2518 100644 --- a/src/nxt_main_process.c +++ b/src/nxt_main_process.c @@ -271,6 +271,11 @@ static nxt_conf_map_t nxt_ruby_app_conf[] = { NXT_CONF_MAP_INT32, offsetof(nxt_common_app_conf_t, u.ruby.threads), }, + { + nxt_string("hooks"), + NXT_CONF_MAP_STR, + offsetof(nxt_common_app_conf_t, u.ruby.hooks), + } }; diff --git a/src/ruby/nxt_ruby.c b/src/ruby/nxt_ruby.c index 10935528..522869b5 100644 --- a/src/ruby/nxt_ruby.c +++ b/src/ruby/nxt_ruby.c @@ -29,6 +29,11 @@ typedef struct { static nxt_int_t nxt_ruby_start(nxt_task_t *task, nxt_process_data_t *data); static VALUE nxt_ruby_init_basic(VALUE arg); + +static VALUE nxt_ruby_hook_procs_load(VALUE path); +static VALUE nxt_ruby_hook_register(VALUE arg); +static VALUE nxt_ruby_hook_call(VALUE name); + static VALUE nxt_ruby_rack_init(nxt_ruby_rack_init_t *rack_init); static VALUE nxt_ruby_require_rubygems(VALUE arg); @@ -78,6 +83,7 @@ static uint32_t compat[] = { NXT_VERNUM, NXT_DEBUG, }; +static VALUE nxt_ruby_hook_procs; static VALUE nxt_ruby_rackup; static VALUE nxt_ruby_call; @@ -115,6 +121,10 @@ static VALUE nxt_rb_server_addr_str; static VALUE nxt_rb_server_name_str; static VALUE nxt_rb_server_port_str; static VALUE nxt_rb_server_protocol_str; +static VALUE nxt_rb_on_worker_boot; +static VALUE nxt_rb_on_worker_shutdown; +static VALUE nxt_rb_on_thread_boot; +static VALUE nxt_rb_on_thread_shutdown; static nxt_ruby_string_t nxt_rb_strings[] = { { nxt_string("80"), &nxt_rb_80_str }, @@ -132,6 +142,10 @@ static nxt_ruby_string_t nxt_rb_strings[] = { { nxt_string("SERVER_NAME"), &nxt_rb_server_name_str }, { nxt_string("SERVER_PORT"), &nxt_rb_server_port_str }, { nxt_string("SERVER_PROTOCOL"), &nxt_rb_server_protocol_str }, + { nxt_string("on_worker_boot"), &nxt_rb_on_worker_boot }, + { nxt_string("on_worker_shutdown"), &nxt_rb_on_worker_shutdown }, + { nxt_string("on_thread_boot"), &nxt_rb_on_thread_boot }, + { nxt_string("on_thread_shutdown"), &nxt_rb_on_thread_shutdown }, { nxt_null_string, NULL }, }; @@ -183,11 +197,70 @@ nxt_ruby_done_strings(void) } +static VALUE +nxt_ruby_hook_procs_load(VALUE path) +{ + VALUE module, file, file_obj; + + module = rb_define_module("Unit"); + + nxt_ruby_hook_procs = rb_hash_new(); + + rb_gc_register_address(&nxt_ruby_hook_procs); + + rb_define_module_function(module, "on_worker_boot", + &nxt_ruby_hook_register, 0); + rb_define_module_function(module, "on_worker_shutdown", + &nxt_ruby_hook_register, 0); + rb_define_module_function(module, "on_thread_boot", + &nxt_ruby_hook_register, 0); + rb_define_module_function(module, "on_thread_shutdown", + &nxt_ruby_hook_register, 0); + + file = rb_const_get(rb_cObject, rb_intern("File")); + file_obj = rb_funcall(file, rb_intern("read"), 1, path); + + return rb_funcall(module, rb_intern("module_eval"), 3, file_obj, path, + INT2NUM(1)); +} + + +static VALUE +nxt_ruby_hook_register(VALUE arg) +{ + VALUE kernel, callee, callee_str; + + rb_need_block(); + + kernel = rb_const_get(rb_cObject, rb_intern("Kernel")); + callee = rb_funcall(kernel, rb_intern("__callee__"), 0); + callee_str = rb_funcall(callee, rb_intern("to_s"), 0); + + rb_hash_aset(nxt_ruby_hook_procs, callee_str, rb_block_proc()); + + return Qnil; +} + + +static VALUE +nxt_ruby_hook_call(VALUE name) +{ + VALUE proc; + + proc = rb_hash_lookup(nxt_ruby_hook_procs, name); + if (proc == Qnil) { + return Qnil; + } + + return rb_funcall(proc, rb_intern("call"), 0); +} + + static nxt_int_t nxt_ruby_start(nxt_task_t *task, nxt_process_data_t *data) { int state, rc; - VALUE res; + VALUE res, path; nxt_ruby_ctx_t ruby_ctx; nxt_unit_ctx_t *unit_ctx; nxt_unit_init_t ruby_unit_init; @@ -231,6 +304,29 @@ nxt_ruby_start(nxt_task_t *task, nxt_process_data_t *data) } nxt_ruby_call = Qnil; + nxt_ruby_hook_procs = Qnil; + + if (c->hooks.start != NULL) { + path = rb_str_new((const char *) c->hooks.start, + (long) c->hooks.length); + + rb_protect(nxt_ruby_hook_procs_load, path, &state); + rb_str_free(path); + if (nxt_slow_path(state != 0)) { + nxt_ruby_exception_log(NULL, NXT_LOG_ALERT, + "Failed to setup hooks"); + return NXT_ERROR; + } + } + + if (nxt_ruby_hook_procs != Qnil) { + rb_protect(nxt_ruby_hook_call, nxt_rb_on_worker_boot, &state); + if (nxt_slow_path(state != 0)) { + nxt_ruby_exception_log(NULL, NXT_LOG_ERR, + "Failed to call on_worker_boot()"); + return NXT_ERROR; + } + } nxt_ruby_rackup = nxt_ruby_rack_init(&rack_init); if (nxt_slow_path(nxt_ruby_rackup == Qnil)) { @@ -274,11 +370,35 @@ nxt_ruby_start(nxt_task_t *task, nxt_process_data_t *data) goto fail; } + if (nxt_ruby_hook_procs != Qnil) { + rb_protect(nxt_ruby_hook_call, nxt_rb_on_thread_boot, &state); + if (nxt_slow_path(state != 0)) { + nxt_ruby_exception_log(NULL, NXT_LOG_ERR, + "Failed to call on_thread_boot()"); + } + } + rc = (intptr_t) rb_thread_call_without_gvl(nxt_ruby_unit_run, unit_ctx, nxt_ruby_ubf, unit_ctx); + if (nxt_ruby_hook_procs != Qnil) { + rb_protect(nxt_ruby_hook_call, nxt_rb_on_thread_shutdown, &state); + if (nxt_slow_path(state != 0)) { + nxt_ruby_exception_log(NULL, NXT_LOG_ERR, + "Failed to call on_thread_shutdown()"); + } + } + nxt_ruby_join_threads(unit_ctx, c); + if (nxt_ruby_hook_procs != Qnil) { + rb_protect(nxt_ruby_hook_call, nxt_rb_on_worker_shutdown, &state); + if (nxt_slow_path(state != 0)) { + nxt_ruby_exception_log(NULL, NXT_LOG_ERR, + "Failed to call on_worker_shutdown()"); + } + } + nxt_unit_done(unit_ctx); nxt_ruby_ctx_done(&ruby_ctx); @@ -1120,6 +1240,10 @@ nxt_ruby_atexit(void) rb_gc_unregister_address(&nxt_ruby_call); } + if (nxt_ruby_hook_procs != Qnil) { + rb_gc_unregister_address(&nxt_ruby_hook_procs); + } + nxt_ruby_done_strings(); ruby_cleanup(0); @@ -1182,6 +1306,7 @@ nxt_ruby_thread_create_gvl(void *rctx) static VALUE nxt_ruby_thread_func(VALUE arg) { + int state; nxt_unit_ctx_t *ctx; nxt_ruby_ctx_t *rctx; @@ -1194,9 +1319,25 @@ nxt_ruby_thread_func(VALUE arg) goto fail; } + if (nxt_ruby_hook_procs != Qnil) { + rb_protect(nxt_ruby_hook_call, nxt_rb_on_thread_boot, &state); + if (nxt_slow_path(state != 0)) { + nxt_ruby_exception_log(NULL, NXT_LOG_ERR, + "Failed to call on_thread_boot()"); + } + } + (void) rb_thread_call_without_gvl(nxt_ruby_unit_run, ctx, nxt_ruby_ubf, ctx); + if (nxt_ruby_hook_procs != Qnil) { + rb_protect(nxt_ruby_hook_call, nxt_rb_on_thread_shutdown, &state); + if (nxt_slow_path(state != 0)) { + nxt_ruby_exception_log(NULL, NXT_LOG_ERR, + "Failed to call on_thread_shutdown()"); + } + } + nxt_unit_done(ctx); fail: -- cgit From daa051e7e7266325ef38a606b3aee4377a73f0d0 Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Mon, 19 Jul 2021 16:23:13 +0300 Subject: Router: fixing assertion on app thread port handle. A new application thread port message can be processed in the router after the application is removed from the router. Assertion for this case is replaced by a condition to store the new thread port until receiving the stop notification from the application process. --- src/nxt_router.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/nxt_router.c b/src/nxt_router.c index 26b846b0..409f88a1 100644 --- a/src/nxt_router.c +++ b/src/nxt_router.c @@ -680,18 +680,20 @@ nxt_router_new_port_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) nxt_assert(main_app_port != NULL); app = main_app_port->app; - nxt_assert(app != NULL); - nxt_thread_mutex_lock(&app->mutex); + if (nxt_fast_path(app != NULL)) { + nxt_thread_mutex_lock(&app->mutex); - /* TODO here should be find-and-add code because there can be - port waiters in port_hash */ - nxt_port_hash_add(&app->port_hash, port); - app->port_hash_count++; + /* TODO here should be find-and-add code because there can be + port waiters in port_hash */ + nxt_port_hash_add(&app->port_hash, port); + app->port_hash_count++; - nxt_thread_mutex_unlock(&app->mutex); + nxt_thread_mutex_unlock(&app->mutex); + + port->app = app; + } - port->app = app; port->main_app_port = main_app_port; nxt_port_socket_write(task, port, NXT_PORT_MSG_PORT_ACK, -1, 0, 0, NULL); -- cgit From 567545213d95e608b54ce92bfc33fac4327a9f93 Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Tue, 20 Jul 2021 10:37:50 +0300 Subject: Python: fixing ASGI receive() issues. The receive() call never blocks for a GET request and always returns the same empty body message. The Starlette framework creates a separate task when receive() is called in a loop until an 'http.disconnect' message is received. The 'http.disconnect' message was previously issued after the response header had been sent. However, the correct behavior is to respond with 'http.disconnect' after sending the response is complete. This closes #564 issue on GitHub. --- src/python/nxt_python_asgi_http.c | 87 ++++++++++++++++++++++++--------------- 1 file changed, 54 insertions(+), 33 deletions(-) (limited to 'src') diff --git a/src/python/nxt_python_asgi_http.c b/src/python/nxt_python_asgi_http.c index d88c4b00..3074c09f 100644 --- a/src/python/nxt_python_asgi_http.c +++ b/src/python/nxt_python_asgi_http.c @@ -23,10 +23,11 @@ typedef struct { PyObject *send_future; uint64_t content_length; uint64_t bytes_sent; - int complete; - int closed; PyObject *send_body; Py_ssize_t send_body_off; + uint8_t complete; + uint8_t closed; + uint8_t empty_body_received; } nxt_py_asgi_http_t; @@ -37,6 +38,7 @@ static PyObject *nxt_py_asgi_http_response_start(nxt_py_asgi_http_t *http, PyObject *dict); static PyObject *nxt_py_asgi_http_response_body(nxt_py_asgi_http_t *http, PyObject *dict); +static void nxt_py_asgi_http_emit_disconnect(nxt_py_asgi_http_t *http); static PyObject *nxt_py_asgi_http_done(PyObject *self, PyObject *future); @@ -94,10 +96,11 @@ nxt_py_asgi_http_create(nxt_unit_request_info_t *req) http->send_future = NULL; http->content_length = -1; http->bytes_sent = 0; - http->complete = 0; - http->closed = 0; http->send_body = NULL; http->send_body_off = 0; + http->complete = 0; + http->closed = 0; + http->empty_body_received = 0; } return (PyObject *) http; @@ -117,7 +120,7 @@ nxt_py_asgi_http_receive(PyObject *self, PyObject *none) nxt_unit_req_debug(req, "asgi_http_receive"); - if (nxt_slow_path(http->closed || nxt_unit_response_is_sent(req))) { + if (nxt_slow_path(http->closed || http->complete )) { msg = nxt_py_asgi_new_msg(req, nxt_py_http_disconnect_str); } else { @@ -171,6 +174,14 @@ nxt_py_asgi_http_read_msg(nxt_py_asgi_http_t *http) size = nxt_py_asgi_http_body_buf_size; } + if (size == 0) { + if (http->empty_body_received) { + Py_RETURN_NONE; + } + + http->empty_body_received = 1; + } + if (size > 0) { body = PyBytes_FromStringAndSize(NULL, size); if (nxt_slow_path(body == NULL)) { @@ -442,6 +453,8 @@ nxt_py_asgi_http_response_body(nxt_py_asgi_http_t *http, PyObject *dict) if (more_body == NULL || more_body == Py_False) { http->complete = 1; + + nxt_py_asgi_http_emit_disconnect(http); } Py_INCREF(http); @@ -449,6 +462,41 @@ nxt_py_asgi_http_response_body(nxt_py_asgi_http_t *http, PyObject *dict) } +static void +nxt_py_asgi_http_emit_disconnect(nxt_py_asgi_http_t *http) +{ + PyObject *msg, *future, *res; + + if (http->receive_future == NULL) { + return; + } + + msg = nxt_py_asgi_new_msg(http->req, nxt_py_http_disconnect_str); + if (nxt_slow_path(msg == NULL)) { + return; + } + + if (msg == Py_None) { + Py_DECREF(msg); + return; + } + + future = http->receive_future; + http->receive_future = NULL; + + res = PyObject_CallMethodObjArgs(future, nxt_py_set_result_str, msg, NULL); + if (nxt_slow_path(res == NULL)) { + nxt_unit_req_alert(http->req, "'set_result' call failed"); + nxt_python_print_exception(); + } + + Py_XDECREF(res); + Py_DECREF(future); + + Py_DECREF(msg); +} + + void nxt_py_asgi_http_data_handler(nxt_unit_request_info_t *req) { @@ -573,7 +621,6 @@ fail: void nxt_py_asgi_http_close_handler(nxt_unit_request_info_t *req) { - PyObject *msg, *future, *res; nxt_py_asgi_http_t *http; http = req->data; @@ -582,33 +629,7 @@ nxt_py_asgi_http_close_handler(nxt_unit_request_info_t *req) http->closed = 1; - if (http->receive_future == NULL) { - return; - } - - msg = nxt_py_asgi_new_msg(req, nxt_py_http_disconnect_str); - if (nxt_slow_path(msg == NULL)) { - return; - } - - if (msg == Py_None) { - Py_DECREF(msg); - return; - } - - future = http->receive_future; - http->receive_future = NULL; - - res = PyObject_CallMethodObjArgs(future, nxt_py_set_result_str, msg, NULL); - if (nxt_slow_path(res == NULL)) { - nxt_unit_req_alert(req, "'set_result' call failed"); - nxt_python_print_exception(); - } - - Py_XDECREF(res); - Py_DECREF(future); - - Py_DECREF(msg); + nxt_py_asgi_http_emit_disconnect(http); } -- cgit From dfbdc1c11a201e46d61f4bc61cfbe5741fc4fd70 Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Tue, 20 Jul 2021 10:37:53 +0300 Subject: Python: fixing exceptions in Future.set_result for ASGI implementation. An ASGI application can cancel the Future object returned by the receive() call. In this case, Unit's ASGI implementation should not call set_result() because the Future is already handled. In particular, the Starlette framework was noted to cancel the received Future. This patch adds a done() check for the Future before attempting a set_result(). This is related to #564 issue on GitHub. --- src/python/nxt_python_asgi_http.c | 55 +++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/python/nxt_python_asgi_http.c b/src/python/nxt_python_asgi_http.c index 3074c09f..c4a77d53 100644 --- a/src/python/nxt_python_asgi_http.c +++ b/src/python/nxt_python_asgi_http.c @@ -39,6 +39,8 @@ static PyObject *nxt_py_asgi_http_response_start(nxt_py_asgi_http_t *http, static PyObject *nxt_py_asgi_http_response_body(nxt_py_asgi_http_t *http, PyObject *dict); static void nxt_py_asgi_http_emit_disconnect(nxt_py_asgi_http_t *http); +static void nxt_py_asgi_http_set_result(nxt_py_asgi_http_t *http, + PyObject *future, PyObject *msg); static PyObject *nxt_py_asgi_http_done(PyObject *self, PyObject *future); @@ -465,7 +467,7 @@ nxt_py_asgi_http_response_body(nxt_py_asgi_http_t *http, PyObject *dict) static void nxt_py_asgi_http_emit_disconnect(nxt_py_asgi_http_t *http) { - PyObject *msg, *future, *res; + PyObject *msg, *future; if (http->receive_future == NULL) { return; @@ -484,23 +486,45 @@ nxt_py_asgi_http_emit_disconnect(nxt_py_asgi_http_t *http) future = http->receive_future; http->receive_future = NULL; - res = PyObject_CallMethodObjArgs(future, nxt_py_set_result_str, msg, NULL); + nxt_py_asgi_http_set_result(http, future, msg); + + Py_DECREF(msg); +} + + +static void +nxt_py_asgi_http_set_result(nxt_py_asgi_http_t *http, PyObject *future, + PyObject *msg) +{ + PyObject *res; + + res = PyObject_CallMethodObjArgs(future, nxt_py_done_str, NULL); if (nxt_slow_path(res == NULL)) { - nxt_unit_req_alert(http->req, "'set_result' call failed"); + nxt_unit_req_alert(http->req, "'done' call failed"); nxt_python_print_exception(); } + if (nxt_fast_path(res == Py_False)) { + res = PyObject_CallMethodObjArgs(future, nxt_py_set_result_str, msg, + NULL); + if (nxt_slow_path(res == NULL)) { + nxt_unit_req_alert(http->req, "'set_result' call failed"); + nxt_python_print_exception(); + } + + } else { + res = NULL; + } + Py_XDECREF(res); Py_DECREF(future); - - Py_DECREF(msg); } void nxt_py_asgi_http_data_handler(nxt_unit_request_info_t *req) { - PyObject *msg, *future, *res; + PyObject *msg, *future; nxt_py_asgi_http_t *http; http = req->data; @@ -524,14 +548,7 @@ nxt_py_asgi_http_data_handler(nxt_unit_request_info_t *req) future = http->receive_future; http->receive_future = NULL; - res = PyObject_CallMethodObjArgs(future, nxt_py_set_result_str, msg, NULL); - if (nxt_slow_path(res == NULL)) { - nxt_unit_req_alert(req, "'set_result' call failed"); - nxt_python_print_exception(); - } - - Py_XDECREF(res); - Py_DECREF(future); + nxt_py_asgi_http_set_result(http, future, msg); Py_DECREF(msg); } @@ -575,15 +592,7 @@ nxt_py_asgi_http_drain(nxt_queue_link_t *lnk) future = http->send_future; http->send_future = NULL; - res = PyObject_CallMethodObjArgs(future, nxt_py_set_result_str, Py_None, - NULL); - if (nxt_slow_path(res == NULL)) { - nxt_unit_req_alert(http->req, "'set_result' call failed"); - nxt_python_print_exception(); - } - - Py_XDECREF(res); - Py_DECREF(future); + nxt_py_asgi_http_set_result(http, future, Py_None); return NXT_UNIT_OK; -- cgit From f27fbd9b4d2bdaddf1e7001d0d0bc5586ba04cd4 Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Tue, 20 Jul 2021 10:37:54 +0300 Subject: Python: using default event_loop for main thread for ASGI. Unit's ASGI implementation creates a new event loop to run an application for each thread since 542b5b8c0647. This may cause unexpected exceptions or strange bugs if asyncio synchronisation primitives are initialised before the application starts (e.g. globally). Although the approach with a new event loop for the main thread is consistent and helps to prepare the application to run in multiple threads, it can be a source of pain for people who just want to run single-threaded ASGI applications in Unit. This is related to #560 issue on GitHub. --- src/python/nxt_python.c | 4 ++-- src/python/nxt_python.h | 2 +- src/python/nxt_python_asgi.c | 28 +++++++++++++++++----------- src/python/nxt_python_wsgi.c | 4 ++-- 4 files changed, 22 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/python/nxt_python.c b/src/python/nxt_python.c index 588a147a..bdce68b2 100644 --- a/src/python/nxt_python.c +++ b/src/python/nxt_python.c @@ -264,7 +264,7 @@ nxt_python_start(nxt_task_t *task, nxt_process_data_t *data) goto fail; } - rc = nxt_py_proto.ctx_data_alloc(&python_init.ctx_data); + rc = nxt_py_proto.ctx_data_alloc(&python_init.ctx_data, 1); if (nxt_slow_path(rc != NXT_UNIT_OK)) { goto fail; } @@ -504,7 +504,7 @@ nxt_python_init_threads(nxt_python_app_conf_t *c) for (i = 0; i < c->threads - 1; i++) { ti = &nxt_py_threads[i]; - res = nxt_py_proto.ctx_data_alloc(&ti->ctx_data); + res = nxt_py_proto.ctx_data_alloc(&ti->ctx_data, 0); if (nxt_slow_path(res != NXT_UNIT_OK)) { return NXT_UNIT_ERROR; } diff --git a/src/python/nxt_python.h b/src/python/nxt_python.h index a5c1d9a6..e4eac9dc 100644 --- a/src/python/nxt_python.h +++ b/src/python/nxt_python.h @@ -60,7 +60,7 @@ typedef struct { typedef struct { - int (*ctx_data_alloc)(void **pdata); + int (*ctx_data_alloc)(void **pdata, int main); void (*ctx_data_free)(void *data); int (*startup)(void *data); int (*run)(nxt_unit_ctx_t *ctx); diff --git a/src/python/nxt_python_asgi.c b/src/python/nxt_python_asgi.c index 1d220678..26003805 100644 --- a/src/python/nxt_python_asgi.c +++ b/src/python/nxt_python_asgi.c @@ -17,7 +17,7 @@ static PyObject *nxt_python_asgi_get_func(PyObject *obj); -static int nxt_python_asgi_ctx_data_alloc(void **pdata); +static int nxt_python_asgi_ctx_data_alloc(void **pdata, int main); static void nxt_python_asgi_ctx_data_free(void *data); static int nxt_python_asgi_startup(void *data); static int nxt_python_asgi_run(nxt_unit_ctx_t *ctx); @@ -194,10 +194,11 @@ nxt_python_asgi_init(nxt_unit_init_t *init, nxt_python_proto_t *proto) static int -nxt_python_asgi_ctx_data_alloc(void **pdata) +nxt_python_asgi_ctx_data_alloc(void **pdata, int main) { uint32_t i; - PyObject *asyncio, *loop, *new_event_loop, *obj; + PyObject *asyncio, *loop, *event_loop, *obj; + const char *event_loop_func; nxt_py_asgi_ctx_data_t *ctx_data; ctx_data = nxt_unit_malloc(NULL, sizeof(nxt_py_asgi_ctx_data_t)); @@ -232,23 +233,28 @@ nxt_python_asgi_ctx_data_alloc(void **pdata) goto fail; } - new_event_loop = PyDict_GetItemString(PyModule_GetDict(asyncio), - "new_event_loop"); - if (nxt_slow_path(new_event_loop == NULL)) { + event_loop_func = main ? "get_event_loop" : "new_event_loop"; + + event_loop = PyDict_GetItemString(PyModule_GetDict(asyncio), + event_loop_func); + if (nxt_slow_path(event_loop == NULL)) { nxt_unit_alert(NULL, - "Python failed to get 'new_event_loop' from module 'asyncio'"); + "Python failed to get '%s' from module 'asyncio'", + event_loop_func); goto fail; } - if (nxt_slow_path(PyCallable_Check(new_event_loop) == 0)) { + if (nxt_slow_path(PyCallable_Check(event_loop) == 0)) { nxt_unit_alert(NULL, - "'asyncio.new_event_loop' is not a callable object"); + "'asyncio.%s' is not a callable object", + event_loop_func); goto fail; } - loop = PyObject_CallObject(new_event_loop, NULL); + loop = PyObject_CallObject(event_loop, NULL); if (nxt_slow_path(loop == NULL)) { - nxt_unit_alert(NULL, "Python failed to call 'asyncio.new_event_loop'"); + nxt_unit_alert(NULL, "Python failed to call 'asyncio.%s'", + event_loop_func); goto fail; } diff --git a/src/python/nxt_python_wsgi.c b/src/python/nxt_python_wsgi.c index b80d10fa..87dcfaa2 100644 --- a/src/python/nxt_python_wsgi.c +++ b/src/python/nxt_python_wsgi.c @@ -51,7 +51,7 @@ typedef struct { } nxt_python_ctx_t; -static int nxt_python_wsgi_ctx_data_alloc(void **pdata); +static int nxt_python_wsgi_ctx_data_alloc(void **pdata, int main); static void nxt_python_wsgi_ctx_data_free(void *data); static int nxt_python_wsgi_run(nxt_unit_ctx_t *ctx); static void nxt_python_wsgi_done(void); @@ -210,7 +210,7 @@ fail: static int -nxt_python_wsgi_ctx_data_alloc(void **pdata) +nxt_python_wsgi_ctx_data_alloc(void **pdata, int main) { nxt_python_ctx_t *pctx; -- cgit From c37ff7ed0ed06b0e928efdb217a8999ff3ff7f50 Mon Sep 17 00:00:00 2001 From: Andrey Suvorov Date: Wed, 21 Jul 2021 15:22:52 -0700 Subject: Enabling configure TLS sessions. To support TLS sessions, Unit uses the OpenSSL built-in session cache; the cache_size option defines the number sessions to store. To disable the feather, the option must be zero. --- src/nxt_conf_validation.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++ src/nxt_openssl.c | 36 +++++++++++++++++++++++------ src/nxt_router.c | 48 ++++++++++++++++++++++++++++---------- src/nxt_tls.h | 15 +++++++++--- 4 files changed, 136 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index a16c955c..fd57a983 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -95,6 +95,10 @@ static nxt_int_t nxt_conf_vldt_object_conf_commands(nxt_conf_validation_t *vldt, #endif static nxt_int_t nxt_conf_vldt_certificate_element(nxt_conf_validation_t *vldt, nxt_conf_value_t *value); +static nxt_int_t nxt_conf_vldt_tls_cache_size(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value, void *data); +static nxt_int_t nxt_conf_vldt_tls_timeout(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value, void *data); #endif static nxt_int_t nxt_conf_vldt_action(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data); @@ -206,6 +210,7 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_websocket_members[]; static nxt_conf_vldt_object_t nxt_conf_vldt_static_members[]; #if (NXT_TLS) static nxt_conf_vldt_object_t nxt_conf_vldt_tls_members[]; +static nxt_conf_vldt_object_t nxt_conf_vldt_session_members[]; #endif static nxt_conf_vldt_object_t nxt_conf_vldt_match_members[]; static nxt_conf_vldt_object_t nxt_conf_vldt_python_target_members[]; @@ -378,11 +383,65 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_tls_members[] = { .validator = nxt_conf_vldt_unsupported, .u.string = "conf_commands", #endif + }, { + .name = nxt_string("session"), + .type = NXT_CONF_VLDT_OBJECT, + .validator = nxt_conf_vldt_object, + .u.members = nxt_conf_vldt_session_members, }, NXT_CONF_VLDT_END }; + +static nxt_conf_vldt_object_t nxt_conf_vldt_session_members[] = { + { + .name = nxt_string("cache_size"), + .type = NXT_CONF_VLDT_INTEGER, + .validator = nxt_conf_vldt_tls_cache_size, + }, { + .name = nxt_string("timeout"), + .type = NXT_CONF_VLDT_INTEGER, + .validator = nxt_conf_vldt_tls_timeout, + }, + + NXT_CONF_VLDT_END +}; + + +static nxt_int_t +nxt_conf_vldt_tls_cache_size(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value, void *data) +{ + int64_t cache_size; + + cache_size = nxt_conf_get_number(value); + + if (cache_size < 0) { + return nxt_conf_vldt_error(vldt, "The \"cache_size\" number must not " + "be negative."); + } + + return NXT_OK; +} + + +static nxt_int_t +nxt_conf_vldt_tls_timeout(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, + void *data) +{ + int64_t timeout; + + timeout = nxt_conf_get_number(value); + + if (timeout <= 0) { + return nxt_conf_vldt_error(vldt, "The \"timeout\" number must be " + "greater than zero."); + } + + return NXT_OK; +} + #endif diff --git a/src/nxt_openssl.c b/src/nxt_openssl.c index 2fd5d1bf..3b5d4fda 100644 --- a/src/nxt_openssl.c +++ b/src/nxt_openssl.c @@ -42,15 +42,16 @@ static void nxt_openssl_lock(int mode, int type, const char *file, int line); static unsigned long nxt_openssl_thread_id(void); static void nxt_openssl_locks_free(void); #endif -static nxt_int_t nxt_openssl_server_init(nxt_task_t *task, - nxt_tls_conf_t *conf, nxt_mp_t *mp, nxt_conf_value_t *conf_cmds, - nxt_bool_t last); +static nxt_int_t nxt_openssl_server_init(nxt_task_t *task, nxt_mp_t *mp, + nxt_tls_init_t *tls_init, nxt_bool_t last); static nxt_int_t nxt_openssl_chain_file(nxt_task_t *task, SSL_CTX *ctx, nxt_tls_conf_t *conf, nxt_mp_t *mp, nxt_bool_t single); #if (NXT_HAVE_OPENSSL_CONF_CMD) static nxt_int_t nxt_ssl_conf_commands(nxt_task_t *task, SSL_CTX *ctx, nxt_conf_value_t *value, nxt_mp_t *mp); #endif +static void nxt_ssl_session_cache(SSL_CTX *ctx, size_t cache_size, + time_t timeout); static nxt_uint_t nxt_openssl_cert_get_names(nxt_task_t *task, X509 *cert, nxt_tls_conf_t *conf, nxt_mp_t *mp); static nxt_int_t nxt_openssl_bundle_hash_test(nxt_lvlhsh_query_t *lhq, @@ -265,11 +266,12 @@ nxt_openssl_locks_free(void) static nxt_int_t -nxt_openssl_server_init(nxt_task_t *task, nxt_tls_conf_t *conf, - nxt_mp_t *mp, nxt_conf_value_t *conf_cmds, nxt_bool_t last) +nxt_openssl_server_init(nxt_task_t *task, nxt_mp_t *mp, + nxt_tls_init_t *tls_init, nxt_bool_t last) { SSL_CTX *ctx; const char *ciphers, *ca_certificate; + nxt_tls_conf_t *conf; STACK_OF(X509_NAME) *list; nxt_tls_bundle_conf_t *bundle; @@ -279,6 +281,8 @@ nxt_openssl_server_init(nxt_task_t *task, nxt_tls_conf_t *conf, return NXT_ERROR; } + conf = tls_init->conf; + bundle = conf->bundle; nxt_assert(bundle != NULL); @@ -337,13 +341,15 @@ nxt_openssl_server_init(nxt_task_t *task, nxt_tls_conf_t *conf, } #if (NXT_HAVE_OPENSSL_CONF_CMD) - if (conf_cmds != NULL - && nxt_ssl_conf_commands(task, ctx, conf_cmds, mp) != NXT_OK) + if (tls_init->conf_cmds != NULL + && nxt_ssl_conf_commands(task, ctx, tls_init->conf_cmds, mp) != NXT_OK) { goto fail; } #endif + nxt_ssl_session_cache(ctx, tls_init->cache_size, tls_init->timeout); + SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); if (conf->ca_certificate != NULL) { @@ -582,6 +588,22 @@ fail: #endif +static void +nxt_ssl_session_cache(SSL_CTX *ctx, size_t cache_size, time_t timeout) +{ + if (cache_size == 0) { + SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF); + return; + } + + SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_SERVER); + + SSL_CTX_sess_set_cache_size(ctx, cache_size); + + SSL_CTX_set_timeout(ctx, (long) timeout); +} + + static nxt_uint_t nxt_openssl_cert_get_names(nxt_task_t *task, X509 *cert, nxt_tls_conf_t *conf, nxt_mp_t *mp) diff --git a/src/nxt_router.c b/src/nxt_router.c index 409f88a1..1156edb8 100644 --- a/src/nxt_router.c +++ b/src/nxt_router.c @@ -44,10 +44,10 @@ typedef struct { nxt_str_t name; nxt_socket_conf_t *socket_conf; nxt_router_temp_conf_t *temp_conf; - nxt_conf_value_t *conf_cmds; + nxt_tls_init_t *tls_init; nxt_bool_t last; - nxt_queue_link_t link; /* for nxt_socket_conf_t.tls */ + nxt_queue_link_t link; /* for nxt_socket_conf_t.tls */ } nxt_router_tlssock_t; #endif @@ -123,8 +123,8 @@ static void nxt_router_listen_socket_error(nxt_task_t *task, static void nxt_router_tls_rpc_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg, void *data); static nxt_int_t nxt_router_conf_tls_insert(nxt_router_temp_conf_t *tmcf, - nxt_conf_value_t *value, nxt_socket_conf_t *skcf, - nxt_conf_value_t * conf_cmds, nxt_bool_t last); + nxt_conf_value_t *value, nxt_socket_conf_t *skcf, nxt_tls_init_t *tls_init, + nxt_bool_t last); #endif static void nxt_router_app_rpc_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_app_t *app); @@ -1341,7 +1341,8 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_router_t *router; nxt_app_joint_t *app_joint; #if (NXT_TLS) - nxt_conf_value_t *certificate, *conf_cmds; + nxt_tls_init_t *tls_init; + nxt_conf_value_t *certificate; #endif nxt_conf_value_t *conf, *http, *value, *websocket; nxt_conf_value_t *applications, *application; @@ -1363,6 +1364,8 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, #if (NXT_TLS) static nxt_str_t certificate_path = nxt_string("/tls/certificate"); static nxt_str_t conf_commands_path = nxt_string("/tls/conf_commands"); + static nxt_str_t conf_cache_path = nxt_string("/tls/session/cache_size"); + static nxt_str_t conf_timeout_path = nxt_string("/tls/session/timeout"); #endif static nxt_str_t static_path = nxt_string("/settings/http/static"); static nxt_str_t websocket_path = nxt_string("/settings/http/websocket"); @@ -1741,7 +1744,26 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, certificate = nxt_conf_get_path(listener, &certificate_path); if (certificate != NULL) { - conf_cmds = nxt_conf_get_path(listener, &conf_commands_path); + tls_init = nxt_mp_get(tmcf->mem_pool, sizeof(nxt_tls_init_t)); + if (nxt_slow_path(tls_init == NULL)) { + return NXT_ERROR; + } + + tls_init->cache_size = 0; + tls_init->timeout = 300; + + value = nxt_conf_get_path(listener, &conf_cache_path); + if (value != NULL) { + tls_init->cache_size = nxt_conf_get_number(value); + } + + value = nxt_conf_get_path(listener, &conf_timeout_path); + if (value != NULL) { + tls_init->timeout = nxt_conf_get_number(value); + } + + tls_init->conf_cmds = nxt_conf_get_path(listener, + &conf_commands_path); if (nxt_conf_type(certificate) == NXT_CONF_ARRAY) { n = nxt_conf_array_elements_count(certificate); @@ -1752,7 +1774,7 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_assert(value != NULL); ret = nxt_router_conf_tls_insert(tmcf, value, skcf, - conf_cmds, i == 0); + tls_init, i == 0); if (nxt_slow_path(ret != NXT_OK)) { goto fail; } @@ -1761,7 +1783,7 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, } else { /* NXT_CONF_STRING */ ret = nxt_router_conf_tls_insert(tmcf, certificate, skcf, - conf_cmds, 1); + tls_init, 1); if (nxt_slow_path(ret != NXT_OK)) { goto fail; } @@ -1856,7 +1878,7 @@ fail: static nxt_int_t nxt_router_conf_tls_insert(nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *value, nxt_socket_conf_t *skcf, - nxt_conf_value_t *conf_cmds, nxt_bool_t last) + nxt_tls_init_t *tls_init, nxt_bool_t last) { nxt_router_tlssock_t *tls; @@ -1865,8 +1887,8 @@ nxt_router_conf_tls_insert(nxt_router_temp_conf_t *tmcf, return NXT_ERROR; } + tls->tls_init = tls_init; tls->socket_conf = skcf; - tls->conf_cmds = conf_cmds; tls->temp_conf = tmcf; tls->last = last; nxt_conf_get_string(value, &tls->name); @@ -2467,6 +2489,8 @@ nxt_router_tls_rpc_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg, tlscf = tls->socket_conf->tls; } + tls->tls_init->conf = tlscf; + bundle = nxt_mp_get(mp, sizeof(nxt_tls_bundle_conf_t)); if (nxt_slow_path(bundle == NULL)) { goto fail; @@ -2480,8 +2504,8 @@ nxt_router_tls_rpc_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg, bundle->next = tlscf->bundle; tlscf->bundle = bundle; - ret = task->thread->runtime->tls->server_init(task, tlscf, mp, - tls->conf_cmds, tls->last); + ret = task->thread->runtime->tls->server_init(task, mp, tls->tls_init, + tls->last); if (nxt_slow_path(ret != NXT_OK)) { goto fail; } diff --git a/src/nxt_tls.h b/src/nxt_tls.h index 63c49ee4..a92f1c21 100644 --- a/src/nxt_tls.h +++ b/src/nxt_tls.h @@ -28,14 +28,14 @@ typedef struct nxt_tls_conf_s nxt_tls_conf_t; typedef struct nxt_tls_bundle_conf_s nxt_tls_bundle_conf_t; +typedef struct nxt_tls_init_s nxt_tls_init_t; typedef struct { nxt_int_t (*library_init)(nxt_task_t *task); void (*library_free)(nxt_task_t *task); - nxt_int_t (*server_init)(nxt_task_t *task, - nxt_tls_conf_t *conf, nxt_mp_t *mp, - nxt_conf_value_t *conf_cmds, + nxt_int_t (*server_init)(nxt_task_t *task, nxt_mp_t *mp, + nxt_tls_init_t *tls_init, nxt_bool_t last); void (*server_free)(nxt_task_t *task, nxt_tls_conf_t *conf); @@ -78,6 +78,15 @@ struct nxt_tls_conf_s { }; +struct nxt_tls_init_s { + size_t cache_size; + nxt_time_t timeout; + nxt_conf_value_t *conf_cmds; + + nxt_tls_conf_t *conf; +}; + + #if (NXT_HAVE_OPENSSL) extern const nxt_tls_lib_t nxt_openssl_lib; -- cgit From f965e358b6ca878ead629dffb2f0df57230995ea Mon Sep 17 00:00:00 2001 From: Andrey Suvorov Date: Thu, 22 Jul 2021 11:23:48 -0700 Subject: Changing SNI callback return code if a client sends no SNI. When a client sends no SNI is a common situation. But currently the server processes it as an error and returns SSL_TLSEXT_ERR_ALERT_FATAL causing termination of a current TLS session. The problem occurs if configuration has more than one certificate bundle in a listener. This fix changes the return code to SSL_TLSEXT_ERR_OK and the log level of a message. --- src/nxt_openssl.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/nxt_openssl.c b/src/nxt_openssl.c index 3b5d4fda..297e11cf 100644 --- a/src/nxt_openssl.c +++ b/src/nxt_openssl.c @@ -804,15 +804,15 @@ nxt_openssl_servername(SSL *s, int *ad, void *arg) } servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name); - if (nxt_slow_path(servername == NULL)) { - nxt_log(c->socket.task, NXT_LOG_ALERT, "SSL_get_servername() returned " - "NULL in server name callback"); - return SSL_TLSEXT_ERR_ALERT_FATAL; + + if (servername == NULL) { + nxt_debug(c->socket.task, "SSL_get_servername(): NULL"); + goto done; } str.length = nxt_strlen(servername); if (str.length == 0) { - nxt_debug(c->socket.task, "client sent zero-length server name"); + nxt_debug(c->socket.task, "SSL_get_servername(): \"\" is empty"); goto done; } -- cgit From a3df6efc8d5994899a985080b574d9043ecd80f3 Mon Sep 17 00:00:00 2001 From: Zhidao HONG Date: Fri, 23 Jul 2021 09:14:43 +0800 Subject: Router: split nxt_http_static_conf_t from nxt_http_action_t. No functional changes. --- src/nxt_conf_validation.c | 14 +-- src/nxt_http.h | 27 +++--- src/nxt_http_route.c | 101 ++++----------------- src/nxt_http_static.c | 219 +++++++++++++++++++++++++++++++++------------- src/nxt_router.c | 10 +-- 5 files changed, 198 insertions(+), 173 deletions(-) (limited to 'src') diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index fd57a983..0ec2e811 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -1277,7 +1277,7 @@ static nxt_int_t nxt_conf_vldt_mtypes_extension(nxt_conf_validation_t *vldt, nxt_conf_value_t *value) { - nxt_str_t ext, *dup_type; + nxt_str_t exten, *dup_type; nxt_conf_vldt_mtypes_ctx_t *ctx; ctx = vldt->ctx; @@ -1287,24 +1287,24 @@ nxt_conf_vldt_mtypes_extension(nxt_conf_validation_t *vldt, "contain only strings.", ctx->type); } - nxt_conf_get_string(value, &ext); + nxt_conf_get_string(value, &exten); - if (ext.length == 0) { + if (exten.length == 0) { return nxt_conf_vldt_error(vldt, "An empty file extension for " "the \"%V\" MIME type.", ctx->type); } - dup_type = nxt_http_static_mtypes_hash_find(&ctx->hash, &ext); + dup_type = nxt_http_static_mtype_get(&ctx->hash, &exten); if (dup_type->length != 0) { return nxt_conf_vldt_error(vldt, "The \"%V\" file extension has been " "declared for \"%V\" and \"%V\" " "MIME types at the same time.", - &ext, dup_type, ctx->type); + &exten, dup_type, ctx->type); } - return nxt_http_static_mtypes_hash_add(ctx->pool, &ctx->hash, - &ext, ctx->type); + return nxt_http_static_mtypes_hash_add(ctx->pool, &ctx->hash, &exten, + ctx->type); } diff --git a/src/nxt_http.h b/src/nxt_http.h index 18cf8680..f0276afc 100644 --- a/src/nxt_http.h +++ b/src/nxt_http.h @@ -230,16 +230,10 @@ struct nxt_http_action_s { nxt_app_t *application; nxt_int_t target; } app; - - struct { - nxt_str_t chroot; - nxt_uint_t resolve; - nxt_http_route_rule_t *types; - nxt_http_action_t *fallback; - } share; } u; nxt_str_t name; + nxt_http_action_t *fallback; }; @@ -322,27 +316,30 @@ nxt_int_t nxt_http_pass_segments(nxt_mp_t *mp, nxt_str_t *pass, nxt_str_t *segments, nxt_uint_t n); nxt_http_action_t *nxt_http_pass_application(nxt_task_t *task, nxt_router_conf_t *rtcf, nxt_str_t *name); +nxt_http_route_rule_t *nxt_http_route_types_rule_create(nxt_task_t *task, + nxt_mp_t *mp, nxt_conf_value_t *types); nxt_int_t nxt_http_route_test_rule(nxt_http_request_t *r, nxt_http_route_rule_t *rule, u_char *start, size_t length); +nxt_int_t nxt_http_action_init(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, + nxt_conf_value_t *cv, nxt_http_action_t *action); +void nxt_http_request_action(nxt_task_t *task, nxt_http_request_t *r, + nxt_http_action_t *action); + nxt_int_t nxt_upstreams_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *conf); nxt_int_t nxt_upstreams_joint_create(nxt_router_temp_conf_t *tmcf, nxt_upstream_t ***upstream_joint); -void nxt_http_request_action(nxt_task_t *task, nxt_http_request_t *r, - nxt_http_action_t *action); - nxt_int_t nxt_http_return_init(nxt_mp_t *mp, nxt_http_action_t *action, nxt_http_action_conf_t *acf); -nxt_http_action_t *nxt_http_static_handler(nxt_task_t *task, - nxt_http_request_t *r, nxt_http_action_t *action); +nxt_int_t nxt_http_static_init(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, + nxt_http_action_t *action, nxt_http_action_conf_t *acf); nxt_int_t nxt_http_static_mtypes_init(nxt_mp_t *mp, nxt_lvlhsh_t *hash); nxt_int_t nxt_http_static_mtypes_hash_add(nxt_mp_t *mp, nxt_lvlhsh_t *hash, - nxt_str_t *extension, nxt_str_t *type); -nxt_str_t *nxt_http_static_mtypes_hash_find(nxt_lvlhsh_t *hash, - nxt_str_t *extension); + nxt_str_t *exten, nxt_str_t *type); +nxt_str_t *nxt_http_static_mtype_get(nxt_lvlhsh_t *hash, nxt_str_t *exten); nxt_http_action_t *nxt_http_application_handler(nxt_task_t *task, nxt_http_request_t *r, nxt_http_action_t *action); diff --git a/src/nxt_http_route.c b/src/nxt_http_route.c index 46c9b536..3cf78ec8 100644 --- a/src/nxt_http_route.c +++ b/src/nxt_http_route.c @@ -185,9 +185,6 @@ static nxt_http_route_t *nxt_http_route_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *cv); static nxt_http_route_match_t *nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *cv); -static nxt_int_t nxt_http_action_init(nxt_task_t *task, - nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *cv, - nxt_http_action_t *action); static nxt_http_route_table_t *nxt_http_route_table_create(nxt_task_t *task, nxt_mp_t *mp, nxt_conf_value_t *table_cv, nxt_http_route_object_t object, nxt_bool_t case_sensitive, nxt_http_route_encoding_t encoding); @@ -653,20 +650,14 @@ static nxt_conf_map_t nxt_http_route_action_conf[] = { }; -static nxt_int_t +nxt_int_t nxt_http_action_init(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *cv, nxt_http_action_t *action) { -#if (NXT_HAVE_OPENAT2) - u_char *p; - uint8_t slash; - nxt_str_t *chroot; -#endif nxt_mp_t *mp; nxt_int_t ret; nxt_str_t name, *string; nxt_conf_value_t *conf; - nxt_http_route_rule_t *rule; nxt_http_action_conf_t acf; nxt_memzero(&acf, sizeof(acf)); @@ -686,9 +677,10 @@ nxt_http_action_init(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, } if (acf.share != NULL) { - conf = acf.share; + return nxt_http_static_init(task, tmcf, action, &acf); + } - } else if (acf.proxy != NULL) { + if (acf.proxy != NULL) { conf = acf.proxy; } else { @@ -702,72 +694,6 @@ nxt_http_action_init(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, return NXT_ERROR; } - if (acf.share != NULL) { - action->handler = nxt_http_static_handler; - -#if (NXT_HAVE_OPENAT2) - string = &acf.chroot; - chroot = &action->u.share.chroot; - - if (string->length > 0) { - action->u.share.resolve |= RESOLVE_IN_ROOT; - - slash = (string->start[string->length - 1] != '/'); - - chroot->length = string->length + (slash ? 1 : 0); - - chroot->start = nxt_mp_alloc(mp, chroot->length + 1); - if (nxt_slow_path(chroot->start == NULL)) { - return NXT_ERROR; - } - - p = nxt_cpymem(chroot->start, string->start, string->length); - - if (slash) { - *p++ = '/'; - } - - *p = '\0'; - } - - if (acf.follow_symlinks != NULL - && !nxt_conf_get_boolean(acf.follow_symlinks)) - { - action->u.share.resolve |= RESOLVE_NO_SYMLINKS; - } - - if (acf.traverse_mounts != NULL - && !nxt_conf_get_boolean(acf.traverse_mounts)) - { - action->u.share.resolve |= RESOLVE_NO_XDEV; - } -#endif - - if (acf.types != NULL) { - rule = nxt_http_route_rule_create(task, mp, acf.types, 0, - NXT_HTTP_ROUTE_PATTERN_LOWCASE, - NXT_HTTP_ROUTE_ENCODING_NONE); - if (nxt_slow_path(rule == NULL)) { - return NXT_ERROR; - } - - action->u.share.types = rule; - } - - if (acf.fallback != NULL) { - action->u.share.fallback = nxt_mp_alloc(mp, - sizeof(nxt_http_action_t)); - if (nxt_slow_path(action->u.share.fallback == NULL)) { - return NXT_ERROR; - } - - return nxt_http_action_init(task, tmcf, acf.fallback, - action->u.share.fallback); - } - - return NXT_OK; - } - if (acf.proxy != NULL) { return nxt_http_proxy_create(mp, action); } @@ -1075,6 +1001,16 @@ nxt_http_route_addr_rule_create(nxt_task_t *task, nxt_mp_t *mp, } +nxt_http_route_rule_t * +nxt_http_route_types_rule_create(nxt_task_t *task, nxt_mp_t *mp, + nxt_conf_value_t *types) +{ + return nxt_http_route_rule_create(task, mp, types, 0, + NXT_HTTP_ROUTE_PATTERN_LOWCASE, + NXT_HTTP_ROUTE_ENCODING_NONE); +} + + static int nxt_http_pattern_compare(const void *one, const void *two) { @@ -1447,15 +1383,12 @@ static nxt_int_t nxt_http_action_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_http_action_t *action) { - nxt_var_t *var; nxt_int_t ret; + nxt_var_t *var; if (action->handler != NULL) { - if (action->handler == nxt_http_static_handler - && action->u.share.fallback != NULL) - { - return nxt_http_action_resolve(task, tmcf, - action->u.share.fallback); + if (action->fallback != NULL) { + return nxt_http_action_resolve(task, tmcf, action->fallback); } return NXT_OK; diff --git a/src/nxt_http_static.c b/src/nxt_http_static.c index c8b73fac..9b79a666 100644 --- a/src/nxt_http_static.c +++ b/src/nxt_http_static.c @@ -7,12 +7,22 @@ #include +typedef struct { + nxt_str_t share; + nxt_str_t chroot; + nxt_uint_t resolve; + nxt_http_route_rule_t *types; +} nxt_http_static_conf_t; + + #define NXT_HTTP_STATIC_BUF_COUNT 2 #define NXT_HTTP_STATIC_BUF_SIZE (128 * 1024) +static nxt_http_action_t *nxt_http_static(nxt_task_t *task, + nxt_http_request_t *r, nxt_http_action_t *action); static void nxt_http_static_extract_extension(nxt_str_t *path, - nxt_str_t *extension); + nxt_str_t *exten); static void nxt_http_static_body_handler(nxt_task_t *task, void *obj, void *data); static void nxt_http_static_buf_completion(nxt_task_t *task, void *obj, @@ -27,30 +37,122 @@ static void nxt_http_static_mtypes_hash_free(void *data, void *p); static const nxt_http_request_state_t nxt_http_static_send_state; -nxt_http_action_t * -nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r, +nxt_int_t +nxt_http_static_init(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, + nxt_http_action_t *action, nxt_http_action_conf_t *acf) +{ + nxt_mp_t *mp; + nxt_str_t *str, value; + nxt_http_static_conf_t *conf; + + mp = tmcf->router_conf->mem_pool; + + conf = nxt_mp_zget(mp, sizeof(nxt_http_static_conf_t)); + if (nxt_slow_path(conf == NULL)) { + return NXT_ERROR; + } + + action->handler = nxt_http_static; + action->u.conf = conf; + + nxt_conf_get_string(acf->share, &value); + + str = nxt_str_dup(mp, &conf->share, &value); + if (nxt_slow_path(str == NULL)) { + return NXT_ERROR; + } + +#if (NXT_HAVE_OPENAT2) + if (acf->chroot.length > 0) { + u_char *p; + nxt_str_t slash; + + if (acf->chroot.start[acf->chroot.length - 1] != '/') { + nxt_str_set(&slash, "/"); + + } else { + nxt_str_set(&slash, ""); + } + + value.length = acf->chroot.length + slash.length; + + value.start = nxt_mp_alloc(mp, value.length + 1); + if (nxt_slow_path(value.start == NULL)) { + return NXT_ERROR; + } + + p = value.start; + p = nxt_cpymem(p, acf->chroot.start, acf->chroot.length); + p = nxt_cpymem(p, slash.start, slash.length); + *p = '\0'; + + conf->chroot = value; + conf->resolve |= RESOLVE_IN_ROOT; + } + + if (acf->follow_symlinks != NULL + && !nxt_conf_get_boolean(acf->follow_symlinks)) + { + conf->resolve |= RESOLVE_NO_SYMLINKS; + } + + if (acf->traverse_mounts != NULL + && !nxt_conf_get_boolean(acf->traverse_mounts)) + { + conf->resolve |= RESOLVE_NO_XDEV; + } +#endif + + if (acf->types != NULL) { + conf->types = nxt_http_route_types_rule_create(task, mp, acf->types); + if (nxt_slow_path(conf->types == NULL)) { + return NXT_ERROR; + } + } + + if (acf->fallback != NULL) { + action->fallback = nxt_mp_alloc(mp, sizeof(nxt_http_action_t)); + if (nxt_slow_path(action->fallback == NULL)) { + return NXT_ERROR; + } + + return nxt_http_action_init(task, tmcf, acf->fallback, + action->fallback); + } + + return NXT_OK; +} + + +static nxt_http_action_t * +nxt_http_static(nxt_task_t *task, nxt_http_request_t *r, nxt_http_action_t *action) { - size_t length, encode; - u_char *p, *fname; - struct tm tm; - nxt_buf_t *fb; - nxt_int_t ret; - nxt_str_t index, extension, *mtype, *chroot; - nxt_uint_t level; - nxt_bool_t need_body; - nxt_file_t *f, file; - nxt_file_info_t fi; - nxt_http_field_t *field; - nxt_http_status_t status; - nxt_router_conf_t *rtcf; - nxt_work_handler_t body_handler; + size_t length, encode; + u_char *p, *fname; + struct tm tm; + nxt_buf_t *fb; + nxt_int_t ret; + nxt_str_t index, exten, *mtype, *chroot; + nxt_uint_t level; + nxt_bool_t need_body; + nxt_file_t *f, file; + nxt_file_info_t fi; + nxt_http_field_t *field; + nxt_http_status_t status; + nxt_router_conf_t *rtcf; + nxt_work_handler_t body_handler; + nxt_http_static_conf_t *conf; + + conf = action->u.conf; + + nxt_debug(task, "http static: \"%V\"", &conf->share); if (nxt_slow_path(!nxt_str_eq(r->method, "GET", 3))) { if (!nxt_str_eq(r->method, "HEAD", 4)) { - if (action->u.share.fallback != NULL) { - return action->u.share.fallback; + if (action->fallback != NULL) { + return action->fallback; } nxt_http_request_error(task, r, NXT_HTTP_METHOD_NOT_ALLOWED); @@ -66,11 +168,11 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r, if (r->path->start[r->path->length - 1] == '/') { /* TODO: dynamic index setting. */ nxt_str_set(&index, "index.html"); - nxt_str_set(&extension, ".html"); + nxt_str_set(&exten, ".html"); } else { nxt_str_set(&index, ""); - nxt_str_null(&extension); + nxt_str_null(&exten); } f = NULL; @@ -79,20 +181,19 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r, mtype = NULL; - if (action->u.share.types != NULL && extension.start == NULL) { - nxt_http_static_extract_extension(r->path, &extension); - mtype = nxt_http_static_mtypes_hash_find(&rtcf->mtypes_hash, - &extension); + if (conf->types != NULL && exten.start == NULL) { + nxt_http_static_extract_extension(r->path, &exten); + mtype = nxt_http_static_mtype_get(&rtcf->mtypes_hash, &exten); - ret = nxt_http_route_test_rule(r, action->u.share.types, - mtype->start, mtype->length); + ret = nxt_http_route_test_rule(r, conf->types, mtype->start, + mtype->length); if (nxt_slow_path(ret == NXT_ERROR)) { goto fail; } if (ret == 0) { - if (action->u.share.fallback != NULL) { - return action->u.share.fallback; + if (action->fallback != NULL) { + return action->fallback; } nxt_http_request_error(task, r, NXT_HTTP_FORBIDDEN); @@ -100,7 +201,7 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r, } } - length = action->name.length + r->path->length + index.length; + length = conf->share.length + r->path->length + index.length; fname = nxt_mp_nget(r->mem_pool, length + 1); if (nxt_slow_path(fname == NULL)) { @@ -108,7 +209,7 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r, } p = fname; - p = nxt_cpymem(p, action->name.start, action->name.length); + p = nxt_cpymem(p, conf->share.start, conf->share.length); p = nxt_cpymem(p, r->path->start, r->path->length); p = nxt_cpymem(p, index.start, index.length); *p = '\0'; @@ -117,11 +218,10 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r, file.name = fname; - chroot = &action->u.share.chroot; + chroot = &conf->chroot; #if (NXT_HAVE_OPENAT2) - - if (action->u.share.resolve != 0) { + if (conf->resolve != 0) { if (chroot->length > 0) { file.name = chroot->start; @@ -156,8 +256,7 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r, file.name = fname; ret = nxt_file_openat2(task, &file, NXT_FILE_RDONLY, - NXT_FILE_OPEN, 0, af.fd, - action->u.share.resolve); + NXT_FILE_OPEN, 0, af.fd, conf->resolve); if (af.fd != AT_FDCWD) { nxt_file_close(task, &af); @@ -169,9 +268,7 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r, } #else - ret = nxt_file_open(task, &file, NXT_FILE_RDONLY, NXT_FILE_OPEN, 0); - #endif if (nxt_slow_path(ret != NXT_OK)) { @@ -211,8 +308,8 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r, break; } - if (level == NXT_LOG_ERR && action->u.share.fallback != NULL) { - return action->u.share.fallback; + if (level == NXT_LOG_ERR && action->fallback != NULL) { + return action->fallback; } if (status != NXT_HTTP_NOT_FOUND) { @@ -283,13 +380,12 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r, nxt_file_size(&fi)) - p; - if (extension.start == NULL) { - nxt_http_static_extract_extension(r->path, &extension); + if (exten.start == NULL) { + nxt_http_static_extract_extension(r->path, &exten); } if (mtype == NULL) { - mtype = nxt_http_static_mtypes_hash_find(&rtcf->mtypes_hash, - &extension); + mtype = nxt_http_static_mtype_get(&rtcf->mtypes_hash, &exten); } if (mtype->length != 0) { @@ -328,8 +424,8 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r, nxt_file_close(task, f); if (nxt_slow_path(!nxt_is_dir(&fi))) { - if (action->u.share.fallback != NULL) { - return action->u.share.fallback; + if (action->fallback != NULL) { + return action->fallback; } nxt_log(task, NXT_LOG_ERR, "\"%FN\" is not a regular file", @@ -401,7 +497,7 @@ fail: static void -nxt_http_static_extract_extension(nxt_str_t *path, nxt_str_t *extension) +nxt_http_static_extract_extension(nxt_str_t *path, nxt_str_t *exten) { u_char ch, *p, *end; @@ -419,8 +515,8 @@ nxt_http_static_extract_extension(nxt_str_t *path, nxt_str_t *extension) p++; /* Fall through. */ case '.': - extension->length = end - p; - extension->start = p; + exten->length = end - p; + exten->start = p; return; } } @@ -571,13 +667,13 @@ clean: nxt_int_t nxt_http_static_mtypes_init(nxt_mp_t *mp, nxt_lvlhsh_t *hash) { - nxt_str_t *type, extension; + nxt_str_t *type, exten; nxt_int_t ret; nxt_uint_t i; static const struct { nxt_str_t type; - const char *extension; + const char *exten; } default_types[] = { { nxt_string("text/html"), ".html" }, @@ -644,10 +740,10 @@ nxt_http_static_mtypes_init(nxt_mp_t *mp, nxt_lvlhsh_t *hash) for (i = 0; i < nxt_nitems(default_types); i++) { type = (nxt_str_t *) &default_types[i].type; - extension.start = (u_char *) default_types[i].extension; - extension.length = nxt_strlen(extension.start); + exten.start = (u_char *) default_types[i].exten; + exten.length = nxt_strlen(exten.start); - ret = nxt_http_static_mtypes_hash_add(mp, hash, &extension, type); + ret = nxt_http_static_mtypes_hash_add(mp, hash, &exten, type); if (nxt_slow_path(ret != NXT_OK)) { return NXT_ERROR; } @@ -668,14 +764,14 @@ static const nxt_lvlhsh_proto_t nxt_http_static_mtypes_hash_proto typedef struct { - nxt_str_t extension; + nxt_str_t exten; nxt_str_t *type; } nxt_http_static_mtype_t; nxt_int_t nxt_http_static_mtypes_hash_add(nxt_mp_t *mp, nxt_lvlhsh_t *hash, - nxt_str_t *extension, nxt_str_t *type) + nxt_str_t *exten, nxt_str_t *type) { nxt_lvlhsh_query_t lhq; nxt_http_static_mtype_t *mtype; @@ -685,10 +781,10 @@ nxt_http_static_mtypes_hash_add(nxt_mp_t *mp, nxt_lvlhsh_t *hash, return NXT_ERROR; } - mtype->extension = *extension; + mtype->exten = *exten; mtype->type = type; - lhq.key = *extension; + lhq.key = *exten; lhq.key_hash = nxt_djb_hash_lowcase(lhq.key.start, lhq.key.length); lhq.replace = 1; lhq.value = mtype; @@ -700,14 +796,14 @@ nxt_http_static_mtypes_hash_add(nxt_mp_t *mp, nxt_lvlhsh_t *hash, nxt_str_t * -nxt_http_static_mtypes_hash_find(nxt_lvlhsh_t *hash, nxt_str_t *extension) +nxt_http_static_mtype_get(nxt_lvlhsh_t *hash, nxt_str_t *exten) { nxt_lvlhsh_query_t lhq; nxt_http_static_mtype_t *mtype; static nxt_str_t empty = nxt_string(""); - lhq.key = *extension; + lhq.key = *exten; lhq.key_hash = nxt_djb_hash_lowcase(lhq.key.start, lhq.key.length); lhq.proto = &nxt_http_static_mtypes_hash_proto; @@ -727,8 +823,7 @@ nxt_http_static_mtypes_hash_test(nxt_lvlhsh_query_t *lhq, void *data) mtype = data; - return nxt_strcasestr_eq(&lhq->key, &mtype->extension) ? NXT_OK - : NXT_DECLINED; + return nxt_strcasestr_eq(&lhq->key, &mtype->exten) ? NXT_OK : NXT_DECLINED; } diff --git a/src/nxt_router.c b/src/nxt_router.c index 1156edb8..96d62575 100644 --- a/src/nxt_router.c +++ b/src/nxt_router.c @@ -1907,7 +1907,7 @@ nxt_router_conf_process_static(nxt_task_t *task, nxt_router_conf_t *rtcf, { uint32_t next, i; nxt_mp_t *mp; - nxt_str_t *type, extension, str; + nxt_str_t *type, exten, str; nxt_int_t ret; nxt_uint_t exts; nxt_conf_value_t *mtypes_conf, *ext_conf, *value; @@ -1945,12 +1945,12 @@ nxt_router_conf_process_static(nxt_task_t *task, nxt_router_conf_t *rtcf, if (nxt_conf_type(ext_conf) == NXT_CONF_STRING) { nxt_conf_get_string(ext_conf, &str); - if (nxt_slow_path(nxt_str_dup(mp, &extension, &str) == NULL)) { + if (nxt_slow_path(nxt_str_dup(mp, &exten, &str) == NULL)) { return NXT_ERROR; } ret = nxt_http_static_mtypes_hash_add(mp, &rtcf->mtypes_hash, - &extension, type); + &exten, type); if (nxt_slow_path(ret != NXT_OK)) { return NXT_ERROR; } @@ -1965,12 +1965,12 @@ nxt_router_conf_process_static(nxt_task_t *task, nxt_router_conf_t *rtcf, nxt_conf_get_string(value, &str); - if (nxt_slow_path(nxt_str_dup(mp, &extension, &str) == NULL)) { + if (nxt_slow_path(nxt_str_dup(mp, &exten, &str) == NULL)) { return NXT_ERROR; } ret = nxt_http_static_mtypes_hash_add(mp, &rtcf->mtypes_hash, - &extension, type); + &exten, type); if (nxt_slow_path(ret != NXT_OK)) { return NXT_ERROR; } -- cgit From b47f1ac7ea3d80981485fd4ae82cd1c7fca3c337 Mon Sep 17 00:00:00 2001 From: Zhidao HONG Date: Mon, 26 Jul 2021 15:00:46 +0800 Subject: Router: renamed nxt_http_proxy_create() as nxt_http_proxy_init(). No functional changes. --- src/nxt_http.h | 4 ++-- src/nxt_http_proxy.c | 19 +++++++++++++------ src/nxt_http_route.c | 12 ++---------- 3 files changed, 17 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/nxt_http.h b/src/nxt_http.h index f0276afc..c08b3ba7 100644 --- a/src/nxt_http.h +++ b/src/nxt_http.h @@ -348,8 +348,8 @@ nxt_int_t nxt_upstream_find(nxt_upstreams_t *upstreams, nxt_str_t *name, nxt_http_action_t *nxt_upstream_proxy_handler(nxt_task_t *task, nxt_http_request_t *r, nxt_upstream_t *upstream); - -nxt_int_t nxt_http_proxy_create(nxt_mp_t *mp, nxt_http_action_t *action); +nxt_int_t nxt_http_proxy_init(nxt_mp_t *mp, nxt_http_action_t *action, + nxt_http_action_conf_t *acf); nxt_int_t nxt_http_proxy_date(void *ctx, nxt_http_field_t *field, uintptr_t data); nxt_int_t nxt_http_proxy_content_length(void *ctx, nxt_http_field_t *field, diff --git a/src/nxt_http_proxy.c b/src/nxt_http_proxy.c index 338d9fce..6aa3aabb 100644 --- a/src/nxt_http_proxy.c +++ b/src/nxt_http_proxy.c @@ -21,7 +21,7 @@ static void nxt_http_proxy_upstream_ready(nxt_task_t *task, nxt_upstream_server_t *us); static void nxt_http_proxy_upstream_error(nxt_task_t *task, nxt_upstream_server_t *us); -static nxt_http_action_t *nxt_http_proxy_handler(nxt_task_t *task, +static nxt_http_action_t *nxt_http_proxy(nxt_task_t *task, nxt_http_request_t *r, nxt_http_action_t *action); static void nxt_http_proxy_header_send(nxt_task_t *task, void *obj, void *data); static void nxt_http_proxy_header_sent(nxt_task_t *task, void *obj, void *data); @@ -50,7 +50,8 @@ static const nxt_upstream_peer_state_t nxt_upstream_proxy_state = { nxt_int_t -nxt_http_proxy_create(nxt_mp_t *mp, nxt_http_action_t *action) +nxt_http_proxy_init(nxt_mp_t *mp, nxt_http_action_t *action, + nxt_http_action_conf_t *acf) { nxt_str_t name; nxt_sockaddr_t *sa; @@ -58,7 +59,7 @@ nxt_http_proxy_create(nxt_mp_t *mp, nxt_http_action_t *action) nxt_upstream_proxy_t *proxy; sa = NULL; - name = action->name; + nxt_conf_get_string(acf->proxy, &name); if (nxt_str_start(&name, "http://", 7)) { name.length -= 7; @@ -92,7 +93,7 @@ nxt_http_proxy_create(nxt_mp_t *mp, nxt_http_action_t *action) up->type.proxy = proxy; action->u.upstream = up; - action->handler = nxt_http_proxy_handler; + action->handler = nxt_http_proxy; } return NXT_OK; @@ -100,10 +101,16 @@ nxt_http_proxy_create(nxt_mp_t *mp, nxt_http_action_t *action) static nxt_http_action_t * -nxt_http_proxy_handler(nxt_task_t *task, nxt_http_request_t *r, +nxt_http_proxy(nxt_task_t *task, nxt_http_request_t *r, nxt_http_action_t *action) { - return nxt_upstream_proxy_handler(task, r, action->u.upstream); + nxt_upstream_t *u; + + u = action->u.upstream; + + nxt_debug(task, "http proxy: \"%V\"", &u->name); + + return nxt_upstream_proxy_handler(task, r, u); } diff --git a/src/nxt_http_route.c b/src/nxt_http_route.c index 3cf78ec8..bed127c9 100644 --- a/src/nxt_http_route.c +++ b/src/nxt_http_route.c @@ -657,7 +657,6 @@ nxt_http_action_init(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_mp_t *mp; nxt_int_t ret; nxt_str_t name, *string; - nxt_conf_value_t *conf; nxt_http_action_conf_t acf; nxt_memzero(&acf, sizeof(acf)); @@ -681,23 +680,16 @@ nxt_http_action_init(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, } if (acf.proxy != NULL) { - conf = acf.proxy; - - } else { - conf = acf.pass; + return nxt_http_proxy_init(mp, action, &acf); } - nxt_conf_get_string(conf, &name); + nxt_conf_get_string(acf.pass, &name); string = nxt_str_dup(mp, &action->name, &name); if (nxt_slow_path(string == NULL)) { return NXT_ERROR; } - if (acf.proxy != NULL) { - return nxt_http_proxy_create(mp, action); - } - return NXT_OK; } -- cgit From f3a1c1deb541784b2b0ed179514e4d5eba9fe626 Mon Sep 17 00:00:00 2001 From: Zhidao HONG Date: Sat, 24 Jul 2021 11:44:52 +0800 Subject: Router: split nxt_http_app_conf_t from nxt_http_action_t. No functional changes. --- src/nxt_http.h | 5 ----- src/nxt_http_request.c | 4 +--- src/nxt_http_route.c | 26 +++----------------------- src/nxt_router.c | 47 ++++++++++++++++++++++++++++++++++++++--------- src/nxt_router.h | 6 +++--- 5 files changed, 45 insertions(+), 43 deletions(-) (limited to 'src') diff --git a/src/nxt_http.h b/src/nxt_http.h index c08b3ba7..faba83f2 100644 --- a/src/nxt_http.h +++ b/src/nxt_http.h @@ -225,11 +225,6 @@ struct nxt_http_action_s { nxt_upstream_t *upstream; uint32_t upstream_number; nxt_var_t *var; - - struct { - nxt_app_t *application; - nxt_int_t target; - } app; } u; nxt_str_t name; diff --git a/src/nxt_http_request.c b/src/nxt_http_request.c index 779cfcf8..16563a98 100644 --- a/src/nxt_http_request.c +++ b/src/nxt_http_request.c @@ -348,9 +348,7 @@ nxt_http_application_handler(nxt_task_t *task, nxt_http_request_t *r, nxt_str_set(&r->server_name, "localhost"); } - r->app_target = action->u.app.target; - - nxt_router_process_http_request(task, r, action->u.app.application); + nxt_router_process_http_request(task, r, action); return NULL; } diff --git a/src/nxt_http_route.c b/src/nxt_http_route.c index bed127c9..c38acdf3 100644 --- a/src/nxt_http_route.c +++ b/src/nxt_http_route.c @@ -1482,9 +1482,7 @@ static nxt_int_t nxt_http_pass_find(nxt_task_t *task, nxt_mp_t *mp, nxt_router_conf_t *rtcf, nxt_http_action_t *action) { - nxt_str_t *targets; nxt_int_t ret; - nxt_uint_t i; nxt_str_t segments[3]; ret = nxt_http_pass_segments(mp, &action->name, segments, 3); @@ -1493,24 +1491,8 @@ nxt_http_pass_find(nxt_task_t *task, nxt_mp_t *mp, nxt_router_conf_t *rtcf, } if (nxt_str_eq(&segments[0], "applications", 12)) { - ret = nxt_router_listener_application(rtcf, &segments[1], action); - - if (ret != NXT_OK) { - return ret; - } - - if (segments[2].length != 0) { - targets = action->u.app.application->targets; - - for (i = 0; !nxt_strstr_eq(&segments[2], &targets[i]); i++); - - action->u.app.target = i; - - } else { - action->u.app.target = 0; - } - - return NXT_OK; + return nxt_router_application_init(rtcf, &segments[1], &segments[2], + action); } if (segments[2].length == 0) { @@ -1643,9 +1625,7 @@ nxt_http_pass_application(nxt_task_t *task, nxt_router_conf_t *rtcf, action->name = *name; - (void) nxt_router_listener_application(rtcf, name, action); - - action->u.app.target = 0; + (void) nxt_router_application_init(rtcf, name, NULL, action); return action; } diff --git a/src/nxt_router.c b/src/nxt_router.c index 96d62575..c766c25e 100644 --- a/src/nxt_router.c +++ b/src/nxt_router.c @@ -2158,21 +2158,46 @@ nxt_router_apps_hash_use(nxt_task_t *task, nxt_router_conf_t *rtcf, int i) } +typedef struct { + nxt_app_t *app; + nxt_int_t target; +} nxt_http_app_conf_t; + nxt_int_t -nxt_router_listener_application(nxt_router_conf_t *rtcf, nxt_str_t *name, - nxt_http_action_t *action) +nxt_router_application_init(nxt_router_conf_t *rtcf, nxt_str_t *name, + nxt_str_t *target, nxt_http_action_t *action) { - nxt_app_t *app; + nxt_app_t *app; + nxt_str_t *targets; + nxt_uint_t i; + nxt_http_app_conf_t *conf; app = nxt_router_apps_hash_get(rtcf, name); - if (app == NULL) { return NXT_DECLINED; } - action->u.app.application = app; + conf = nxt_mp_get(rtcf->mem_pool, sizeof(nxt_http_app_conf_t)); + if (nxt_slow_path(conf == NULL)) { + return NXT_ERROR; + } + action->handler = nxt_http_application_handler; + action->u.conf = conf; + + conf->app = app; + + if (target != NULL && target->length != 0) { + targets = app->targets; + + for (i = 0; !nxt_strstr_eq(target, &targets[i]); i++); + + conf->target = i; + + } else { + conf->target = 0; + } return NXT_OK; } @@ -4901,13 +4926,17 @@ nxt_router_app_port_get(nxt_task_t *task, nxt_app_t *app, void nxt_router_process_http_request(nxt_task_t *task, nxt_http_request_t *r, - nxt_app_t *app) + nxt_http_action_t *action) { nxt_event_engine_t *engine; + nxt_http_app_conf_t *conf; nxt_request_rpc_data_t *req_rpc_data; + conf = action->u.conf; engine = task->thread->engine; + r->app_target = conf->target; + req_rpc_data = nxt_port_rpc_register_handler_ex(task, engine->port, nxt_router_response_ready_handler, nxt_router_response_error_handler, @@ -4938,11 +4967,11 @@ nxt_router_process_http_request(nxt_task_t *task, nxt_http_request_t *r, r->err_work.obj = r; req_rpc_data->stream = nxt_port_rpc_ex_stream(req_rpc_data); - req_rpc_data->app = app; + req_rpc_data->app = conf->app; req_rpc_data->msg_info.body_fd = -1; req_rpc_data->rpc_cancel = 1; - nxt_router_app_use(task, app, 1); + nxt_router_app_use(task, conf->app, 1); req_rpc_data->request = r; r->req_rpc_data = req_rpc_data; @@ -4951,7 +4980,7 @@ nxt_router_process_http_request(nxt_task_t *task, nxt_http_request_t *r, r->last->completion_handler = nxt_router_http_request_done; } - nxt_router_app_port_get(task, app, req_rpc_data); + nxt_router_app_port_get(task, conf->app, req_rpc_data); nxt_router_app_prepare_request(task, req_rpc_data); } diff --git a/src/nxt_router.h b/src/nxt_router.h index b1ccdf51..9f26a622 100644 --- a/src/nxt_router.h +++ b/src/nxt_router.h @@ -223,10 +223,10 @@ struct nxt_router_access_log_s { void nxt_router_process_http_request(nxt_task_t *task, nxt_http_request_t *r, - nxt_app_t *app); + nxt_http_action_t *action); void nxt_router_app_port_close(nxt_task_t *task, nxt_port_t *port); -nxt_int_t nxt_router_listener_application(nxt_router_conf_t *rtcf, - nxt_str_t *name, nxt_http_action_t *action); +nxt_int_t nxt_router_application_init(nxt_router_conf_t *rtcf, nxt_str_t *name, + nxt_str_t *target, nxt_http_action_t *action); void nxt_router_listen_event_release(nxt_task_t *task, nxt_listen_event_t *lev, nxt_socket_conf_joint_t *joint); void nxt_router_conf_release(nxt_task_t *task, nxt_socket_conf_joint_t *joint); -- cgit From fa9fb29be221e0393562831a9e3bcba416652f60 Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Thu, 29 Jul 2021 19:50:39 +0300 Subject: Application restart introduced. When processing a restart request, the router sends a QUIT message to all existing processes of the application. Then, a new shared application port is created to ensure that new requests won't be handled by the old processes of the application. --- src/nxt_controller.c | 161 +++++++++++++++++++++++++++++++++++++ src/nxt_port.h | 3 + src/nxt_router.c | 219 +++++++++++++++++++++++++++++++++++++++++---------- src/nxt_router.h | 2 + 4 files changed, 344 insertions(+), 41 deletions(-) (limited to 'src') diff --git a/src/nxt_controller.c b/src/nxt_controller.c index 772d10c8..779a625d 100644 --- a/src/nxt_controller.c +++ b/src/nxt_controller.c @@ -92,6 +92,10 @@ static nxt_bool_t nxt_controller_cert_in_use(nxt_str_t *name); static void nxt_controller_cert_cleanup(nxt_task_t *task, void *obj, void *data); #endif +static void nxt_controller_process_control(nxt_task_t *task, + nxt_controller_request_t *req, nxt_str_t *path); +static void nxt_controller_app_restart_handler(nxt_task_t *task, + nxt_port_recv_msg_t *msg, void *data); static void nxt_controller_conf_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg, void *data); static void nxt_controller_conf_store(nxt_task_t *task, @@ -1022,6 +1026,14 @@ nxt_controller_process_request(nxt_task_t *task, nxt_controller_request_t *req) #endif + if (nxt_str_start(&path, "/control/", 9)) { + path.length -= 9; + path.start += 9; + + nxt_controller_process_control(task, req, &path); + return; + } + nxt_memzero(&resp, sizeof(nxt_controller_response_t)); if (path.length == 1 && path.start[0] == '/') { @@ -1683,6 +1695,155 @@ nxt_controller_conf_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg, } +static void +nxt_controller_process_control(nxt_task_t *task, + nxt_controller_request_t *req, nxt_str_t *path) +{ + uint32_t stream; + nxt_buf_t *b; + nxt_int_t rc; + nxt_port_t *router_port, *controller_port; + nxt_runtime_t *rt; + nxt_conf_value_t *value; + nxt_controller_response_t resp; + + static nxt_str_t applications = nxt_string("applications"); + + nxt_memzero(&resp, sizeof(nxt_controller_response_t)); + + if (!nxt_str_eq(&req->parser.method, "GET", 3)) { + goto not_allowed; + } + + if (!nxt_str_start(path, "applications/", 13) + || nxt_memcmp(path->start + path->length - 8, "/restart", 8) != 0) + { + goto not_found; + } + + path->start += 13; + path->length -= 13 + 8; + + if (nxt_controller_check_postpone_request(task)) { + nxt_queue_insert_tail(&nxt_controller_waiting_requests, &req->link); + return; + } + + value = nxt_controller_conf.root; + if (value == NULL) { + goto not_found; + } + + value = nxt_conf_get_object_member(value, &applications, NULL); + if (value == NULL) { + goto not_found; + } + + value = nxt_conf_get_object_member(value, path, NULL); + if (value == NULL) { + goto not_found; + } + + b = nxt_buf_mem_alloc(req->conn->mem_pool, path->length, 0); + if (nxt_slow_path(b == NULL)) { + goto alloc_fail; + } + + b->mem.free = nxt_cpymem(b->mem.pos, path->start, path->length); + + rt = task->thread->runtime; + + controller_port = rt->port_by_type[NXT_PROCESS_CONTROLLER]; + router_port = rt->port_by_type[NXT_PROCESS_ROUTER]; + + stream = nxt_port_rpc_register_handler(task, controller_port, + nxt_controller_app_restart_handler, + nxt_controller_app_restart_handler, + router_port->pid, req); + if (nxt_slow_path(stream == 0)) { + goto alloc_fail; + } + + rc = nxt_port_socket_write(task, router_port, NXT_PORT_MSG_APP_RESTART, + -1, stream, 0, b); + if (nxt_slow_path(rc != NXT_OK)) { + nxt_port_rpc_cancel(task, controller_port, stream); + + goto fail; + } + + nxt_queue_insert_head(&nxt_controller_waiting_requests, &req->link); + + return; + +not_allowed: + + resp.status = 405; + resp.title = (u_char *) "Method isn't allowed."; + resp.offset = -1; + + nxt_controller_response(task, req, &resp); + return; + +not_found: + + resp.status = 404; + resp.title = (u_char *) "Value doesn't exist."; + resp.offset = -1; + + nxt_controller_response(task, req, &resp); + return; + +alloc_fail: + + resp.status = 500; + resp.title = (u_char *) "Memory allocation failed."; + resp.offset = -1; + + nxt_controller_response(task, req, &resp); + return; + +fail: + + resp.status = 500; + resp.title = (u_char *) "Send restart failed."; + resp.offset = -1; + + nxt_controller_response(task, req, &resp); +} + + +static void +nxt_controller_app_restart_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg, + void *data) +{ + nxt_controller_request_t *req; + nxt_controller_response_t resp; + + req = data; + + nxt_debug(task, "controller app restart handler"); + + nxt_queue_remove(&req->link); + + nxt_memzero(&resp, sizeof(nxt_controller_response_t)); + + if (msg->port_msg.type == NXT_PORT_MSG_RPC_READY) { + resp.status = 200; + resp.title = (u_char *) "Ok"; + + } else { + resp.status = 500; + resp.title = (u_char *) "Failed to restart app."; + resp.offset = -1; + } + + nxt_controller_response(task, req, &resp); + + nxt_controller_flush_requests(task); +} + + static void nxt_controller_conf_store(nxt_task_t *task, nxt_conf_value_t *conf) { diff --git a/src/nxt_port.h b/src/nxt_port.h index 5ece3bfa..a0bc2512 100644 --- a/src/nxt_port.h +++ b/src/nxt_port.h @@ -50,6 +50,7 @@ struct nxt_port_handlers_s { /* Various data. */ nxt_port_handler_t data; + nxt_port_handler_t app_restart; nxt_port_handler_t oosm; nxt_port_handler_t shm_ack; @@ -100,6 +101,7 @@ typedef enum { _NXT_PORT_MSG_WEBSOCKET = nxt_port_handler_idx(websocket_frame), _NXT_PORT_MSG_DATA = nxt_port_handler_idx(data), + _NXT_PORT_MSG_APP_RESTART = nxt_port_handler_idx(app_restart), _NXT_PORT_MSG_OOSM = nxt_port_handler_idx(oosm), _NXT_PORT_MSG_SHM_ACK = nxt_port_handler_idx(shm_ack), @@ -139,6 +141,7 @@ typedef enum { NXT_PORT_MSG_DATA = _NXT_PORT_MSG_DATA, NXT_PORT_MSG_DATA_LAST = nxt_msg_last(_NXT_PORT_MSG_DATA), + NXT_PORT_MSG_APP_RESTART = nxt_msg_last(_NXT_PORT_MSG_APP_RESTART), NXT_PORT_MSG_OOSM = nxt_msg_last(_NXT_PORT_MSG_OOSM), NXT_PORT_MSG_SHM_ACK = nxt_msg_last(_NXT_PORT_MSG_SHM_ACK), diff --git a/src/nxt_router.c b/src/nxt_router.c index c766c25e..8360e75a 100644 --- a/src/nxt_router.c +++ b/src/nxt_router.c @@ -18,6 +18,8 @@ #include #include +#define NXT_SHARED_PORT_ID 0xFFFFu + typedef struct { nxt_str_t type; uint32_t processes; @@ -67,6 +69,12 @@ typedef struct { } nxt_app_rpc_t; +typedef struct { + nxt_app_joint_t *app_joint; + uint32_t generation; +} nxt_app_joint_rpc_t; + + static nxt_int_t nxt_router_prefork(nxt_task_t *task, nxt_process_t *process, nxt_mp_t *mp); static nxt_int_t nxt_router_start(nxt_task_t *task, nxt_process_data_t *data); @@ -79,6 +87,8 @@ static void nxt_router_new_port_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg); static void nxt_router_conf_data_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg); +static void nxt_router_app_restart_handler(nxt_task_t *task, + nxt_port_recv_msg_t *msg); static void nxt_router_remove_pid_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg); static void nxt_router_access_log_reopen_handler(nxt_task_t *task, @@ -281,6 +291,7 @@ static const nxt_port_handlers_t nxt_router_process_port_handlers = { .mmap = nxt_port_mmap_handler, .get_mmap = nxt_router_get_mmap_handler, .data = nxt_router_conf_data_handler, + .app_restart = nxt_router_app_restart_handler, .remove_pid = nxt_router_remove_pid_handler, .access_log = nxt_router_access_log_reopen_handler, .rpc_ready = nxt_port_rpc_handler, @@ -379,14 +390,15 @@ static void nxt_router_start_app_process_handler(nxt_task_t *task, nxt_port_t *port, void *data) { - size_t size; - uint32_t stream; - nxt_mp_t *mp; - nxt_int_t ret; - nxt_app_t *app; - nxt_buf_t *b; - nxt_port_t *main_port; - nxt_runtime_t *rt; + size_t size; + uint32_t stream; + nxt_mp_t *mp; + nxt_int_t ret; + nxt_app_t *app; + nxt_buf_t *b; + nxt_port_t *main_port; + nxt_runtime_t *rt; + nxt_app_joint_rpc_t *app_joint_rpc; app = data; @@ -407,30 +419,29 @@ nxt_router_start_app_process_handler(nxt_task_t *task, nxt_port_t *port, *b->mem.free++ = '\0'; nxt_buf_cpystr(b, &app->conf); - nxt_router_app_joint_use(task, app->joint, 1); - - stream = nxt_port_rpc_register_handler(task, port, - nxt_router_app_port_ready, - nxt_router_app_port_error, - -1, app->joint); - - if (nxt_slow_path(stream == 0)) { - nxt_router_app_joint_use(task, app->joint, -1); - + app_joint_rpc = nxt_port_rpc_register_handler_ex(task, port, + nxt_router_app_port_ready, + nxt_router_app_port_error, + sizeof(nxt_app_joint_rpc_t)); + if (nxt_slow_path(app_joint_rpc == NULL)) { goto failed; } + stream = nxt_port_rpc_ex_stream(app_joint_rpc); + ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_START_PROCESS, -1, stream, port->id, b); - if (nxt_slow_path(ret != NXT_OK)) { nxt_port_rpc_cancel(task, port, stream); - nxt_router_app_joint_use(task, app->joint, -1); - goto failed; } + app_joint_rpc->app_joint = app->joint; + app_joint_rpc->generation = app->generation; + + nxt_router_app_joint_use(task, app->joint, 1); + nxt_router_app_use(task, app, -1); return; @@ -504,6 +515,7 @@ nxt_router_msg_cancel(nxt_task_t *task, nxt_request_rpc_data_t *req_rpc_data) { nxt_buf_t *b, *next; nxt_bool_t cancelled; + nxt_port_t *app_port; nxt_msg_info_t *msg_info; msg_info = &req_rpc_data->msg_info; @@ -512,13 +524,20 @@ nxt_router_msg_cancel(nxt_task_t *task, nxt_request_rpc_data_t *req_rpc_data) return 0; } - cancelled = nxt_app_queue_cancel(req_rpc_data->app->shared_port->queue, - msg_info->tracking_cookie, - req_rpc_data->stream); + app_port = req_rpc_data->app_port; + + if (app_port != NULL && app_port->id == NXT_SHARED_PORT_ID) { + cancelled = nxt_app_queue_cancel(app_port->queue, + msg_info->tracking_cookie, + req_rpc_data->stream); + + if (cancelled) { + nxt_debug(task, "stream #%uD: cancelled by router", + req_rpc_data->stream); + } - if (cancelled) { - nxt_debug(task, "stream #%uD: cancelled by router", - req_rpc_data->stream); + } else { + cancelled = 0; } for (b = msg_info->buf; b != NULL; b = next) { @@ -793,6 +812,90 @@ cleanup: } +static void +nxt_router_app_restart_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) +{ + nxt_app_t *app; + nxt_int_t ret; + nxt_str_t app_name; + nxt_port_t *port, *reply_port, *shared_port, *old_shared_port; + nxt_port_msg_type_t reply; + + reply_port = nxt_runtime_port_find(task->thread->runtime, + msg->port_msg.pid, + msg->port_msg.reply_port); + if (nxt_slow_path(reply_port == NULL)) { + nxt_alert(task, "app_restart_handler: reply port not found"); + return; + } + + app_name.length = nxt_buf_mem_used_size(&msg->buf->mem); + app_name.start = msg->buf->mem.pos; + + nxt_debug(task, "app_restart_handler: %V", &app_name); + + app = nxt_router_app_find(&nxt_router->apps, &app_name); + + if (nxt_fast_path(app != NULL)) { + shared_port = nxt_port_new(task, NXT_SHARED_PORT_ID, nxt_pid, + NXT_PROCESS_APP); + if (nxt_slow_path(shared_port == NULL)) { + goto fail; + } + + ret = nxt_port_socket_init(task, shared_port, 0); + if (nxt_slow_path(ret != NXT_OK)) { + nxt_port_use(task, shared_port, -1); + goto fail; + } + + ret = nxt_router_app_queue_init(task, shared_port); + if (nxt_slow_path(ret != NXT_OK)) { + nxt_port_write_close(shared_port); + nxt_port_read_close(shared_port); + nxt_port_use(task, shared_port, -1); + goto fail; + } + + nxt_port_write_enable(task, shared_port); + + nxt_thread_mutex_lock(&app->mutex); + + nxt_queue_each(port, &app->ports, nxt_port_t, app_link) { + + (void) nxt_port_socket_write(task, port, NXT_PORT_MSG_QUIT, -1, + 0, 0, NULL); + + } nxt_queue_loop; + + app->generation++; + + shared_port->app = app; + + old_shared_port = app->shared_port; + old_shared_port->app = NULL; + + app->shared_port = shared_port; + + nxt_thread_mutex_unlock(&app->mutex); + + nxt_port_close(task, old_shared_port); + nxt_port_use(task, old_shared_port, -1); + + reply = NXT_PORT_MSG_RPC_READY_LAST; + + } else { + +fail: + + reply = NXT_PORT_MSG_RPC_ERROR; + } + + nxt_port_socket_write(task, reply_port, reply, -1, msg->port_msg.stream, + 0, NULL); +} + + static void nxt_router_app_process_remove_pid(nxt_task_t *task, nxt_port_t *port, void *data) @@ -1607,7 +1710,7 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, app_joint->free_app_work.task = &engine->task; app_joint->free_app_work.obj = app_joint; - port = nxt_port_new(task, (nxt_port_id_t) -1, nxt_pid, + port = nxt_port_new(task, NXT_SHARED_PORT_ID, nxt_pid, NXT_PROCESS_APP); if (nxt_slow_path(port == NULL)) { return NXT_ERROR; @@ -4233,11 +4336,16 @@ static void nxt_router_app_port_ready(nxt_task_t *task, nxt_port_recv_msg_t *msg, void *data) { - nxt_app_t *app; - nxt_port_t *port; - nxt_app_joint_t *app_joint; + nxt_app_t *app; + nxt_bool_t start_process; + nxt_port_t *port; + nxt_app_joint_t *app_joint; + nxt_app_joint_rpc_t *app_joint_rpc; - app_joint = data; + nxt_assert(data != NULL); + + app_joint_rpc = data; + app_joint = app_joint_rpc->app_joint; port = msg->u.new_port; nxt_assert(app_joint != NULL); @@ -4257,14 +4365,37 @@ nxt_router_app_port_ready(nxt_task_t *task, nxt_port_recv_msg_t *msg, return; } - port->app = app; - port->main_app_port = port; - nxt_thread_mutex_lock(&app->mutex); nxt_assert(app->pending_processes != 0); app->pending_processes--; + + if (nxt_slow_path(app->generation != app_joint_rpc->generation)) { + nxt_debug(task, "new port ready for restarted app, send QUIT"); + + start_process = !task->thread->engine->shutdown + && nxt_router_app_can_start(app) + && nxt_router_app_need_start(app); + + if (start_process) { + app->pending_processes++; + } + + nxt_thread_mutex_unlock(&app->mutex); + + nxt_port_socket_write(task, port, NXT_PORT_MSG_QUIT, -1, 0, 0, NULL); + + if (start_process) { + nxt_router_start_app_process(task, app); + } + + return; + } + + port->app = app; + port->main_app_port = port; + app->processes++; nxt_port_hash_add(&app->port_hash, port); app->port_hash_count++; @@ -4318,12 +4449,16 @@ static void nxt_router_app_port_error(nxt_task_t *task, nxt_port_recv_msg_t *msg, void *data) { - nxt_app_t *app; - nxt_app_joint_t *app_joint; - nxt_queue_link_t *link; - nxt_http_request_t *r; + nxt_app_t *app; + nxt_app_joint_t *app_joint; + nxt_queue_link_t *link; + nxt_http_request_t *r; + nxt_app_joint_rpc_t *app_joint_rpc; + + nxt_assert(data != NULL); - app_joint = data; + app_joint_rpc = data; + app_joint = app_joint_rpc->app_joint; nxt_assert(app_joint != NULL); @@ -4490,7 +4625,7 @@ nxt_router_app_port_release(nxt_task_t *task, nxt_port_t *port, port->pid, port->id, (int) inc_use, (int) got_response); - if (port == app->shared_port) { + if (port->id == NXT_SHARED_PORT_ID) { nxt_thread_mutex_lock(&app->mutex); app->active_requests -= got_response + dec_requests; @@ -4860,6 +4995,8 @@ nxt_router_free_app(nxt_task_t *task, void *obj, void *data) app->shared_port->app = NULL; nxt_port_close(task, app->shared_port); nxt_port_use(task, app->shared_port, -1); + + app->shared_port = NULL; } nxt_thread_mutex_destroy(&app->mutex); diff --git a/src/nxt_router.h b/src/nxt_router.h index 9f26a622..6611cf45 100644 --- a/src/nxt_router.h +++ b/src/nxt_router.h @@ -125,6 +125,8 @@ struct nxt_app_s { uint32_t max_pending_processes; uint32_t max_requests; + uint32_t generation; + nxt_msec_t timeout; nxt_msec_t idle_timeout; -- cgit From db03dfad6745a7d87d784ac51ed2d52e1c50a557 Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Tue, 3 Aug 2021 13:59:27 +0300 Subject: Fixed dead assignments. Found by Clang Static Analyzer. --- src/nxt_buf.c | 2 -- src/nxt_event_engine.c | 3 +-- src/nxt_h1proto.c | 1 - src/nxt_http_chunk_parse.c | 1 - src/nxt_isolation.c | 4 ++-- src/nxt_main_process.c | 2 -- 6 files changed, 3 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/nxt_buf.c b/src/nxt_buf.c index 83be0fac..cbde069e 100644 --- a/src/nxt_buf.c +++ b/src/nxt_buf.c @@ -201,7 +201,6 @@ nxt_buf_completion(nxt_task_t *task, void *obj, void *data) nxt_buf_t *b, *next, *parent; b = obj; - parent = data; nxt_debug(task, "buf completion: %p %p", b, b->mem.start); @@ -275,7 +274,6 @@ nxt_buf_ts_completion(nxt_task_t *task, void *obj, void *data) nxt_buf_t *b, *next, *parent; b = obj; - parent = data; if (nxt_buf_ts_handle(task, obj, data)) { return; diff --git a/src/nxt_event_engine.c b/src/nxt_event_engine.c index 4384d3b1..78c79bb1 100644 --- a/src/nxt_event_engine.c +++ b/src/nxt_event_engine.c @@ -720,11 +720,10 @@ nxt_event_engine_buf_mem_free(nxt_event_engine_t *engine, nxt_buf_t *b) void nxt_event_engine_buf_mem_completion(nxt_task_t *task, void *obj, void *data) { - nxt_event_engine_t *engine; nxt_buf_t *b, *next, *parent; + nxt_event_engine_t *engine; b = obj; - parent = data; nxt_debug(task, "buf completion: %p %p", b, b->mem.start); diff --git a/src/nxt_h1proto.c b/src/nxt_h1proto.c index d3da6942..b683cb22 100755 --- a/src/nxt_h1proto.c +++ b/src/nxt_h1proto.c @@ -955,7 +955,6 @@ nxt_h1p_request_body_read(nxt_task_t *task, nxt_http_request_t *r) } else { size = nxt_min(body_buffer_size, size); b->mem.free = nxt_cpymem(b->mem.free, in->mem.pos, size); - body_buffer_size -= size; } in->mem.pos += size; diff --git a/src/nxt_http_chunk_parse.c b/src/nxt_http_chunk_parse.c index be3a2023..deab116d 100644 --- a/src/nxt_http_chunk_parse.c +++ b/src/nxt_http_chunk_parse.c @@ -253,7 +253,6 @@ nxt_http_chunk_buf_completion(nxt_task_t *task, void *obj, void *data) nxt_buf_t *b, *next, *parent; b = obj; - parent = data; nxt_debug(task, "buf completion: %p %p", b, b->mem.start); diff --git a/src/nxt_isolation.c b/src/nxt_isolation.c index cab0074b..e3cb1f22 100644 --- a/src/nxt_isolation.c +++ b/src/nxt_isolation.c @@ -126,10 +126,10 @@ nxt_isolation_main_prefork(nxt_task_t *task, nxt_process_t *process, return ret; } - has_mnt = 0; - #if (NXT_HAVE_CLONE_NEWNS) has_mnt = nxt_is_clone_flag_set(process->isolation.clone.flags, NEWNS); +#else + has_mnt = 0; #endif if (process->user_cred->uid == 0 && !has_mnt) { diff --git a/src/nxt_main_process.c b/src/nxt_main_process.c index 10bd2518..16c6a297 100644 --- a/src/nxt_main_process.c +++ b/src/nxt_main_process.c @@ -347,8 +347,6 @@ nxt_port_main_start_process_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) nxt_process_init_t *init; nxt_common_app_conf_t *app_conf; - ret = NXT_ERROR; - rt = task->thread->runtime; process = nxt_main_process_new(task, rt); -- cgit From d16cf0416784db82147ee5aaad1054840d028e7d Mon Sep 17 00:00:00 2001 From: Zhidao HONG Date: Mon, 2 Aug 2021 12:30:38 +0800 Subject: Router: fixed segmentation fault. In the case that routes or upstreams is empty and the pass option is a variable. If the resolved pass is routes or upstreams, a segment error occurred. --- src/nxt_http_route.c | 4 ++++ src/nxt_upstream.c | 4 ++++ 2 files changed, 8 insertions(+) (limited to 'src') diff --git a/src/nxt_http_route.c b/src/nxt_http_route.c index c38acdf3..065b3488 100644 --- a/src/nxt_http_route.c +++ b/src/nxt_http_route.c @@ -1567,6 +1567,10 @@ nxt_http_route_find(nxt_http_routes_t *routes, nxt_str_t *name, { nxt_http_route_t **route, **end; + if (routes == NULL) { + return NXT_DECLINED; + } + route = &routes->route[0]; end = route + routes->items; diff --git a/src/nxt_upstream.c b/src/nxt_upstream.c index 9f81b286..de9b1d49 100644 --- a/src/nxt_upstream.c +++ b/src/nxt_upstream.c @@ -78,6 +78,10 @@ nxt_upstream_find(nxt_upstreams_t *upstreams, nxt_str_t *name, uint32_t i, n; nxt_upstream_t *upstream; + if (upstreams == NULL) { + return NXT_DECLINED; + } + upstream = &upstreams->upstream[0]; n = upstreams->items; -- cgit From 60cf1399611ae1b2728492c94ff57a4a044774b4 Mon Sep 17 00:00:00 2001 From: Oisin Canty Date: Thu, 5 Aug 2021 16:00:01 +0000 Subject: Router: fixed crash when matching an empty address pattern array. A crash would occur when the router tried to match an against an empty address pattern array. The following configuration was used to reproduce the issue: { "listeners": { "127.0.0.1:8082": { "pass": "routes" } }, "routes": [ { "match": { "source": [] }, "action": { "return": 200 } } ] } --- src/nxt_http_route.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src') diff --git a/src/nxt_http_route.c b/src/nxt_http_route.c index 065b3488..b330796f 100644 --- a/src/nxt_http_route.c +++ b/src/nxt_http_route.c @@ -1936,6 +1936,11 @@ nxt_http_route_addr_rule(nxt_http_request_t *r, nxt_http_route_addr_pattern_t *p; n = addr_rule->items; + + if (n == 0) { + return 0; + } + p = &addr_rule->addr_pattern[0] - 1; do { -- cgit From 3580842d34f8543f7bb41551f7a0dec8723289a8 Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Mon, 9 Aug 2021 10:15:00 +0300 Subject: Python: fixing misprint in error message. --- src/python/nxt_python.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/python/nxt_python.c b/src/python/nxt_python.c index bdce68b2..abb04194 100644 --- a/src/python/nxt_python.c +++ b/src/python/nxt_python.c @@ -364,13 +364,13 @@ nxt_python_set_target(nxt_task_t *task, nxt_python_target_t *target, obj = PyDict_GetItemString(PyModule_GetDict(module), callable); if (nxt_slow_path(obj == NULL)) { nxt_alert(task, "Python failed to get \"%s\" from module \"%s\"", - callable, module); + callable, module_name); goto fail; } if (nxt_slow_path(PyCallable_Check(obj) == 0)) { nxt_alert(task, "\"%s\" in module \"%s\" is not a callable object", - callable, module); + callable, module_name); goto fail; } -- cgit From 73ea6a1c3a7a4a7f3ab14230b80e783e57a7d830 Mon Sep 17 00:00:00 2001 From: Oisin Canty Date: Thu, 12 Aug 2021 08:23:09 +0000 Subject: Introduced nxt_sockaddr_parse_optport() for addresses w/o ports. --- src/nxt_http_route_addr.c | 19 +----- src/nxt_sockaddr.c | 156 ++++++++++++++++++++++++++++++++-------------- src/nxt_sockaddr.h | 3 + 3 files changed, 114 insertions(+), 64 deletions(-) (limited to 'src') diff --git a/src/nxt_http_route_addr.c b/src/nxt_http_route_addr.c index 6d4955ed..2907a902 100644 --- a/src/nxt_http_route_addr.c +++ b/src/nxt_http_route_addr.c @@ -8,7 +8,6 @@ #include -static nxt_bool_t nxt_str_looks_like_ipv6(const nxt_str_t *str); #if (NXT_INET6) static nxt_bool_t nxt_valid_ipv6_blocks(u_char *c, size_t len); #endif @@ -57,7 +56,7 @@ nxt_http_route_addr_pattern_parse(nxt_mp_t *mp, goto parse_port; } - if (nxt_str_looks_like_ipv6(&addr)) { + if (nxt_inet6_probe(&addr)) { #if (NXT_INET6) u_char *end; uint8_t i; @@ -304,22 +303,6 @@ parse_port: } -static nxt_bool_t -nxt_str_looks_like_ipv6(const nxt_str_t *str) -{ - u_char *colon, *end; - - colon = nxt_memchr(str->start, ':', str->length); - - if (colon != NULL) { - end = str->start + str->length; - colon = nxt_memchr(colon + 1, ':', end - (colon + 1)); - } - - return (colon != NULL); -} - - #if (NXT_INET6) static nxt_bool_t diff --git a/src/nxt_sockaddr.c b/src/nxt_sockaddr.c index 47ee165f..730428e4 100644 --- a/src/nxt_sockaddr.c +++ b/src/nxt_sockaddr.c @@ -605,10 +605,35 @@ nxt_sockaddr_parse(nxt_mp_t *mp, nxt_str_t *addr) { nxt_sockaddr_t *sa; + sa = nxt_sockaddr_parse_optport(mp, addr); + + if (sa != NULL + && sa->u.sockaddr.sa_family != AF_UNIX + && nxt_sockaddr_port_number(sa) == 0) + { + nxt_thread_log_error(NXT_LOG_ERR, + "The address \"%V\" must specify a port.", addr); + return NULL; + } + + return sa; +} + + +nxt_sockaddr_t * +nxt_sockaddr_parse_optport(nxt_mp_t *mp, nxt_str_t *addr) +{ + nxt_sockaddr_t *sa; + + if (addr->length == 0) { + nxt_thread_log_error(NXT_LOG_ERR, "socket address cannot be empty"); + return NULL; + } + if (addr->length > 6 && nxt_memcmp(addr->start, "unix:", 5) == 0) { sa = nxt_sockaddr_unix_parse(mp, addr); - } else if (addr->length != 0 && addr->start[0] == '[') { + } else if (addr->start[0] == '[' || nxt_inet6_probe(addr)) { sa = nxt_sockaddr_inet6_parse(mp, addr); } else { @@ -703,44 +728,60 @@ nxt_sockaddr_inet6_parse(nxt_mp_t *mp, nxt_str_t *addr) nxt_int_t ret, port; nxt_sockaddr_t *sa; - length = addr->length - 1; - start = addr->start + 1; + if (addr->start[0] == '[') { + length = addr->length - 1; + start = addr->start + 1; - end = nxt_memchr(start, ']', length); - - if (end != NULL) { - sa = nxt_sockaddr_alloc(mp, sizeof(struct sockaddr_in6), - NXT_INET6_ADDR_STR_LEN); - if (nxt_slow_path(sa == NULL)) { + end = nxt_memchr(start, ']', length); + if (nxt_slow_path(end == NULL)) { return NULL; } - ret = nxt_inet6_addr(&sa->u.sockaddr_in6.sin6_addr, start, end - start); + p = end + 1; + + } else { + length = addr->length; + start = addr->start; + end = addr->start + addr->length; + p = NULL; + } - if (nxt_fast_path(ret == NXT_OK)) { - p = end + 1; - length = (start + length) - p; + port = 0; - if (length > 2 && *p == ':') { - port = nxt_int_parse(p + 1, length - 1); + if (p != NULL) { + length = (start + length) - p; - if (port > 0 && port < 65536) { - sa->u.sockaddr_in6.sin6_port = htons((in_port_t) port); - sa->u.sockaddr_in6.sin6_family = AF_INET6; + if (length < 2 || *p != ':') { + nxt_thread_log_error(NXT_LOG_ERR, "invalid IPv6 address in \"%V\"", + addr); + return NULL; + } - return sa; - } - } + port = nxt_int_parse(p + 1, length - 1); + if (port < 1 || port > 65535) { nxt_thread_log_error(NXT_LOG_ERR, "invalid port in \"%V\"", addr); - return NULL; } } - nxt_thread_log_error(NXT_LOG_ERR, "invalid IPv6 address in \"%V\"", addr); + sa = nxt_sockaddr_alloc(mp, sizeof(struct sockaddr_in6), + NXT_INET6_ADDR_STR_LEN); + if (nxt_slow_path(sa == NULL)) { + return NULL; + } - return NULL; + ret = nxt_inet6_addr(&sa->u.sockaddr_in6.sin6_addr, start, end - start); + if (nxt_slow_path(ret != NXT_OK)) { + nxt_thread_log_error(NXT_LOG_ERR, "invalid IPv6 address in \"%V\"", + addr); + return NULL; + } + + sa->u.sockaddr_in6.sin6_family = AF_INET6; + sa->u.sockaddr_in6.sin6_port = htons((in_port_t) port); + + return sa; #else /* !(NXT_INET6) */ @@ -763,41 +804,48 @@ nxt_sockaddr_inet_parse(nxt_mp_t *mp, nxt_str_t *addr) p = nxt_memchr(addr->start, ':', addr->length); - if (nxt_fast_path(p != NULL)) { - inaddr = INADDR_ANY; + if (p == NULL) { + length = addr->length; + + } else { length = p - addr->start; + } - if (length != 1 || addr->start[0] != '*') { - inaddr = nxt_inet_addr(addr->start, length); + inaddr = INADDR_ANY; - if (nxt_slow_path(inaddr == INADDR_NONE)) { - nxt_thread_log_error(NXT_LOG_ERR, "invalid address \"%V\"", - addr); - return NULL; - } + if (length != 1 || addr->start[0] != '*') { + inaddr = nxt_inet_addr(addr->start, length); + if (nxt_slow_path(inaddr == INADDR_NONE)) { + nxt_thread_log_error(NXT_LOG_ERR, "invalid address \"%V\"", addr); + return NULL; } + } + + port = 0; + if (p != NULL) { p++; length = (addr->start + addr->length) - p; - port = nxt_int_parse(p, length); - - if (port > 0 && port < 65536) { - sa = nxt_sockaddr_alloc(mp, sizeof(struct sockaddr_in), - NXT_INET_ADDR_STR_LEN); - if (nxt_fast_path(sa != NULL)) { - sa->u.sockaddr_in.sin_family = AF_INET; - sa->u.sockaddr_in.sin_port = htons((in_port_t) port); - sa->u.sockaddr_in.sin_addr.s_addr = inaddr; - } + port = nxt_int_parse(p, length); - return sa; + if (port < 1 || port > 65535) { + nxt_thread_log_error(NXT_LOG_ERR, "invalid port in \"%V\"", addr); + return NULL; } } - nxt_thread_log_error(NXT_LOG_ERR, "invalid port in \"%V\"", addr); + sa = nxt_sockaddr_alloc(mp, sizeof(struct sockaddr_in), + NXT_INET_ADDR_STR_LEN); + if (nxt_slow_path(sa == NULL)) { + return NULL; + } - return NULL; + sa->u.sockaddr_in.sin_family = AF_INET; + sa->u.sockaddr_in.sin_addr.s_addr = inaddr; + sa->u.sockaddr_in.sin_port = htons((in_port_t) port); + + return sa; } @@ -1320,3 +1368,19 @@ nxt_inet6_addr(struct in6_addr *in6_addr, u_char *buf, size_t length) } #endif + + +nxt_bool_t +nxt_inet6_probe(nxt_str_t *str) +{ + u_char *colon, *end; + + colon = nxt_memchr(str->start, ':', str->length); + + if (colon != NULL) { + end = str->start + str->length; + colon = nxt_memchr(colon + 1, ':', end - (colon + 1)); + } + + return (colon != NULL); +} diff --git a/src/nxt_sockaddr.h b/src/nxt_sockaddr.h index aa4da5d2..a8f1b393 100644 --- a/src/nxt_sockaddr.h +++ b/src/nxt_sockaddr.h @@ -91,12 +91,15 @@ NXT_EXPORT nxt_bool_t nxt_sockaddr_cmp(nxt_sockaddr_t *sa1, NXT_EXPORT size_t nxt_sockaddr_ntop(nxt_sockaddr_t *sa, u_char *buf, u_char *end, nxt_bool_t port); NXT_EXPORT nxt_sockaddr_t *nxt_sockaddr_parse(nxt_mp_t *mp, nxt_str_t *addr); +NXT_EXPORT nxt_sockaddr_t *nxt_sockaddr_parse_optport(nxt_mp_t *mp, + nxt_str_t *addr); NXT_EXPORT void nxt_job_sockaddr_parse(nxt_job_sockaddr_parse_t *jbs); NXT_EXPORT in_addr_t nxt_inet_addr(u_char *buf, size_t len); #if (NXT_INET6) NXT_EXPORT nxt_int_t nxt_inet6_addr(struct in6_addr *in6_addr, u_char *buf, size_t len); #endif +NXT_EXPORT nxt_bool_t nxt_inet6_probe(nxt_str_t *addr); #define NXT_INET_ADDR_STR_LEN nxt_length("255.255.255.255:65535") -- cgit From ca373aaccd276fb412e59557a3971a8d06ada0f8 Mon Sep 17 00:00:00 2001 From: Oisin Canty Date: Thu, 12 Aug 2021 08:23:16 +0000 Subject: Router: client IP address replacement. This commit introduces the replacement of the client address based on the value of a specified HTTP header. This is intended for use when Unit is placed behind a reverse proxy like nginx or a CDN. You must specify the source addresses of the trusted proxies. This can be accomplished with any valid IP pattern supported by Unit's match block: ["10.0.0.1", "10.4.0.0/16", "!192.168.1.1"] The feature is configured per listener. The client address replacement functionality only operates when there is a source IP match and the specified header is present. Typically this would be an 'X-Forwarded-For' header. { "listeners": { "127.0.0.1:8080": { "client_ip": { "header": "X-Forwarded-For", "source": [ "10.0.0.0/8" ] }, "pass": "applications/my_app" }, } } If a request occurs and Unit receives a header like below: "X-Forwarded-For: 84.123.23.23" By default, Unit trusts the last rightmost IP in the header, so REMOTE_ADDR will be set to 84.123.23.23 if the connection originated from 10.0.0.0/8. If Unit runs behind consecutive reverse proxies and receives a header similar to the following: "X-Forwarded-For: 84.123.23.23, 10.0.0.254" You will need to enable "recursive" checking, which walks the header from last address to first and chooses the first non-trusted address it finds. { "listeners": { "127.0.0.1:8080": { "client_ip": { "header": "X-Forwarded-For", "source": [ "10.0.0.0/8" ] "recursive": true, }, "pass": "applications/my_app" }, } } If a connection from 10.0.0.0/8 occurs, the chain is walked. Here, 10.0.0.254 is also a trusted address so the client address will be replaced with 84.123.23.23. If all IP addresses in the header are trusted, the client address is set to the first address in the header: If 10.0.0.0/8 is trusted and "X-Forwarded-For: 10.0.0.3, 10.0.0.2, 10.0.0.1", the client address will be replaced with 10.0.0.3. --- src/nxt_conf_validation.c | 25 ++++++++ src/nxt_http.h | 17 +++++- src/nxt_http_request.c | 150 ++++++++++++++++++++++++++++++++++++++++++++++ src/nxt_http_route.c | 12 ++-- src/nxt_router.c | 86 +++++++++++++++++++++++++- src/nxt_router.h | 3 + 6 files changed, 282 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index 0ec2e811..0106ebc8 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -208,6 +208,7 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_setting_members[]; static nxt_conf_vldt_object_t nxt_conf_vldt_http_members[]; static nxt_conf_vldt_object_t nxt_conf_vldt_websocket_members[]; static nxt_conf_vldt_object_t nxt_conf_vldt_static_members[]; +static nxt_conf_vldt_object_t nxt_conf_vldt_client_ip_members[]; #if (NXT_TLS) static nxt_conf_vldt_object_t nxt_conf_vldt_tls_members[]; static nxt_conf_vldt_object_t nxt_conf_vldt_session_members[]; @@ -351,6 +352,11 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_listener_members[] = { .name = nxt_string("application"), .type = NXT_CONF_VLDT_STRING, .validator = nxt_conf_vldt_app_name, + }, { + .name = nxt_string("client_ip"), + .type = NXT_CONF_VLDT_OBJECT, + .validator = nxt_conf_vldt_object, + .u.members = nxt_conf_vldt_client_ip_members }, #if (NXT_TLS) @@ -366,6 +372,25 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_listener_members[] = { }; +static nxt_conf_vldt_object_t nxt_conf_vldt_client_ip_members[] = { + { + .name = nxt_string("source"), + .type = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY, + .validator = nxt_conf_vldt_match_addrs, + .flags = NXT_CONF_VLDT_REQUIRED + }, { + .name = nxt_string("header"), + .type = NXT_CONF_VLDT_STRING, + .flags = NXT_CONF_VLDT_REQUIRED + }, { + .name = nxt_string("recursive"), + .type = NXT_CONF_VLDT_BOOLEAN, + }, + + NXT_CONF_VLDT_END +}; + + #if (NXT_TLS) static nxt_conf_vldt_object_t nxt_conf_vldt_tls_members[] = { diff --git a/src/nxt_http.h b/src/nxt_http.h index faba83f2..3bc2fd61 100644 --- a/src/nxt_http.h +++ b/src/nxt_http.h @@ -197,8 +197,9 @@ struct nxt_http_request_s { }; -typedef struct nxt_http_route_s nxt_http_route_t; -typedef struct nxt_http_route_rule_s nxt_http_route_rule_t; +typedef struct nxt_http_route_s nxt_http_route_t; +typedef struct nxt_http_route_rule_s nxt_http_route_rule_t; +typedef struct nxt_http_route_addr_rule_s nxt_http_route_addr_rule_t; typedef struct { @@ -254,6 +255,14 @@ typedef struct { } nxt_http_proto_table_t; +struct nxt_http_client_ip_s { + nxt_http_route_addr_rule_t *source; + nxt_str_t *header; + uint32_t header_hash; + uint8_t recursive; /* 1 bit */ +}; + + #define NXT_HTTP_DATE_LEN nxt_length("Wed, 31 Dec 1986 16:40:00 GMT") nxt_inline u_char * @@ -311,6 +320,10 @@ nxt_int_t nxt_http_pass_segments(nxt_mp_t *mp, nxt_str_t *pass, nxt_str_t *segments, nxt_uint_t n); nxt_http_action_t *nxt_http_pass_application(nxt_task_t *task, nxt_router_conf_t *rtcf, nxt_str_t *name); +nxt_http_route_addr_rule_t *nxt_http_route_addr_rule_create( + nxt_task_t *task, nxt_mp_t *mp, nxt_conf_value_t *cv); +nxt_int_t nxt_http_route_addr_rule(nxt_http_request_t *r, + nxt_http_route_addr_rule_t *addr_rule, nxt_sockaddr_t *sockaddr); nxt_http_route_rule_t *nxt_http_route_types_rule_create(nxt_task_t *task, nxt_mp_t *mp, nxt_conf_value_t *types); nxt_int_t nxt_http_route_test_rule(nxt_http_request_t *r, diff --git a/src/nxt_http_request.c b/src/nxt_http_request.c index 16563a98..b71b25d9 100644 --- a/src/nxt_http_request.c +++ b/src/nxt_http_request.c @@ -10,6 +10,10 @@ static nxt_int_t nxt_http_validate_host(nxt_str_t *host, nxt_mp_t *mp); static void nxt_http_request_start(nxt_task_t *task, void *obj, void *data); +static nxt_int_t nxt_http_request_client_ip(nxt_task_t *task, + nxt_http_request_t *r); +static nxt_sockaddr_t *nxt_http_request_client_ip_sockaddr( + nxt_http_request_t *r, u_char *start, size_t len); static void nxt_http_request_ready(nxt_task_t *task, void *obj, void *data); static void nxt_http_request_proto_info(nxt_task_t *task, nxt_http_request_t *r); @@ -272,16 +276,162 @@ static const nxt_http_request_state_t nxt_http_request_init_state static void nxt_http_request_start(nxt_task_t *task, void *obj, void *data) { + nxt_int_t ret; nxt_http_request_t *r; r = obj; r->state = &nxt_http_request_body_state; + ret = nxt_http_request_client_ip(task, r); + if (nxt_slow_path(ret != NXT_OK)) { + nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR); + } + nxt_http_request_read_body(task, r); } +static nxt_int_t +nxt_http_request_client_ip(nxt_task_t *task, nxt_http_request_t *r) +{ + u_char *start, *p; + nxt_int_t ret, i, len; + nxt_str_t *header; + nxt_array_t *fields_arr; /* of nxt_http_field_t * */ + nxt_sockaddr_t *sa, *prev_sa; + nxt_http_field_t *f, **fields; + nxt_http_client_ip_t *client_ip; + + client_ip = r->conf->socket_conf->client_ip; + + if (client_ip == NULL) { + return NXT_OK; + } + + ret = nxt_http_route_addr_rule(r, client_ip->source, r->remote); + if (ret <= 0) { + return NXT_OK; + } + + header = client_ip->header; + + fields_arr = nxt_array_create(r->mem_pool, 2, sizeof(nxt_http_field_t *)); + if (nxt_slow_path(fields_arr == NULL)) { + return NXT_ERROR; + } + + nxt_list_each(f, r->fields) { + if (f->hash == client_ip->header_hash + && f->name_length == client_ip->header->length + && f->value_length > 0 + && nxt_memcasecmp(f->name, header->start, header->length) == 0) + { + fields = nxt_array_add(fields_arr); + if (nxt_slow_path(fields == NULL)) { + return NXT_ERROR; + } + + *fields = f; + } + } nxt_list_loop; + + prev_sa = r->remote; + fields = (nxt_http_field_t **) fields_arr->elts; + + i = fields_arr->nelts; + + while (i-- > 0) { + f = fields[i]; + start = f->value; + len = f->value_length; + + do { + for (p = start + len - 1; p > start; p--, len--) { + if (*p != ' ' && *p != ',') { + break; + } + } + + for (/* void */; p > start; p--) { + if (*p == ' ' || *p == ',') { + p++; + break; + } + } + + sa = nxt_http_request_client_ip_sockaddr(r, p, len - (p - start)); + if (nxt_slow_path(sa == NULL)) { + if (prev_sa != NULL) { + r->remote = prev_sa; + } + + return NXT_OK; + } + + if (!client_ip->recursive) { + r->remote = sa; + + return NXT_OK; + } + + ret = nxt_http_route_addr_rule(r, client_ip->source, sa); + if (ret <= 0 || (i == 0 && p == start)) { + r->remote = sa; + + return NXT_OK; + } + + prev_sa = sa; + len = p - 1 - start; + + } while (len > 0); + } + + return NXT_OK; +} + + +static nxt_sockaddr_t * +nxt_http_request_client_ip_sockaddr(nxt_http_request_t *r, u_char *start, + size_t len) +{ + nxt_str_t addr; + nxt_sockaddr_t *sa; + + addr.start = start; + addr.length = len; + + sa = nxt_sockaddr_parse_optport(r->mem_pool, &addr); + if (nxt_slow_path(sa == NULL)) { + return NULL; + } + + switch (sa->u.sockaddr.sa_family) { + case AF_INET: + if (sa->u.sockaddr_in.sin_addr.s_addr == INADDR_ANY) { + return NULL; + } + + break; + +#if (NXT_INET6) + case AF_INET6: + if (IN6_IS_ADDR_UNSPECIFIED(&sa->u.sockaddr_in6.sin6_addr)) { + return NULL; + } + + break; +#endif /* NXT_INET6 */ + + default: + return NULL; + } + + return sa; +} + + static const nxt_http_request_state_t nxt_http_request_body_state nxt_aligned(64) = { diff --git a/src/nxt_http_route.c b/src/nxt_http_route.c index b330796f..cff69f96 100644 --- a/src/nxt_http_route.c +++ b/src/nxt_http_route.c @@ -135,12 +135,12 @@ typedef struct { } nxt_http_route_table_t; -typedef struct { +struct nxt_http_route_addr_rule_s { /* The object must be the first field. */ nxt_http_route_object_t object:8; uint32_t items; nxt_http_route_addr_pattern_t addr_pattern[0]; -} nxt_http_route_addr_rule_t; +}; typedef union { @@ -194,8 +194,6 @@ static nxt_http_route_ruleset_t *nxt_http_route_ruleset_create(nxt_task_t *task, static nxt_http_route_rule_t *nxt_http_route_rule_name_create(nxt_task_t *task, nxt_mp_t *mp, nxt_conf_value_t *rule_cv, nxt_str_t *name, nxt_bool_t case_sensitive, nxt_http_route_encoding_t encoding); -static nxt_http_route_addr_rule_t *nxt_http_route_addr_rule_create( - nxt_task_t *task, nxt_mp_t *mp, nxt_conf_value_t *cv); static nxt_http_route_rule_t *nxt_http_route_rule_create(nxt_task_t *task, nxt_mp_t *mp, nxt_conf_value_t *cv, nxt_bool_t case_sensitive, nxt_http_route_pattern_case_t pattern_case, @@ -237,8 +235,6 @@ static nxt_int_t nxt_http_route_table(nxt_http_request_t *r, nxt_http_route_table_t *table); static nxt_int_t nxt_http_route_ruleset(nxt_http_request_t *r, nxt_http_route_ruleset_t *ruleset); -static nxt_int_t nxt_http_route_addr_rule(nxt_http_request_t *r, - nxt_http_route_addr_rule_t *addr_rule, nxt_sockaddr_t *sockaddr); static nxt_int_t nxt_http_route_rule(nxt_http_request_t *r, nxt_http_route_rule_t *rule); static nxt_int_t nxt_http_route_header(nxt_http_request_t *r, @@ -940,7 +936,7 @@ nxt_http_route_rule_create(nxt_task_t *task, nxt_mp_t *mp, } -static nxt_http_route_addr_rule_t * +nxt_http_route_addr_rule_t * nxt_http_route_addr_rule_create(nxt_task_t *task, nxt_mp_t *mp, nxt_conf_value_t *cv) { @@ -1927,7 +1923,7 @@ nxt_http_route_addr_pattern_match(nxt_http_route_addr_pattern_t *p, } -static nxt_int_t +nxt_int_t nxt_http_route_addr_rule(nxt_http_request_t *r, nxt_http_route_addr_rule_t *addr_rule, nxt_sockaddr_t *sa) { diff --git a/src/nxt_router.c b/src/nxt_router.c index 8360e75a..1aa919c2 100644 --- a/src/nxt_router.c +++ b/src/nxt_router.c @@ -107,6 +107,9 @@ static nxt_int_t nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, u_char *start, u_char *end); static nxt_int_t nxt_router_conf_process_static(nxt_task_t *task, nxt_router_conf_t *rtcf, nxt_conf_value_t *conf); +static nxt_int_t nxt_router_conf_process_client_ip(nxt_task_t *task, + nxt_router_temp_conf_t *tmcf, nxt_socket_conf_t *skcf, + nxt_conf_value_t *conf); static nxt_app_t *nxt_router_app_find(nxt_queue_t *queue, nxt_str_t *name); static nxt_int_t nxt_router_apps_hash_test(nxt_lvlhsh_query_t *lhq, void *data); @@ -1450,7 +1453,7 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *conf, *http, *value, *websocket; nxt_conf_value_t *applications, *application; nxt_conf_value_t *listeners, *listener; - nxt_conf_value_t *routes_conf, *static_conf; + nxt_conf_value_t *routes_conf, *static_conf, *client_ip_conf; nxt_socket_conf_t *skcf; nxt_http_routes_t *routes; nxt_event_engine_t *engine; @@ -1472,6 +1475,7 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, #endif static nxt_str_t static_path = nxt_string("/settings/http/static"); static nxt_str_t websocket_path = nxt_string("/settings/http/websocket"); + static nxt_str_t client_ip_path = nxt_string("/client_ip"); conf = nxt_conf_json_parse(tmcf->mem_pool, start, end, NULL); if (conf == NULL) { @@ -1843,6 +1847,13 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, t->length = nxt_strlen(t->start); } + client_ip_conf = nxt_conf_get_path(listener, &client_ip_path); + ret = nxt_router_conf_process_client_ip(task, tmcf, skcf, + client_ip_conf); + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } + #if (NXT_TLS) certificate = nxt_conf_get_path(listener, &certificate_path); @@ -2085,6 +2096,79 @@ nxt_router_conf_process_static(nxt_task_t *task, nxt_router_conf_t *rtcf, } +static nxt_int_t +nxt_router_conf_process_client_ip(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, + nxt_socket_conf_t *skcf, nxt_conf_value_t *conf) +{ + char c; + size_t i; + nxt_mp_t *mp; + uint32_t hash; + nxt_str_t header; + nxt_conf_value_t *source_conf, *header_conf, *recursive_conf; + nxt_http_client_ip_t *client_ip; + nxt_http_route_addr_rule_t *source; + + static nxt_str_t header_path = nxt_string("/header"); + static nxt_str_t source_path = nxt_string("/source"); + static nxt_str_t recursive_path = nxt_string("/recursive"); + + if (conf == NULL) { + skcf->client_ip = NULL; + + return NXT_OK; + } + + mp = tmcf->router_conf->mem_pool; + + source_conf = nxt_conf_get_path(conf, &source_path); + header_conf = nxt_conf_get_path(conf, &header_path); + recursive_conf = nxt_conf_get_path(conf, &recursive_path); + + if (source_conf == NULL || header_conf == NULL) { + return NXT_ERROR; + } + + client_ip = nxt_mp_zget(mp, sizeof(nxt_http_client_ip_t)); + if (nxt_slow_path(client_ip == NULL)) { + return NXT_ERROR; + } + + source = nxt_http_route_addr_rule_create(task, mp, source_conf); + if (nxt_slow_path(source == NULL)) { + return NXT_ERROR; + } + + client_ip->source = source; + + nxt_conf_get_string(header_conf, &header); + + if (recursive_conf != NULL) { + client_ip->recursive = nxt_conf_get_boolean(recursive_conf); + } + + client_ip->header = nxt_str_dup(mp, NULL, &header); + if (nxt_slow_path(client_ip->header == NULL)) { + return NXT_ERROR; + } + + hash = NXT_HTTP_FIELD_HASH_INIT; + + for (i = 0; i < client_ip->header->length; i++) { + c = client_ip->header->start[i]; + hash = nxt_http_field_hash_char(hash, nxt_lowcase(c)); + } + + hash = nxt_http_field_hash_end(hash) & 0xFFFF; + + client_ip->header_hash = hash; + + skcf->client_ip = client_ip; + + return NXT_OK; +} + + static nxt_app_t * nxt_router_app_find(nxt_queue_t *queue, nxt_str_t *name) { diff --git a/src/nxt_router.h b/src/nxt_router.h index 6611cf45..fc068b53 100644 --- a/src/nxt_router.h +++ b/src/nxt_router.h @@ -18,6 +18,7 @@ typedef struct nxt_http_request_s nxt_http_request_t; typedef struct nxt_http_action_s nxt_http_action_t; typedef struct nxt_http_routes_s nxt_http_routes_t; +typedef struct nxt_http_client_ip_s nxt_http_client_ip_t; typedef struct nxt_upstream_s nxt_upstream_t; typedef struct nxt_upstreams_s nxt_upstreams_t; typedef struct nxt_router_access_log_s nxt_router_access_log_t; @@ -195,6 +196,8 @@ typedef struct { uint8_t discard_unsafe_fields; /* 1 bit */ + nxt_http_client_ip_t *client_ip; + #if (NXT_TLS) nxt_tls_conf_t *tls; #endif -- cgit From 598f1493f6cbcd2a680b4f01ca7490e06092f5e2 Mon Sep 17 00:00:00 2001 From: Zhidao HONG Date: Thu, 12 Aug 2021 17:41:21 +0800 Subject: Log: renamed related variables "log" as "_log" to prevent conflicts. --- src/nxt_log.h | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/nxt_log.h b/src/nxt_log.h index 48742721..0cf10b5c 100644 --- a/src/nxt_log.h +++ b/src/nxt_log.h @@ -48,29 +48,29 @@ nxt_log_level_enough(log, level) \ #define nxt_alert(task, ...) \ do { \ - nxt_log_t *log = (task)->log; \ + nxt_log_t *_log = (task)->log; \ \ - log->handler(NXT_LOG_ALERT, log, __VA_ARGS__); \ + _log->handler(NXT_LOG_ALERT, _log, __VA_ARGS__); \ } while (0) #define nxt_log(task, _level, ...) \ do { \ - nxt_log_t *log = (task)->log; \ + nxt_log_t *_log = (task)->log; \ nxt_uint_t _level_ = (_level); \ \ - if (nxt_slow_path(log->level >= _level_)) { \ - log->handler(_level_, log, __VA_ARGS__); \ + if (nxt_slow_path(_log->level >= _level_)) { \ + _log->handler(_level_, _log, __VA_ARGS__); \ } \ } while (0) #define nxt_trace(task, ...) \ do { \ - nxt_log_t *log = (task)->log; \ + nxt_log_t *_log = (task)->log; \ \ - if (nxt_slow_path(log->level >= NXT_LOG_NOTICE || nxt_trace)) { \ - log->handler(NXT_LOG_NOTICE, log, __VA_ARGS__); \ + if (nxt_slow_path(_log->level >= NXT_LOG_NOTICE || nxt_trace)) { \ + _log->handler(NXT_LOG_NOTICE, _log, __VA_ARGS__); \ } \ } while (0) @@ -99,10 +99,10 @@ nxt_log_error(_level, _log, ...) \ #define nxt_debug(task, ...) \ do { \ - nxt_log_t *log = (task)->log; \ + nxt_log_t *_log = (task)->log; \ \ - if (nxt_slow_path(log->level == NXT_LOG_DEBUG || nxt_debug)) { \ - log->handler(NXT_LOG_DEBUG, log, __VA_ARGS__); \ + if (nxt_slow_path(_log->level == NXT_LOG_DEBUG || nxt_debug)) { \ + _log->handler(NXT_LOG_DEBUG, _log, __VA_ARGS__); \ } \ } while (0) -- cgit From 48a9399f23b9aaa2c9e5deb8013c58313e76740e Mon Sep 17 00:00:00 2001 From: Zhidao HONG Date: Thu, 12 Aug 2021 17:39:00 +0800 Subject: Introduced the generic API nxt_buf_dummy_completion(). No functional changes. --- src/nxt_buf.h | 6 ++++++ src/nxt_router.c | 14 +++----------- 2 files changed, 9 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/nxt_buf.h b/src/nxt_buf.h index 25e8499a..5121d659 100644 --- a/src/nxt_buf.h +++ b/src/nxt_buf.h @@ -288,4 +288,10 @@ nxt_buf_cpystr(nxt_buf_t *b, const nxt_str_t *str) } +nxt_inline void +nxt_buf_dummy_completion(nxt_task_t *task, void *obj, void *data) +{ +} + + #endif /* _NXT_BUF_H_INCLIDED_ */ diff --git a/src/nxt_router.c b/src/nxt_router.c index 1aa919c2..e0029d13 100644 --- a/src/nxt_router.c +++ b/src/nxt_router.c @@ -237,8 +237,6 @@ static void nxt_router_http_request_error(nxt_task_t *task, void *obj, static void nxt_router_http_request_done(nxt_task_t *task, void *obj, void *data); -static void nxt_router_dummy_buf_completion(nxt_task_t *task, void *obj, - void *data); static void nxt_router_app_prepare_request(nxt_task_t *task, nxt_request_rpc_data_t *req_rpc_data); static nxt_buf_t *nxt_router_prepare_msg(nxt_task_t *task, @@ -2532,7 +2530,7 @@ nxt_router_listen_socket_rpc_create(nxt_task_t *task, goto fail; } - b->completion_handler = nxt_router_dummy_buf_completion; + b->completion_handler = nxt_buf_dummy_completion; b->mem.free = nxt_cpymem(b->mem.free, skcf->listen->sockaddr, size); @@ -2763,7 +2761,7 @@ nxt_router_app_rpc_create(nxt_task_t *task, goto fail; } - b->completion_handler = nxt_router_dummy_buf_completion; + b->completion_handler = nxt_buf_dummy_completion; nxt_buf_cpystr(b, &app->name); *b->mem.free++ = '\0'; @@ -3792,7 +3790,7 @@ nxt_router_access_log_open(nxt_task_t *task, nxt_router_temp_conf_t *tmcf) goto fail; } - b->completion_handler = nxt_router_dummy_buf_completion; + b->completion_handler = nxt_buf_dummy_completion; nxt_buf_cpystr(b, &access_log->path); *b->mem.free++ = '\0'; @@ -5242,12 +5240,6 @@ nxt_router_http_request_done(nxt_task_t *task, void *obj, void *data) } -static void -nxt_router_dummy_buf_completion(nxt_task_t *task, void *obj, void *data) -{ -} - - static void nxt_router_app_prepare_request(nxt_task_t *task, nxt_request_rpc_data_t *req_rpc_data) -- cgit From e0aa132172f03fe7c31484ce7d301813b5dacb89 Mon Sep 17 00:00:00 2001 From: Andrey Suvorov Date: Tue, 17 Aug 2021 16:52:32 -0700 Subject: Added TLS session tickets support. --- src/nxt_conf_validation.c | 73 +++++++++++ src/nxt_openssl.c | 321 ++++++++++++++++++++++++++++++++++++++++++++++ src/nxt_router.c | 4 + src/nxt_tls.h | 21 +++ 4 files changed, 419 insertions(+) (limited to 'src') diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index 0106ebc8..a53fff74 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -99,6 +99,12 @@ static nxt_int_t nxt_conf_vldt_tls_cache_size(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data); static nxt_int_t nxt_conf_vldt_tls_timeout(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data); +#if (NXT_HAVE_OPENSSL_TLSEXT) +static nxt_int_t nxt_conf_vldt_ticket_key(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value, void *data); +static nxt_int_t nxt_conf_vldt_ticket_key_element(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value); +#endif #endif static nxt_int_t nxt_conf_vldt_action(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data); @@ -428,6 +434,17 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_session_members[] = { .name = nxt_string("timeout"), .type = NXT_CONF_VLDT_INTEGER, .validator = nxt_conf_vldt_tls_timeout, + }, { + .name = nxt_string("tickets"), + .type = NXT_CONF_VLDT_STRING + | NXT_CONF_VLDT_ARRAY + | NXT_CONF_VLDT_BOOLEAN, +#if (NXT_HAVE_OPENSSL_TLSEXT) + .validator = nxt_conf_vldt_ticket_key, +#else + .validator = nxt_conf_vldt_unsupported, + .u.string = "tickets", +#endif }, NXT_CONF_VLDT_END @@ -469,6 +486,62 @@ nxt_conf_vldt_tls_timeout(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, #endif +#if (NXT_HAVE_OPENSSL_TLSEXT) + +static nxt_int_t +nxt_conf_vldt_ticket_key(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, + void *data) +{ + if (nxt_conf_type(value) == NXT_CONF_BOOLEAN) { + return NXT_OK; + } + + if (nxt_conf_type(value) == NXT_CONF_ARRAY) { + return nxt_conf_vldt_array_iterator(vldt, value, + &nxt_conf_vldt_ticket_key_element); + } + + /* NXT_CONF_STRING */ + + return nxt_conf_vldt_ticket_key_element(vldt, value); +} + + +static nxt_int_t +nxt_conf_vldt_ticket_key_element(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value) +{ + nxt_str_t key; + nxt_int_t ret; + + if (nxt_conf_type(value) != NXT_CONF_STRING) { + return nxt_conf_vldt_error(vldt, "The \"key\" array must " + "contain only string values."); + } + + nxt_conf_get_string(value, &key); + + ret = nxt_openssl_base64_decode(NULL, 0, key.start, key.length); + if (nxt_slow_path(ret == NXT_ERROR)) { + return NXT_ERROR; + } + + if (ret == NXT_DECLINED) { + return nxt_conf_vldt_error(vldt, "Invalid Base64 format for the ticket " + "key \"%V\".", &key); + } + + if (ret != 48 && ret != 80) { + return nxt_conf_vldt_error(vldt, "Invalid length %d of the ticket " + "key \"%V\". Must be 48 or 80 bytes.", + ret, &key); + } + + return NXT_OK; +} + +#endif + static nxt_conf_vldt_object_t nxt_conf_vldt_route_members[] = { { diff --git a/src/nxt_openssl.c b/src/nxt_openssl.c index 297e11cf..273ca7f4 100644 --- a/src/nxt_openssl.c +++ b/src/nxt_openssl.c @@ -11,6 +11,8 @@ #include #include #include +#include +#include typedef struct { @@ -50,6 +52,12 @@ static nxt_int_t nxt_openssl_chain_file(nxt_task_t *task, SSL_CTX *ctx, static nxt_int_t nxt_ssl_conf_commands(nxt_task_t *task, SSL_CTX *ctx, nxt_conf_value_t *value, nxt_mp_t *mp); #endif +#if (NXT_HAVE_OPENSSL_TLSEXT) +static nxt_int_t nxt_tls_ticket_keys(nxt_task_t *task, SSL_CTX *ctx, + nxt_tls_init_t *tls_init, nxt_mp_t *mp); +static int nxt_tls_ticket_key_callback(SSL *s, unsigned char *name, + unsigned char *iv, EVP_CIPHER_CTX *ectx,HMAC_CTX *hctx, int enc); +#endif static void nxt_ssl_session_cache(SSL_CTX *ctx, size_t cache_size, time_t timeout); static nxt_uint_t nxt_openssl_cert_get_names(nxt_task_t *task, X509 *cert, @@ -350,6 +358,12 @@ nxt_openssl_server_init(nxt_task_t *task, nxt_mp_t *mp, nxt_ssl_session_cache(ctx, tls_init->cache_size, tls_init->timeout); +#if (NXT_HAVE_OPENSSL_TLSEXT) + if (nxt_tls_ticket_keys(task, ctx, tls_init, mp) != NXT_OK) { + goto fail; + } +#endif + SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); if (conf->ca_certificate != NULL) { @@ -587,6 +601,241 @@ fail: #endif +#if (NXT_HAVE_OPENSSL_TLSEXT) + +static nxt_int_t +nxt_tls_ticket_keys(nxt_task_t *task, SSL_CTX *ctx, nxt_tls_init_t *tls_init, + nxt_mp_t *mp) +{ + uint32_t i; + nxt_int_t ret; + nxt_str_t value; + nxt_uint_t count; + nxt_conf_value_t *member, *tickets_conf; + nxt_tls_ticket_t *ticket; + nxt_tls_tickets_t *tickets; + u_char buf[80]; + + tickets_conf = tls_init->tickets_conf; + + if (tickets_conf == NULL) { + goto no_ticket; + } + + if (nxt_conf_type(tickets_conf) == NXT_CONF_BOOLEAN) { + if (nxt_conf_get_boolean(tickets_conf) == 0) { + goto no_ticket; + } + + return NXT_OK; + } + + if (nxt_conf_type(tickets_conf) == NXT_CONF_ARRAY) { + count = nxt_conf_array_elements_count(tickets_conf); + + if (count == 0) { + goto no_ticket; + } + + } else { + /* nxt_conf_type(tickets_conf) == NXT_CONF_STRING */ + count = 1; + } + +#ifdef SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB + + tickets = nxt_mp_get(mp, sizeof(nxt_tls_tickets_t) + + count * sizeof(nxt_tls_ticket_t)); + if (nxt_slow_path(tickets == NULL)) { + return NXT_ERROR; + } + + tickets->count = count; + tls_init->conf->tickets = tickets; + i = 0; + + do { + ticket = &tickets->tickets[i]; + + i++; + + if (nxt_conf_type(tickets_conf) == NXT_CONF_ARRAY) { + member = nxt_conf_get_array_element(tickets_conf, count - i); + if (member == NULL) { + break; + } + + } else { + /* nxt_conf_type(tickets_conf) == NXT_CONF_STRING */ + member = tickets_conf; + } + + nxt_conf_get_string(member, &value); + + ret = nxt_openssl_base64_decode(buf, 80, value.start, value.length); + if (nxt_slow_path(ret == NXT_ERROR)) { + return NXT_ERROR; + } + + if (ret == 48) { + ticket->aes128 = 1; + nxt_memcpy(ticket->aes_key, buf + 16, 16); + nxt_memcpy(ticket->hmac_key, buf + 32, 16); + + } else { + ticket->aes128 = 0; + nxt_memcpy(ticket->hmac_key, buf + 16, 32); + nxt_memcpy(ticket->aes_key, buf + 48, 32); + } + + nxt_memcpy(ticket->name, buf, 16); + } while (i < count); + + if (SSL_CTX_set_tlsext_ticket_key_cb(ctx, nxt_tls_ticket_key_callback) + == 0) + { + nxt_openssl_log_error(task, NXT_LOG_ALERT, + "Unit was built with Session Tickets support, however, " + "now it is linked dynamically to an OpenSSL library " + "which has no tlsext support, therefore Session Tickets " + "are not available"); + + return NXT_ERROR; + } + + return NXT_OK; + +#else + nxt_alert(task, "Setting custom session ticket keys is not supported with " + "this version of OpenSSL library"); + + return NXT_ERROR; + +#endif + +no_ticket: + + SSL_CTX_set_options(ctx, SSL_OP_NO_TICKET); + + return NXT_OK; +} + + +#ifdef SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB + +static int +nxt_tls_ticket_key_callback(SSL *s, unsigned char *name, unsigned char *iv, + EVP_CIPHER_CTX *ectx, HMAC_CTX *hctx, int enc) +{ + size_t size; + nxt_uint_t i; + nxt_conn_t *c; + const EVP_MD *digest; + const EVP_CIPHER *cipher; + nxt_tls_ticket_t *ticket; + nxt_openssl_conn_t *tls; + + c = SSL_get_ex_data(s, nxt_openssl_connection_index); + + if (nxt_slow_path(c == NULL)) { + nxt_thread_log_alert("SSL_get_ex_data() failed"); + return -1; + } + + tls = c->u.tls; + ticket = tls->conf->tickets->tickets; + +#ifdef OPENSSL_NO_SHA256 + digest = EVP_sha1(); +#else + digest = EVP_sha256(); +#endif + + if (enc == 1) { + /* encrypt session ticket */ + + nxt_debug(c->socket.task, "TLS session ticket encrypt"); + + if (ticket[0].aes128 == 1) { + cipher = EVP_aes_128_cbc(); + size = 16; + + } else { + cipher = EVP_aes_256_cbc(); + size = 32; + } + + if (RAND_bytes(iv, EVP_CIPHER_iv_length(cipher)) != 1) { + nxt_openssl_log_error(c->socket.task, NXT_LOG_ALERT, + "RAND_bytes() failed"); + return -1; + } + + if (EVP_EncryptInit_ex(ectx, cipher, NULL, ticket[0].aes_key, iv) + != 1) + { + nxt_openssl_log_error(c->socket.task, NXT_LOG_ALERT, + "EVP_EncryptInit_ex() failed"); + return -1; + } + + if (HMAC_Init_ex(hctx, ticket[0].hmac_key, size, digest, NULL) != 1) { + nxt_openssl_log_error(c->socket.task, NXT_LOG_ALERT, + "HMAC_Init_ex() failed"); + return -1; + } + + nxt_memcpy(name, ticket[0].name, 16); + + return 1; + + } else { + /* decrypt session ticket */ + + for (i = 0; i < tls->conf->tickets->count; i++) { + if (nxt_memcmp(name, ticket[i].name, 16) == 0) { + goto found; + } + } + + nxt_debug(c->socket.task, "TLS session ticket decrypt, key not found"); + + return 0; + + found: + + nxt_debug(c->socket.task, + "TLS session ticket decrypt, key number: \"%d\"", i); + + if (ticket[i].aes128 == 1) { + cipher = EVP_aes_128_cbc(); + size = 16; + + } else { + cipher = EVP_aes_256_cbc(); + size = 32; + } + + if (EVP_DecryptInit_ex(ectx, cipher, NULL, ticket[i].aes_key, iv) != 1) { + nxt_openssl_log_error(c->socket.task, NXT_LOG_ALERT, + "EVP_DecryptInit_ex() failed"); + return -1; + } + + if (HMAC_Init_ex(hctx, ticket[i].hmac_key, size, digest, NULL) != 1) { + nxt_openssl_log_error(c->socket.task, NXT_LOG_ALERT, + "HMAC_Init_ex() failed"); + return -1; + } + + return (i == 0) ? 1 : 2 /* renew */; + } +} + +#endif /* SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB */ + +#endif /* NXT_HAVE_OPENSSL_TLSEXT */ + static void nxt_ssl_session_cache(SSL_CTX *ctx, size_t cache_size, time_t timeout) @@ -904,6 +1153,11 @@ nxt_openssl_server_free(nxt_task_t *task, nxt_tls_conf_t *conf) bundle = bundle->next; } while (bundle != NULL); + if (conf->tickets) { + nxt_memzero(conf->tickets->tickets, + conf->tickets->count * sizeof(nxt_tls_ticket_t)); + } + #if (OPENSSL_VERSION_NUMBER >= 0x1010100fL \ && OPENSSL_VERSION_NUMBER < 0x1010101fL) RAND_keep_random_devices_open(0); @@ -1565,3 +1819,70 @@ nxt_openssl_copy_error(u_char *p, u_char *end) return p; } + + +nxt_int_t +nxt_openssl_base64_decode(u_char *d, size_t dlen, const u_char *s, size_t slen) +{ + BIO *bio, *b64; + nxt_int_t count, ret; + u_char buf[128]; + + b64 = BIO_new(BIO_f_base64()); + if (nxt_slow_path(b64 == NULL)) { + goto error; + } + + bio = BIO_new_mem_buf(s, slen); + if (nxt_slow_path(bio == NULL)) { + goto error; + } + + bio = BIO_push(b64, bio); + + BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); + + count = 0; + + if (d == NULL) { + + for ( ;; ) { + ret = BIO_read(bio, buf, 128); + + if (ret < 0) { + goto invalid; + } + + count += ret; + + if (ret != 128) { + break; + } + } + + } else { + count = BIO_read(bio, d, dlen); + + if (count < 0) { + goto invalid; + } + } + + BIO_free_all(bio); + + return count; + +error: + + BIO_vfree(b64); + ERR_clear_error(); + + return NXT_ERROR; + +invalid: + + BIO_free_all(bio); + ERR_clear_error(); + + return NXT_DECLINED; +} diff --git a/src/nxt_router.c b/src/nxt_router.c index e0029d13..39d375f8 100644 --- a/src/nxt_router.c +++ b/src/nxt_router.c @@ -1470,6 +1470,7 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, static nxt_str_t conf_commands_path = nxt_string("/tls/conf_commands"); static nxt_str_t conf_cache_path = nxt_string("/tls/session/cache_size"); static nxt_str_t conf_timeout_path = nxt_string("/tls/session/timeout"); + static nxt_str_t conf_tickets = nxt_string("/tls/session/tickets"); #endif static nxt_str_t static_path = nxt_string("/settings/http/static"); static nxt_str_t websocket_path = nxt_string("/settings/http/websocket"); @@ -1877,6 +1878,9 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, tls_init->conf_cmds = nxt_conf_get_path(listener, &conf_commands_path); + tls_init->tickets_conf = nxt_conf_get_path(listener, + &conf_tickets); + if (nxt_conf_type(certificate) == NXT_CONF_ARRAY) { n = nxt_conf_array_elements_count(certificate); diff --git a/src/nxt_tls.h b/src/nxt_tls.h index a92f1c21..eeb4e7ba 100644 --- a/src/nxt_tls.h +++ b/src/nxt_tls.h @@ -29,6 +29,8 @@ typedef struct nxt_tls_conf_s nxt_tls_conf_t; typedef struct nxt_tls_bundle_conf_s nxt_tls_bundle_conf_t; typedef struct nxt_tls_init_s nxt_tls_init_t; +typedef struct nxt_tls_ticket_s nxt_tls_ticket_t; +typedef struct nxt_tls_tickets_s nxt_tls_tickets_t; typedef struct { nxt_int_t (*library_init)(nxt_task_t *task); @@ -63,6 +65,8 @@ struct nxt_tls_conf_s { nxt_tls_bundle_conf_t *bundle; nxt_lvlhsh_t bundle_hash; + nxt_tls_tickets_t *tickets; + void (*conn_init)(nxt_task_t *task, nxt_tls_conf_t *conf, nxt_conn_t *c); @@ -82,17 +86,34 @@ struct nxt_tls_init_s { size_t cache_size; nxt_time_t timeout; nxt_conf_value_t *conf_cmds; + nxt_conf_value_t *tickets_conf; nxt_tls_conf_t *conf; }; +struct nxt_tls_ticket_s { + uint8_t aes128; + u_char name[16]; + u_char hmac_key[32]; + u_char aes_key[32]; +}; + + +struct nxt_tls_tickets_s { + nxt_uint_t count; + nxt_tls_ticket_t tickets[]; +}; + + #if (NXT_HAVE_OPENSSL) extern const nxt_tls_lib_t nxt_openssl_lib; void nxt_cdecl nxt_openssl_log_error(nxt_task_t *task, nxt_uint_t level, const char *fmt, ...); u_char *nxt_openssl_copy_error(u_char *p, u_char *end); +nxt_int_t nxt_openssl_base64_decode(u_char *d, size_t dlen, const u_char *s, + size_t slen); #endif #if (NXT_HAVE_GNUTLS) -- cgit