From 0cf0e76a74ce064ee260d8caba1f8b2a517a3b85 Mon Sep 17 00:00:00 2001 From: Andrei Zeliankou Date: Tue, 13 Sep 2022 09:30:30 +0100 Subject: Unit 1.28.0 release. --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index d9fe3e08..79aeaf89 100644 --- a/.hgtags +++ b/.hgtags @@ -35,3 +35,4 @@ f804aaf7eee10a7d8116820840d6312dd4914a41 1.21.0 2be7b623fbfafdb470d832a28abb1cd55c76e04f 1.26.0 1a08f884b24effa8b843d6aeeaf016b6354d1256 1.26.1 8a9055cbe4ffd450fac4d7a849c00e0db5485ad3 1.27.0 +ea073fb3cb75abfb4be5dc12402de73e0c20da60 1.28.0 -- cgit From 5354e05b2f09cbcc6dfeb0242f047ce169b0634b Mon Sep 17 00:00:00 2001 From: Andrei Zeliankou Date: Mon, 19 Sep 2022 11:59:59 +0100 Subject: Version bump. --- docs/changes.xml | 29 +++++++++++++++++++++++++++++ version | 4 ++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/docs/changes.xml b/docs/changes.xml index 391ff2fb..1534a6dc 100644 --- a/docs/changes.xml +++ b/docs/changes.xml @@ -5,6 +5,35 @@ + + + + +NGINX Unit updated to 1.29.0. + + + + + + + + + + + + + +fix HTTP cookie parsing when the value contains an equals sign. + + + diff --git a/src/nxt_http_request.c b/src/nxt_http_request.c index 943ad82d..7a790f73 100644 --- a/src/nxt_http_request.c +++ b/src/nxt_http_request.c @@ -1035,14 +1035,11 @@ nxt_http_cookie_parse(nxt_array_t *cookies, u_char *start, const u_char *end) for (p = start; p < end; p++) { c = *p; - if (c == '=') { + if (c == '=' && name == NULL) { while (start[0] == ' ') { start++; } name_length = p - start; - - if (name_length != 0) { - name = start; - } + name = start; start = p + 1; diff --git a/test/test_routing.py b/test/test_routing.py index 3649b37c..0d7b908c 100644 --- a/test/test_routing.py +++ b/test/test_routing.py @@ -1401,6 +1401,20 @@ class TestRouting(TestApplicationPython): self.route_match_invalid({"cookies": ["var"]}) self.route_match_invalid({"cookies": [{"foo": {}}]}) + def test_routes_match_cookies_complex(self): + self.route_match({"cookies": {"foo": "bar=baz"}}) + self.cookie('foo=bar=baz', 200) + self.cookie(' foo=bar=baz ', 200) + self.cookie('=foo=bar=baz', 404) + + self.route_match({"cookies": {"foo": ""}}) + self.cookie('foo=', 200) + self.cookie('foo=;', 200) + self.cookie(' foo=;', 200) + self.cookie('foo', 404) + self.cookie('', 404) + self.cookie('=', 404) + def test_routes_match_cookies_multiple(self): self.route_match({"cookies": {"foo": "bar", "blah": "blah"}}) -- cgit From 0711101af826cf7f5cb25050587ac6788ac0451c Mon Sep 17 00:00:00 2001 From: Zhidao HONG Date: Thu, 22 Sep 2022 01:01:18 +0800 Subject: Status: fixed error in connection statistics. When proxy is used, the number of accepted connections is not counted, This also results in the wrong number of active connections. --- docs/changes.xml | 6 ++++++ src/nxt_conn.h | 3 +-- src/nxt_conn_close.c | 8 ++++++-- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/docs/changes.xml b/docs/changes.xml index 7c0712f3..e37c8219 100644 --- a/docs/changes.xml +++ b/docs/changes.xml @@ -37,6 +37,12 @@ fix HTTP cookie parsing when the value contains an equals sign. + + +fix error in connection statistics when using proxy. + + + diff --git a/src/nxt_conn.h b/src/nxt_conn.h index 8a703e9a..5717d3c9 100644 --- a/src/nxt_conn.h +++ b/src/nxt_conn.h @@ -312,8 +312,7 @@ NXT_EXPORT void nxt_event_conn_job_sendfile(nxt_task_t *task, \ nxt_queue_remove(&c->link); \ \ - c->idle = 0; \ - e->idle_conns_cnt--; \ + e->idle_conns_cnt -= c->idle; \ } while (0) diff --git a/src/nxt_conn_close.c b/src/nxt_conn_close.c index 92bd8d1b..bdd66951 100644 --- a/src/nxt_conn_close.c +++ b/src/nxt_conn_close.c @@ -119,7 +119,9 @@ nxt_conn_close_handler(nxt_task_t *task, void *obj, void *data) nxt_socket_close(task, c->socket.fd); c->socket.fd = -1; - engine->closed_conns_cnt++; + if (c->idle) { + engine->closed_conns_cnt++; + } if (timers_pending == 0) { nxt_work_queue_add(&engine->fast_work_queue, @@ -155,7 +157,9 @@ nxt_conn_close_timer_handler(nxt_task_t *task, void *obj, void *data) nxt_socket_close(task, c->socket.fd); c->socket.fd = -1; - engine->closed_conns_cnt++; + if (c->idle) { + engine->closed_conns_cnt++; + } } nxt_work_queue_add(&engine->fast_work_queue, c->write_state->ready_handler, -- cgit From 97fa587c9f80ecee7bd2e2be173e700d011b09b9 Mon Sep 17 00:00:00 2001 From: Andrei Zeliankou Date: Tue, 27 Sep 2022 12:08:36 +0100 Subject: Tests: added test with proxy for status. --- test/test_status.py | 95 ++++++++++++++++++++++++++++------------------------- 1 file changed, 51 insertions(+), 44 deletions(-) diff --git a/test/test_status.py b/test/test_status.py index 214072d4..4c5e56ab 100644 --- a/test/test_status.py +++ b/test/test_status.py @@ -9,6 +9,23 @@ from unit.status import Status class TestStatus(TestApplicationPython): prerequisites = {'modules': {'python': 'any'}} + def check_connections(self, accepted, active, idle, closed): + Status.get('/connections') == { + 'accepted': accepted, + 'active': active, + 'idle': idle, + 'closed': closed, + } + + def app_default(self, name="empty", module="wsgi"): + return { + "type": self.get_application_type(), + "processes": {"spare": 0}, + "path": option.test_dir + "/python/" + name, + "working_directory": option.test_dir + "/python/" + name, + "module": module, + } + def test_status(self): assert 'error' in self.conf_delete('/status'), 'DELETE method' @@ -24,13 +41,7 @@ class TestStatus(TestApplicationPython): }, "routes": [{"action": {"return": 200}}], "applications": { - "empty": { - "type": self.get_application_type(), - "processes": {"spare": 0}, - "path": option.test_dir + '/python/empty', - "working_directory": option.test_dir + '/python/empty', - "module": "wsgi", - }, + "empty": self.app_default(), "blah": { "type": self.get_application_type(), "processes": {"spare": 0}, @@ -79,14 +90,6 @@ Connection: close sock.close() def test_status_connections(self): - def check_connections(accepted, active, idle, closed): - Status.get('/connections') == { - 'accepted': accepted, - 'active': active, - 'idle': idle, - 'closed': closed, - } - assert 'success' in self.conf( { "listeners": { @@ -95,14 +98,7 @@ Connection: close }, "routes": [{"action": {"return": 200}}], "applications": { - "delayed": { - "type": self.get_application_type(), - "processes": {"spare": 0}, - "path": option.test_dir + "/python/delayed", - "working_directory": option.test_dir - + "/python/delayed", - "module": "wsgi", - }, + "delayed": self.app_default("delayed"), }, }, ) @@ -112,15 +108,15 @@ Connection: close # accepted, closed assert self.get()['status'] == 200 - check_connections(1, 0, 0, 1) + self.check_connections(1, 0, 0, 1) # idle _, sock = self.http(b'', start=True, raw=True, no_recv=True) - check_connections(2, 0, 1, 1) + self.check_connections(2, 0, 1, 1) self.get(sock=sock) - check_connections(2, 0, 0, 2) + self.check_connections(2, 0, 0, 2) # active @@ -134,10 +130,10 @@ Connection: close start=True, read_timeout=1, ) - check_connections(3, 1, 0, 2) + self.check_connections(3, 1, 0, 2) self.get(sock=sock) - check_connections(3, 0, 0, 3) + self.check_connections(3, 0, 0, 3) def test_status_applications(self): def check_applications(expert): @@ -192,22 +188,8 @@ Connection: close }, "routes": [], "applications": { - "restart": { - "type": self.get_application_type(), - "processes": {"spare": 0}, - "path": option.test_dir + "/python/restart", - "working_directory": option.test_dir - + "/python/restart", - "module": "longstart", - }, - "delayed": { - "type": self.get_application_type(), - "processes": {"spare": 0}, - "path": option.test_dir + "/python/delayed", - "working_directory": option.test_dir - + "/python/delayed", - "module": "wsgi", - }, + "restart": self.app_default("restart", "longstart"), + "delayed": self.app_default("delayed"), }, }, ) @@ -221,3 +203,28 @@ Connection: close check_application('restart', 0, 1, 0, 1) check_application('delayed', 0, 0, 0, 0) + + def test_status_proxy(self): + assert 'success' in self.conf( + { + "listeners": { + "*:7080": {"pass": "routes"}, + "*:7081": {"pass": "applications/empty"}, + }, + "routes": [ + { + "match": {"uri": "/"}, + "action": {"proxy": "http://127.0.0.1:7081"}, + } + ], + "applications": { + "empty": self.app_default(), + }, + }, + ) + + Status.init() + + assert self.get()['status'] == 200 + self.check_connections(2, 0, 0, 2) + assert Status.get('/requests/total') == 2, 'proxy' -- cgit From 2e69b7eb5712e03a198caa66240c2002175c5ed9 Mon Sep 17 00:00:00 2001 From: Andrew Clayton Date: Thu, 22 Sep 2022 21:21:22 +0100 Subject: Added a .mailmap file. This file is used by git (maybe also hg) to map different identities. It can be used to map different names/email addresses to a preferred name/email. So if you have authored/committed under different names/email addresses you can map all these to a particular name/email. Certain git commands (log, shortlog, show, blame etc) will consult this file and show the Author/Committer accordingly. Note: This does _not_ change history, this is simply used by various commands to alter their output and can be disabled for some commands with the --no-mailmap option. This can be useful for commands like git shortlog so that all your commits are shown under a single identity and also so people have an up to date email address should they wish to contact you. And just for overall consistency. Seeing as I've already committed under two different email addresses (not counting this one), I've put entries in this file to map my email addresses to my @nginx.com one. See also, gitmailmap(5). Acked-by: Alex Colomar Signed-off-by: Andrew Clayton --- .mailmap | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .mailmap diff --git a/.mailmap b/.mailmap new file mode 100644 index 00000000..b96dcabc --- /dev/null +++ b/.mailmap @@ -0,0 +1,2 @@ + + -- cgit From 57fc9201cb91e3d8901a64e3daaaf31684ee5bf5 Mon Sep 17 00:00:00 2001 From: Andrew Clayton Date: Sun, 7 Aug 2022 00:19:24 +0100 Subject: Socket: Created control socket & pid file directories. @alejandro-colomar reported an issue on GitHub whereby Unit would fail to start due to not being able to create the control socket (a Unix Domain Socket) 2022/08/05 20:12:22 [alert] 21613#21613 bind(6, unix:/opt/local/unit/var/run/unit/control.unit.sock.tmp) failed (2: No such file or directory) This could happen if the control socket was set to a directory that doesn't exist. A common place to put the control socket would be under /run/unit, and while /run will exist, /run/unit may well not (/run is/should be cleared on each boot). The pid file would also generally go under /run/unit, though this is created after the control socket, however it could go someplace else so we should also ensure its directory exists. This commit will try to create the pid file and control sockets parent directory. In some cases the user will need to ensure that the rest of the path already exists. This adds a new nxt_fs_mkdir_parent() function that given a full path to a file (or directory), strips the last component off before passing the remaining directory path to nxt_fs_mkdir(). Cc: Konstantin Pavlov Closes: Reported-by: Alejandro Colomar Reviewed-by: Alejandro Colomar Tested-by: Alejandro Colomar Signed-off-by: Andrew Clayton --- src/nxt_controller.c | 8 ++++++++ src/nxt_fs.c | 25 +++++++++++++++++++++++++ src/nxt_fs.h | 1 + src/nxt_runtime.c | 2 ++ 4 files changed, 36 insertions(+) diff --git a/src/nxt_controller.c b/src/nxt_controller.c index 09168821..99c7ca10 100644 --- a/src/nxt_controller.c +++ b/src/nxt_controller.c @@ -666,6 +666,14 @@ nxt_runtime_controller_socket(nxt_task_t *task, nxt_runtime_t *rt) #endif ls->handler = nxt_controller_conn_init; +#if (NXT_HAVE_UNIX_DOMAIN) + if (ls->sockaddr->u.sockaddr.sa_family == AF_UNIX) { + const char *path = ls->sockaddr->u.sockaddr_un.sun_path; + + nxt_fs_mkdir_parent((const u_char *) path, 0755); + } +#endif + if (nxt_listen_socket_create(task, rt->mem_pool, ls) != NXT_OK) { return NXT_ERROR; } diff --git a/src/nxt_fs.c b/src/nxt_fs.c index 71498f99..35850798 100644 --- a/src/nxt_fs.c +++ b/src/nxt_fs.c @@ -273,6 +273,31 @@ nxt_fs_mkdir_all(const u_char *dir, mode_t mode) } +nxt_int_t +nxt_fs_mkdir_parent(const u_char *path, mode_t mode) +{ + char *ptr, *dir; + nxt_int_t ret; + + dir = nxt_strdup(path); + if (nxt_slow_path(dir == NULL)) { + return NXT_ERROR; + } + + ret = NXT_OK; + + ptr = strrchr(dir, '/'); + if (nxt_fast_path(ptr != NULL)) { + *ptr = '\0'; + ret = nxt_fs_mkdir((const u_char *) dir, mode); + } + + nxt_free(dir); + + return ret; +} + + static nxt_int_t nxt_fs_mkdir(const u_char *dir, mode_t mode) { diff --git a/src/nxt_fs.h b/src/nxt_fs.h index ff589979..af9585b8 100644 --- a/src/nxt_fs.h +++ b/src/nxt_fs.h @@ -36,6 +36,7 @@ typedef struct { } nxt_fs_mount_t; +nxt_int_t nxt_fs_mkdir_parent(const u_char *path, mode_t mode); nxt_int_t nxt_fs_mkdir_all(const u_char *dir, mode_t mode); nxt_int_t nxt_fs_mount(nxt_task_t *task, nxt_fs_mount_t *mnt); void nxt_fs_unmount(const u_char *path); diff --git a/src/nxt_runtime.c b/src/nxt_runtime.c index d9c9f2ef..c7e4455e 100644 --- a/src/nxt_runtime.c +++ b/src/nxt_runtime.c @@ -1369,6 +1369,8 @@ nxt_runtime_pid_file_create(nxt_task_t *task, nxt_file_name_t *pid_file) file.name = pid_file; + nxt_fs_mkdir_parent(pid_file, 0755); + n = nxt_file_open(task, &file, O_WRONLY, O_CREAT | O_TRUNC, NXT_FILE_DEFAULT_ACCESS); -- cgit From b00983369be5f356280168b4c5d600bd7d614c60 Mon Sep 17 00:00:00 2001 From: Andrew Clayton Date: Thu, 29 Sep 2022 20:59:43 +0100 Subject: Renamed a couple of members of nxt_unit_request_t. This is a preparatory patch that renames the 'local' and 'local_length' members of the nxt_unit_request_t structure to 'local_addr' and 'local_addr_length' in preparation for the adding of 'local_port' and 'local_port_length' members. Suggested-by: Zhidao HONG Signed-off-by: Andrew Clayton --- src/java/nxt_jni_Request.c | 20 ++++++++++---------- src/nodejs/unit-http/unit.cpp | 3 ++- src/nxt_php_sapi.c | 2 +- src/nxt_router.c | 4 ++-- src/nxt_unit_request.h | 4 ++-- src/perl/nxt_perl_psgi.c | 2 +- src/python/nxt_python_asgi.c | 2 +- src/python/nxt_python_wsgi.c | 4 ++-- src/ruby/nxt_ruby.c | 4 ++-- src/test/nxt_unit_app_test.c | 2 +- 10 files changed, 24 insertions(+), 23 deletions(-) diff --git a/src/java/nxt_jni_Request.c b/src/java/nxt_jni_Request.c index 2e9dce67..980a26b6 100644 --- a/src/java/nxt_jni_Request.c +++ b/src/java/nxt_jni_Request.c @@ -461,8 +461,8 @@ nxt_java_Request_getLocalAddr(JNIEnv *env, jclass cls, jlong req_ptr) r = nxt_jlong2ptr(req_ptr); - return nxt_java_newString(env, nxt_unit_sptr_get(&r->local), - r->local_length); + return nxt_java_newString(env, nxt_unit_sptr_get(&r->local_addr), + r->local_addr_length); } @@ -474,11 +474,11 @@ nxt_java_Request_getLocalName(JNIEnv *env, jclass cls, jlong req_ptr) r = nxt_jlong2ptr(req_ptr); - local = nxt_unit_sptr_get(&r->local); - colon = memchr(local, ':', r->local_length); + local = nxt_unit_sptr_get(&r->local_addr); + colon = memchr(local, ':', r->local_addr_length); if (colon == NULL) { - colon = local + r->local_length; + colon = local + r->local_addr_length; } return nxt_java_newString(env, local, colon - local); @@ -494,20 +494,20 @@ nxt_java_Request_getLocalPort(JNIEnv *env, jclass cls, jlong req_ptr) r = nxt_jlong2ptr(req_ptr); - local = nxt_unit_sptr_get(&r->local); - colon = memchr(local, ':', r->local_length); + local = nxt_unit_sptr_get(&r->local_addr); + colon = memchr(local, ':', r->local_addr_length); if (colon == NULL) { return 80; } - tmp = local[r->local_length]; + tmp = local[r->local_addr_length]; - local[r->local_length] = '\0'; + local[r->local_addr_length] = '\0'; res = strtol(colon + 1, NULL, 10); - local[r->local_length] = tmp; + local[r->local_addr_length] = tmp; return res; } diff --git a/src/nodejs/unit-http/unit.cpp b/src/nodejs/unit-http/unit.cpp index ee5dc46f..7912d0ac 100644 --- a/src/nodejs/unit-http/unit.cpp +++ b/src/nodejs/unit-http/unit.cpp @@ -657,7 +657,8 @@ Unit::create_socket(napi_value server_obj, nxt_unit_request_info_t *req) req_data->sock_ref = wrap(res, req, sock_destroy); set_named_property(res, "remoteAddress", r->remote, r->remote_length); - set_named_property(res, "localAddress", r->local, r->local_length); + set_named_property(res, "localAddress", r->local_addr, + r->local_addr_length); return res; } diff --git a/src/nxt_php_sapi.c b/src/nxt_php_sapi.c index 68ef07eb..42fdbc68 100644 --- a/src/nxt_php_sapi.c +++ b/src/nxt_php_sapi.c @@ -1365,7 +1365,7 @@ nxt_php_register_variables(zval *track_vars_array TSRMLS_DC) nxt_php_set_sptr(req, "REMOTE_ADDR", &r->remote, r->remote_length, track_vars_array TSRMLS_CC); - nxt_php_set_sptr(req, "SERVER_ADDR", &r->local, r->local_length, + nxt_php_set_sptr(req, "SERVER_ADDR", &r->local_addr, r->local_addr_length, track_vars_array TSRMLS_CC); nxt_php_set_sptr(req, "SERVER_NAME", &r->server_name, r->server_name_length, diff --git a/src/nxt_router.c b/src/nxt_router.c index f02bf3f2..6430aea4 100644 --- a/src/nxt_router.c +++ b/src/nxt_router.c @@ -5249,8 +5249,8 @@ nxt_router_prepare_msg(nxt_task_t *task, nxt_http_request_t *r, r->remote->address_length); *p++ = '\0'; - req->local_length = r->local->address_length; - nxt_unit_sptr_set(&req->local, p); + req->local_addr_length = r->local->address_length; + nxt_unit_sptr_set(&req->local_addr, p); p = nxt_cpymem(p, nxt_sockaddr_address(r->local), r->local->address_length); *p++ = '\0'; diff --git a/src/nxt_unit_request.h b/src/nxt_unit_request.h index 5dbf648d..f6b96838 100644 --- a/src/nxt_unit_request.h +++ b/src/nxt_unit_request.h @@ -18,7 +18,7 @@ struct nxt_unit_request_s { uint8_t method_length; uint8_t version_length; uint8_t remote_length; - uint8_t local_length; + uint8_t local_addr_length; uint8_t tls; uint8_t websocket_handshake; uint8_t app_target; @@ -38,7 +38,7 @@ struct nxt_unit_request_s { nxt_unit_sptr_t method; nxt_unit_sptr_t version; nxt_unit_sptr_t remote; - nxt_unit_sptr_t local; + nxt_unit_sptr_t local_addr; nxt_unit_sptr_t server_name; nxt_unit_sptr_t target; nxt_unit_sptr_t path; diff --git a/src/perl/nxt_perl_psgi.c b/src/perl/nxt_perl_psgi.c index 08a6f29e..0c1c1222 100644 --- a/src/perl/nxt_perl_psgi.c +++ b/src/perl/nxt_perl_psgi.c @@ -671,7 +671,7 @@ nxt_perl_psgi_env_create(PerlInterpreter *my_perl, RC(nxt_perl_psgi_add_sptr(my_perl, hash_env, NL("REMOTE_ADDR"), &r->remote, r->remote_length)); RC(nxt_perl_psgi_add_sptr(my_perl, hash_env, NL("SERVER_ADDR"), - &r->local, r->local_length)); + &r->local_addr, r->local_addr_length)); RC(nxt_perl_psgi_add_sptr(my_perl, hash_env, NL("SERVER_NAME"), &r->server_name, r->server_name_length)); diff --git a/src/python/nxt_python_asgi.c b/src/python/nxt_python_asgi.c index 4ad0857d..40ea5e24 100644 --- a/src/python/nxt_python_asgi.c +++ b/src/python/nxt_python_asgi.c @@ -674,7 +674,7 @@ nxt_py_asgi_create_http_scope(nxt_unit_request_info_t *req) SET_ITEM(scope, client, v) Py_DECREF(v); - v = nxt_py_asgi_create_address(&r->local, r->local_length, 80); + v = nxt_py_asgi_create_address(&r->local_addr, r->local_addr_length, 80); if (nxt_slow_path(v == NULL)) { nxt_unit_req_alert(req, "Python failed to create 'server' pair"); goto fail; diff --git a/src/python/nxt_python_wsgi.c b/src/python/nxt_python_wsgi.c index 87dcfaa2..3fb6ac3b 100644 --- a/src/python/nxt_python_wsgi.c +++ b/src/python/nxt_python_wsgi.c @@ -609,8 +609,8 @@ nxt_python_get_environ(nxt_python_ctx_t *pctx) RC(nxt_python_add_sptr(pctx, nxt_py_remote_addr_str, &r->remote, r->remote_length)); - RC(nxt_python_add_sptr(pctx, nxt_py_server_addr_str, &r->local, - r->local_length)); + RC(nxt_python_add_sptr(pctx, nxt_py_server_addr_str, &r->local_addr, + r->local_addr_length)); if (r->tls) { RC(nxt_python_add_obj(pctx, nxt_py_wsgi_uri_scheme_str, diff --git a/src/ruby/nxt_ruby.c b/src/ruby/nxt_ruby.c index f316d8a5..1b55e7ef 100644 --- a/src/ruby/nxt_ruby.c +++ b/src/ruby/nxt_ruby.c @@ -747,8 +747,8 @@ nxt_ruby_read_request(nxt_unit_request_info_t *req, VALUE hash_env) r->version_length); nxt_ruby_add_sptr(hash_env, nxt_rb_remote_addr_str, &r->remote, r->remote_length); - nxt_ruby_add_sptr(hash_env, nxt_rb_server_addr_str, &r->local, - r->local_length); + nxt_ruby_add_sptr(hash_env, nxt_rb_server_addr_str, &r->local_addr, + r->local_addr_length); nxt_ruby_add_sptr(hash_env, nxt_rb_server_name_str, &r->server_name, r->server_name_length); diff --git a/src/test/nxt_unit_app_test.c b/src/test/nxt_unit_app_test.c index 8fda9740..d83bd83a 100644 --- a/src/test/nxt_unit_app_test.c +++ b/src/test/nxt_unit_app_test.c @@ -225,7 +225,7 @@ greeting_app_request_handler(nxt_unit_request_info_t *req) *p++ = '\n'; p = copy(p, LOCAL_ADDR, nxt_length(LOCAL_ADDR)); - p = copy(p, nxt_unit_sptr_get(&r->local), r->local_length); + p = copy(p, nxt_unit_sptr_get(&r->local_addr), r->local_addr_length); *p++ = '\n'; p = copy(p, TARGET, nxt_length(TARGET)); -- cgit From dc9f592d6e7123d57924146dcbf1be80366bc98b Mon Sep 17 00:00:00 2001 From: Zhidao HONG Date: Wed, 28 Sep 2022 15:51:55 +0100 Subject: Ruby: added support for rack V3. Ruby applications would fail to start if they were using rack v3 2022/09/28 15:48:46 [alert] 0#80912 [unit] Ruby: Failed to parse rack script 2022/09/28 15:48:46 [notice] 80911#80911 app process 80912 exited with code 1 This was due to a change in the rack API Rack V2 def self.load_file(path, opts = Server::Options.new) ... cfgfile.sub!(/^__END__\n.*\Z/m, '') app = new_from_string cfgfile, path return app, options end Rack V3 def self.load_file(path) ... return new_from_string(config, path) end This patch handles _both_ the above APIs by correctly handling the cases where we do and don't get an array returned from nxt_ruby_rack_parse_script(). Closes: Tested-by: Andrew Clayton Reviewed-by: Andrew Clayton [ Andrew: Patch by Zhidao, commit message by me with input from Zhidao ] Signed-off-by: Andrew Clayton --- docs/changes.xml | 6 ++++++ src/ruby/nxt_ruby.c | 7 ++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/changes.xml b/docs/changes.xml index e37c8219..8f871eb1 100644 --- a/docs/changes.xml +++ b/docs/changes.xml @@ -31,6 +31,12 @@ NGINX Unit updated to 1.29.0. date="" time="" packager="Nginx Packaging <nginx-packaging@f5.com>"> + + +support rack v3 in ruby applications. + + + fix HTTP cookie parsing when the value contains an equals sign. diff --git a/src/ruby/nxt_ruby.c b/src/ruby/nxt_ruby.c index 1b55e7ef..cc287e0e 100644 --- a/src/ruby/nxt_ruby.c +++ b/src/ruby/nxt_ruby.c @@ -480,12 +480,17 @@ nxt_ruby_rack_init(nxt_ruby_rack_init_t *rack_init) rackup = rb_protect(nxt_ruby_rack_parse_script, (VALUE) (uintptr_t) rack_init, &state); - if (nxt_slow_path(TYPE(rackup) != T_ARRAY || state != 0)) { + + if (nxt_slow_path(state != 0)) { nxt_ruby_exception_log(NULL, NXT_LOG_ALERT, "Failed to parse rack script"); return Qnil; } + if (TYPE(rackup) != T_ARRAY) { + return rackup; + } + if (nxt_slow_path(RARRAY_LEN(rackup) < 1)) { nxt_alert(rack_init->task, "Ruby: Invalid rack config file"); return Qnil; -- cgit From d806a90609e99cb4c8db71d620d5db3d26e83693 Mon Sep 17 00:00:00 2001 From: Andrew Clayton Date: Mon, 3 Oct 2022 13:01:33 +0100 Subject: Ruby: used nxt_ruby_exception_log() in nxt_ruby_rack_init(). For consistency use nxt_ruby_exception_log() rather than nxt_alert() in nxt_ruby_rack_init(). Signed-off-by: Andrew Clayton --- src/ruby/nxt_ruby.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ruby/nxt_ruby.c b/src/ruby/nxt_ruby.c index cc287e0e..bcb48f6b 100644 --- a/src/ruby/nxt_ruby.c +++ b/src/ruby/nxt_ruby.c @@ -492,7 +492,7 @@ nxt_ruby_rack_init(nxt_ruby_rack_init_t *rack_init) } if (nxt_slow_path(RARRAY_LEN(rackup) < 1)) { - nxt_alert(rack_init->task, "Ruby: Invalid rack config file"); + nxt_ruby_exception_log(NULL, NXT_LOG_ALERT, "Invalid rack config file"); return Qnil; } -- cgit From 8d976cac522d5223fbe4a4360368a351a197ddbf Mon Sep 17 00:00:00 2001 From: Artem Konev Date: Thu, 6 Oct 2022 12:47:47 +0100 Subject: Added missing slashes in config section references in README.md. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 039200d6..61d5dd45 100644 --- a/README.md +++ b/README.md @@ -91,7 +91,7 @@ application object. Let's store our first config snippet in a file called Saving it as a file isn't necessary, but can come in handy with larger objects. -Now, `PUT` it into the `config/applications` section of Unit's control API, +Now, `PUT` it into the `/config/applications` section of Unit's control API, usually available by default via a Unix domain socket: ``` console @@ -105,7 +105,7 @@ usually available by default via a Unix domain socket: } ``` -Next, reference the app from a listener object in the `config/listeners` +Next, reference the app from a listener object in the `/config/listeners` section of the API. This time, we pass the config snippet straight from the command line: -- cgit From 8e1e0471914b39da0634a23c4231ce98021a4cf7 Mon Sep 17 00:00:00 2001 From: Andrei Zeliankou Date: Tue, 11 Oct 2022 13:49:10 +0100 Subject: Tests: don't try to return response when "no_recv" is True. --- test/test_asgi_application.py | 6 ++---- test/test_java_application.py | 3 +-- test/test_perl_application.py | 3 +-- test/test_proxy.py | 25 +++++++------------------ test/test_python_application.py | 3 +-- test/test_reconfigure.py | 5 ++--- test/test_routing.py | 2 +- test/test_ruby_application.py | 3 +-- test/test_settings.py | 8 +++----- test/test_static.py | 4 ++-- test/test_status.py | 4 ++-- test/test_upstreams_rr.py | 8 +++----- test/unit/applications/websockets.py | 3 +-- test/unit/http.py | 3 +++ 14 files changed, 30 insertions(+), 50 deletions(-) diff --git a/test/test_asgi_application.py b/test/test_asgi_application.py index 34dfe18e..7f5f1578 100644 --- a/test/test_asgi_application.py +++ b/test/test_asgi_application.py @@ -277,10 +277,9 @@ custom-header: BLAH assert self.get()['status'] == 200, 'init' - (_, sock) = self.http( + sock = self.http( b"""GET / HTTP/1.1 """, - start=True, raw=True, no_recv=True, ) @@ -358,14 +357,13 @@ Connection: close socks = [] for i in range(2): - (_, sock) = self.get( + sock = self.get( headers={ 'Host': 'localhost', 'X-Delay': '3', 'Connection': 'close', }, no_recv=True, - start=True, ) socks.append(sock) diff --git a/test/test_java_application.py b/test/test_java_application.py index adcb4eca..b4dbff59 100644 --- a/test/test_java_application.py +++ b/test/test_java_application.py @@ -1001,14 +1001,13 @@ class TestJavaApplication(TestApplicationJava): socks = [] for i in range(4): - (_, sock) = self.get( + sock = self.get( headers={ 'Host': 'localhost', 'X-Delay': '2', 'Connection': 'close', }, no_recv=True, - start=True, ) socks.append(sock) diff --git a/test/test_perl_application.py b/test/test_perl_application.py index 0d1d7906..fe2db72e 100644 --- a/test/test_perl_application.py +++ b/test/test_perl_application.py @@ -259,14 +259,13 @@ class TestPerlApplication(TestApplicationPerl): socks = [] for i in range(4): - (_, sock) = self.get( + sock = self.get( headers={ 'Host': 'localhost', 'X-Delay': '2', 'Connection': 'close', }, no_recv=True, - start=True, ) socks.append(sock) diff --git a/test/test_proxy.py b/test/test_proxy.py index b0d471e4..ede91fd6 100644 --- a/test/test_proxy.py +++ b/test/test_proxy.py @@ -185,9 +185,8 @@ Content-Length: 10 socks = [] for i in range(10): - _, sock = self.post_http10( + sock = self.post_http10( body=payload + str(i), - start=True, no_recv=True, read_buffer_size=buff_size, ) @@ -248,9 +247,7 @@ Content-Length: 10 ), 'custom header 5' def test_proxy_fragmented(self): - _, sock = self.http( - b"""GET / HTT""", raw=True, start=True, no_recv=True - ) + sock = self.http(b"""GET / HTT""", raw=True, no_recv=True) time.sleep(1) @@ -266,9 +263,7 @@ Content-Length: 10 sock.close() def test_proxy_fragmented_close(self): - _, sock = self.http( - b"""GET / HTT""", raw=True, start=True, no_recv=True - ) + sock = self.http(b"""GET / HTT""", raw=True, no_recv=True) time.sleep(1) @@ -277,9 +272,7 @@ Content-Length: 10 sock.close() def test_proxy_fragmented_body(self): - _, sock = self.http( - b"""GET / HTT""", raw=True, start=True, no_recv=True - ) + sock = self.http(b"""GET / HTT""", raw=True, no_recv=True) time.sleep(1) @@ -306,9 +299,7 @@ Content-Length: 10 assert resp['body'] == "X" * 30000, 'body' def test_proxy_fragmented_body_close(self): - _, sock = self.http( - b"""GET / HTT""", raw=True, start=True, no_recv=True - ) + sock = self.http(b"""GET / HTT""", raw=True, no_recv=True) time.sleep(1) @@ -398,7 +389,7 @@ Content-Length: 10 {"pass": "applications/delayed"}, 'listeners/*:7081' ), 'delayed configure' - _, sock = self.post_http10( + sock = self.post_http10( headers={ 'Host': 'localhost', 'Content-Length': '10000', @@ -406,14 +397,13 @@ Content-Length: 10 'X-Delay': '1', }, body='0123456789' * 1000, - start=True, no_recv=True, ) assert re.search('200 OK', sock.recv(100).decode()), 'first' sock.close() - _, sock = self.post_http10( + sock = self.post_http10( headers={ 'Host': 'localhost', 'Content-Length': '10000', @@ -421,7 +411,6 @@ Content-Length: 10 'X-Delay': '1', }, body='0123456789' * 1000, - start=True, no_recv=True, ) diff --git a/test/test_python_application.py b/test/test_python_application.py index 2ea9a22e..946d2118 100644 --- a/test/test_python_application.py +++ b/test/test_python_application.py @@ -765,14 +765,13 @@ last line: 987654321 socks = [] for i in range(4): - (_, sock) = self.get( + sock = self.get( headers={ 'Host': 'localhost', 'X-Delay': '2', 'Connection': 'close', }, no_recv=True, - start=True, ) socks.append(sock) diff --git a/test/test_reconfigure.py b/test/test_reconfigure.py index ab05a1c8..feb027aa 100644 --- a/test/test_reconfigure.py +++ b/test/test_reconfigure.py @@ -21,10 +21,9 @@ class TestReconfigure(TestApplicationProto): assert 'success' in self.conf({"listeners": {}, "applications": {}}) def test_reconfigure(self): - (_, sock) = self.http( + sock = self.http( b"""GET / HTTP/1.1 """, - start=True, raw=True, no_recv=True, ) @@ -42,7 +41,7 @@ Connection: close assert resp['status'] == 200, 'finish request' def test_reconfigure_2(self): - (_, sock) = self.http(b'', raw=True, start=True, no_recv=True) + sock = self.http(b'', raw=True, no_recv=True) # Waiting for connection completion. # Delay should be more than TCP_DEFER_ACCEPT. diff --git a/test/test_routing.py b/test/test_routing.py index 0d7b908c..9e872061 100644 --- a/test/test_routing.py +++ b/test/test_routing.py @@ -1485,7 +1485,7 @@ class TestRouting(TestApplicationPython): def test_routes_source_port(self): def sock_port(): - _, sock = self.http(b'', start=True, raw=True, no_recv=True) + sock = self.http(b'', raw=True, no_recv=True) port = sock.getsockname()[1] return (sock, port) diff --git a/test/test_ruby_application.py b/test/test_ruby_application.py index 83af39be..068b587b 100644 --- a/test/test_ruby_application.py +++ b/test/test_ruby_application.py @@ -388,14 +388,13 @@ class TestRubyApplication(TestApplicationRuby): socks = [] for i in range(4): - (_, sock) = self.get( + sock = self.get( headers={ 'Host': 'localhost', 'X-Delay': '2', 'Connection': 'close', }, no_recv=True, - start=True, ) socks.append(sock) diff --git a/test/test_settings.py b/test/test_settings.py index ea3cfb99..1bc9a432 100644 --- a/test/test_settings.py +++ b/test/test_settings.py @@ -50,20 +50,18 @@ Connection: close {'http': {'header_read_timeout': 4}}, 'settings' ) - (resp, sock) = self.http( + sock = self.http( b"""GET / HTTP/1.1 """, - start=True, raw=True, no_recv=True, ) time.sleep(2) - (resp, sock) = self.http( + sock = self.http( b"""Host: localhost """, - start=True, sock=sock, raw=True, no_recv=True, @@ -245,7 +243,7 @@ Connection: close self.load('empty') def req(): - _, sock = self.http(b'', start=True, raw=True, no_recv=True) + sock = self.http(b'', raw=True, no_recv=True) time.sleep(3) diff --git a/test/test_static.py b/test/test_static.py index b9c78fdd..43d8b7cc 100644 --- a/test/test_static.py +++ b/test/test_static.py @@ -262,8 +262,8 @@ class TestStatic(TestApplicationProto): assert self.get(url='/../assets/')['status'] == 400, 'path invalid 5' def test_static_two_clients(self): - _, sock = self.get(url='/', start=True, no_recv=True) - _, sock2 = self.get(url='/', start=True, no_recv=True) + sock = self.get(no_recv=True) + sock2 = self.get(no_recv=True) assert sock.recv(1) == b'H', 'client 1' assert sock2.recv(1) == b'H', 'client 2' diff --git a/test/test_status.py b/test/test_status.py index 4c5e56ab..6c733474 100644 --- a/test/test_status.py +++ b/test/test_status.py @@ -81,7 +81,7 @@ Connection: close ) assert Status.get('/requests/total') == 6, 'pipeline' - (_, sock) = self.get(port=7081, no_recv=True, start=True) + sock = self.get(port=7081, no_recv=True) time.sleep(1) @@ -112,7 +112,7 @@ Connection: close # idle - _, sock = self.http(b'', start=True, raw=True, no_recv=True) + sock = self.http(b'', raw=True, no_recv=True) self.check_connections(2, 0, 1, 1) self.get(sock=sock) diff --git a/test/test_upstreams_rr.py b/test/test_upstreams_rr.py index dd64e1d9..71af3f5d 100644 --- a/test/test_upstreams_rr.py +++ b/test/test_upstreams_rr.py @@ -290,14 +290,13 @@ Connection: close socks = [] for i in range(req): delay = 1 if i % 5 == 0 else 0 - _, sock = self.get( + sock = self.get( headers={ 'Host': 'localhost', 'Content-Length': '0', 'X-Delay': str(delay), 'Connection': 'close', }, - start=True, no_recv=True, ) socks.append(sock) @@ -320,17 +319,16 @@ Connection: close socks2 = [] for _ in range(conns): - _, sock = self.get(start=True, no_recv=True) + sock = self.get(no_recv=True) socks.append(sock) - _, sock2 = self.http( + sock2 = self.http( b"""POST / HTTP/1.1 Host: localhost Content-Length: 10 Connection: close """, - start=True, no_recv=True, raw=True, ) diff --git a/test/unit/applications/websockets.py b/test/unit/applications/websockets.py index d647ce9b..15f212ff 100644 --- a/test/unit/applications/websockets.py +++ b/test/unit/applications/websockets.py @@ -43,10 +43,9 @@ class TestApplicationWebsocket(TestApplicationProto): 'Sec-WebSocket-Version': 13, } - _, sock = self.get( + sock = self.get( headers=headers, no_recv=True, - start=True, ) resp = '' diff --git a/test/unit/http.py b/test/unit/http.py index b29667c9..144f300c 100644 --- a/test/unit/http.py +++ b/test/unit/http.py @@ -104,6 +104,9 @@ class TestHTTP: resp = self.recvall(sock, **recvall_kwargs).decode(encoding) + else: + return sock + self.log_in(resp) if 'raw_resp' not in kwargs: -- cgit From 08dab702cb4140613e942b86f54824b3c7dea2ad Mon Sep 17 00:00:00 2001 From: Andrei Zeliankou Date: Tue, 11 Oct 2022 13:49:44 +0100 Subject: Tests: reworked "test_variables.py". Access log used for the variables testing instead of limited routing. Added missed test for $status variable. Some tests moved from "test_access_log.py" to "test_variables.py". --- test/test_access_log.py | 22 --- test/test_variables.py | 453 +++++++++++++++++++++++++++--------------------- 2 files changed, 259 insertions(+), 216 deletions(-) diff --git a/test/test_access_log.py b/test/test_access_log.py index b1d89343..a072858b 100644 --- a/test/test_access_log.py +++ b/test/test_access_log.py @@ -280,28 +280,6 @@ Connection: close def test_access_log_variables(self): self.load('mirror') - # $time_local - - self.set_format('$uri $time_local $uri') - assert self.get(url='/time_local')['status'] == 200 - assert self.wait_for_record('/time_local') is not None, 'time log' - date = self.search_in_log( - r'^\/time_local (.*) \/time_local$', 'access.log' - )[1] - assert ( - abs( - self.date_to_sec_epoch(date, '%d/%b/%Y:%X %z') - - time.mktime(time.localtime()) - ) - < 5 - ), '$time_local' - - # $request_line - - self.set_format('$request_line') - assert self.get(url='/r_line')['status'] == 200 - assert self.wait_for_record(r'^GET \/r_line HTTP\/1\.1$') is not None - # $body_bytes_sent self.set_format('$uri $body_bytes_sent') diff --git a/test/test_variables.py b/test/test_variables.py index 2ddfdc0a..43c5c471 100644 --- a/test/test_variables.py +++ b/test/test_variables.py @@ -1,4 +1,8 @@ +import re +import time + from unit.applications.proto import TestApplicationProto +from unit.option import option class TestVariables(TestApplicationProto): @@ -7,79 +11,165 @@ class TestVariables(TestApplicationProto): def setup_method(self): assert 'success' in self.conf( { - "listeners": {"*:7080": {"pass": "routes/$method"}}, - "routes": { - "GET": [{"action": {"return": 201}}], - "POST": [{"action": {"return": 202}}], - "3": [{"action": {"return": 203}}], - "4*": [{"action": {"return": 204}}], - "blahGET}": [{"action": {"return": 205}}], - "5GET": [{"action": {"return": 206}}], - "GETGET": [{"action": {"return": 207}}], - "localhost": [{"action": {"return": 208}}], - "9?q#a": [{"action": {"return": 209}}], - "blah": [{"action": {"return": 210}}], - "127.0.0.1": [{"action": {"return": 211}}], - "::1": [{"action": {"return": 212}}], - "referer-value": [{"action": {"return": 213}}], - "MSIE": [{"action": {"return": 214}}], - }, + "listeners": {"*:7080": {"pass": "routes"}}, + "routes": [{"action": {"return": 200}}], }, ), 'configure routes' - def conf_routes(self, routes): - assert 'success' in self.conf(routes, 'listeners/*:7080/pass') + def set_format(self, format): + assert 'success' in self.conf( + { + 'path': option.temp_dir + '/access.log', + 'format': format, + }, + 'access_log', + ), 'access_log format' + + def wait_for_record(self, pattern, name='access.log'): + return super().wait_for_record(pattern, name) + + def search_in_log(self, pattern, name='access.log'): + return super().search_in_log(pattern, name) + + def test_variables_dollar(self): + assert 'success' in self.conf("301", 'routes/0/action/return') + + def check_dollar(location, expect): + assert 'success' in self.conf( + '"' + location + '"', + 'routes/0/action/location', + ) + assert self.get()['headers']['Location'] == expect + + check_dollar( + 'https://${host}${uri}path${dollar}dollar', + 'https://localhost/path$dollar', + ) + check_dollar('path$dollar${dollar}', 'path$$') def test_variables_method(self): - assert self.get()['status'] == 201, 'method GET' - assert self.post()['status'] == 202, 'method POST' + self.set_format('$method') + + reg = r'^GET$' + assert self.search_in_log(reg) is None + assert self.get()['status'] == 200 + assert self.wait_for_record(reg) is not None, 'method GET' + + reg = r'^POST$' + assert self.search_in_log(reg) is None + assert self.post()['status'] == 200 + assert self.wait_for_record(reg) is not None, 'method POST' def test_variables_request_uri(self): - self.conf_routes("\"routes$request_uri\"") + self.set_format('$request_uri') - assert self.get(url='/3')['status'] == 203, 'request_uri' - assert self.get(url='/4*')['status'] == 204, 'request_uri 2' - assert self.get(url='/4%2A')['status'] == 204, 'request_uri 3' - assert self.get(url='/9?q#a')['status'] == 209, 'request_uri query' + def check_request_uri(req_uri): + reg = r'^' + re.escape(req_uri) + r'$' + + assert self.search_in_log(reg) is None + assert self.get(url=req_uri)['status'] == 200 + assert self.wait_for_record(reg) is not None + + check_request_uri('/3') + check_request_uri('/4*') + check_request_uri('/4%2A') + check_request_uri('/9?q#a') def test_variables_uri(self): - self.conf_routes("\"routes$uri\"") + self.set_format('$uri') + + def check_uri(uri, expect=None): + expect = uri if expect is None else expect + reg = r'^' + re.escape(expect) + r'$' - assert self.get(url='/3')['status'] == 203, 'uri' - assert self.get(url='/4*')['status'] == 204, 'uri 2' - assert self.get(url='/4%2A')['status'] == 204, 'uri 3' + assert self.search_in_log(reg) is None + assert self.get(url=uri)['status'] == 200 + assert self.wait_for_record(reg) is not None + + check_uri('/3') + check_uri('/4*') + check_uri('/5%2A', '/5*') + check_uri('/9?q#a', '/9') def test_variables_host(self): - self.conf_routes("\"routes/$host\"") + self.set_format('$host') + + def check_host(host, expect=None): + expect = host if expect is None else expect + reg = r'^' + re.escape(expect) + r'$' - def check_host(host, status=208): + assert self.search_in_log(reg) is None assert ( self.get(headers={'Host': host, 'Connection': 'close'})[ 'status' ] - == status + == 200 ) + assert self.wait_for_record(reg) is not None check_host('localhost') - check_host('localhost.') - check_host('localhost:7080') - check_host('.localhost', 404) - check_host('www.localhost', 404) - check_host('localhost1', 404) + check_host('localhost1.', 'localhost1') + check_host('localhost2:7080', 'localhost2') + check_host('.localhost') + check_host('www.localhost') def test_variables_remote_addr(self): - self.conf_routes("\"routes/$remote_addr\"") - assert self.get()['status'] == 211 + self.set_format('$remote_addr') + + assert self.get()['status'] == 200 + assert self.wait_for_record(r'^127\.0\.0\.1$') is not None assert 'success' in self.conf( - {"[::1]:7080": {"pass": "routes/$remote_addr"}}, 'listeners' + {"[::1]:7080": {"pass": "routes"}}, 'listeners' ) - assert self.get(sock_type='ipv6')['status'] == 212 + + reg = r'^::1$' + assert self.search_in_log(reg) is None + assert self.get(sock_type='ipv6')['status'] == 200 + assert self.wait_for_record(reg) is not None + + def test_variables_time_local(self): + self.set_format('$uri $time_local $uri') + + assert self.search_in_log(r'/time_local') is None + assert self.get(url='/time_local')['status'] == 200 + assert self.wait_for_record(r'/time_local') is not None, 'time log' + date = self.search_in_log( + r'^\/time_local (.*) \/time_local$', 'access.log' + )[1] + assert ( + abs( + self.date_to_sec_epoch(date, '%d/%b/%Y:%X %z') + - time.mktime(time.localtime()) + ) + < 5 + ), '$time_local' + + def test_variables_request_line(self): + self.set_format('$request_line') + + reg = r'^GET \/r_line HTTP\/1\.1$' + assert self.search_in_log(reg) is None + assert self.get(url='/r_line')['status'] == 200 + assert self.wait_for_record(reg) is not None + + def test_variables_status(self): + self.set_format('$status') + + assert 'success' in self.conf("418", 'routes/0/action/return') + + reg = r'^418$' + assert self.search_in_log(reg) is None + assert self.get()['status'] == 418 + assert self.wait_for_record(reg) is not None def test_variables_header_referer(self): - self.conf_routes("\"routes/$header_referer\"") + self.set_format('$method $header_referer') + + def check_referer(referer): + reg = r'^GET ' + re.escape(referer) + r'$' - def check_referer(referer, status=213): + assert self.search_in_log(reg) is None assert ( self.get( headers={ @@ -88,17 +178,21 @@ class TestVariables(TestApplicationProto): 'Referer': referer, } )['status'] - == status + == 200 ) + assert self.wait_for_record(reg) is not None check_referer('referer-value') - check_referer('', 404) - check_referer('no', 404) + check_referer('') + check_referer('no') def test_variables_header_user_agent(self): - self.conf_routes("\"routes/$header_user_agent\"") + self.set_format('$method $header_user_agent') - def check_user_agent(user_agent, status=214): + def check_user_agent(user_agent): + reg = r'^GET ' + re.escape(user_agent) + r'$' + + assert self.search_in_log(reg) is None assert ( self.get( headers={ @@ -107,152 +201,109 @@ class TestVariables(TestApplicationProto): 'User-Agent': user_agent, } )['status'] - == status + == 200 ) + assert self.wait_for_record(reg) is not None check_user_agent('MSIE') - check_user_agent('', 404) - check_user_agent('no', 404) - - def test_variables_dollar(self): - assert 'success' in self.conf( - { - "listeners": {"*:7080": {"pass": "routes"}}, - "routes": [{"action": {"return": 301}}], - } - ) - - def check_dollar(location, expect): - assert 'success' in self.conf( - '"' + location + '"', - 'routes/0/action/location', - ) - assert self.get()['headers']['Location'] == expect - - check_dollar( - 'https://${host}${uri}path${dollar}dollar', - 'https://localhost/path$dollar', - ) - check_dollar('path$dollar${dollar}', 'path$$') + check_user_agent('') + check_user_agent('no') def test_variables_many(self): - self.conf_routes("\"routes$uri$method\"") - assert self.get(url='/5')['status'] == 206, 'many' - - self.conf_routes("\"routes${uri}${method}\"") - assert self.get(url='/5')['status'] == 206, 'many 2' - - self.conf_routes("\"routes${uri}$method\"") - assert self.get(url='/5')['status'] == 206, 'many 3' + def check_vars(uri, expect): + reg = r'^' + re.escape(expect) + r'$' - self.conf_routes("\"routes/$method$method\"") - assert self.get()['status'] == 207, 'many 4' + assert self.search_in_log(reg) is None + assert self.get(url=uri)['status'] == 200 + assert self.wait_for_record(reg) is not None - self.conf_routes("\"routes/$method$uri\"") - assert self.get()['status'] == 404, 'no route' - assert self.get(url='/blah')['status'] == 404, 'no route 2' + self.set_format('$uri$method') + check_vars('/1', '/1GET') - def test_variables_replace(self): - assert self.get()['status'] == 201 + self.set_format('${uri}${method}') + check_vars('/2', '/2GET') - self.conf_routes("\"routes$uri\"") - assert self.get(url='/3')['status'] == 203 + self.set_format('${uri}$method') + check_vars('/3', '/3GET') - self.conf_routes("\"routes/${method}\"") - assert self.post()['status'] == 202 - - self.conf_routes("\"routes${uri}\"") - assert self.get(url='/4*')['status'] == 204 - - self.conf_routes("\"routes/blah$method}\"") - assert self.get()['status'] == 205 - - def test_variables_upstream(self): - assert 'success' in self.conf( - { - "listeners": { - "*:7080": {"pass": "upstreams$uri"}, - "*:7081": {"pass": "routes/one"}, - }, - "upstreams": {"1": {"servers": {"127.0.0.1:7081": {}}}}, - "routes": {"one": [{"action": {"return": 200}}]}, - }, - ), 'upstreams initial configuration' - - assert self.get(url='/1')['status'] == 200 - assert self.get(url='/2')['status'] == 404 - - def test_variables_empty(self): - def update_pass(prefix): - assert 'success' in self.conf( - {"listeners": {"*:7080": {"pass": prefix + "/$method"}}}, - ), 'variables empty' - - update_pass("routes") - assert self.get(url='/1')['status'] == 404 - - update_pass("upstreams") - assert self.get(url='/2')['status'] == 404 - - update_pass("applications") - assert self.get(url='/3')['status'] == 404 + self.set_format('$method$method') + check_vars('/', 'GETGET') def test_variables_dynamic(self): - self.conf_routes("\"routes/$header_foo$arg_foo$cookie_foo\"") + self.set_format('$header_foo$cookie_foo$arg_foo') self.get( url='/?foo=h', headers={'Foo': 'b', 'Cookie': 'foo=la', 'Connection': 'close'}, - )['status'] = 210 + )['status'] = 200 + assert self.wait_for_record(r'^blah$') is not None + + def test_variables_dynamic_arguments(self): + def check_arg(url, expect=None): + expect = url if expect is None else expect + reg = r'^' + re.escape(expect) + r'$' + + assert self.search_in_log(reg) is None + assert self.get(url=url)['status'] == 200 + assert self.wait_for_record(reg) is not None + + def check_no_arg(url): + assert self.get(url=url)['status'] == 200 + assert self.search_in_log(r'^0$') is None + + self.set_format('$arg_foo_bar') + check_arg('/?foo_bar=1', '1') + check_arg('/?foo_b%61r=2', '2') + check_arg('/?bar&foo_bar=3&foo', '3') + check_arg('/?foo_bar=l&foo_bar=4', '4') + check_no_arg('/') + check_no_arg('/?foo_bar=') + check_no_arg('/?Foo_bar=0') + check_no_arg('/?foo-bar=0') + check_no_arg('/?foo_bar=0&foo_bar=l') + + self.set_format('$arg_foo_b%61r') + check_no_arg('/?foo_b=0') + check_no_arg('/?foo_bar=0') + + self.set_format('$arg_f!~') + check_no_arg('/?f=0') + check_no_arg('/?f!~=0') def test_variables_dynamic_headers(self): - def check_header(header, status=210): + def check_header(header, value): + reg = r'^' + value + r'$' + + assert self.search_in_log(reg) is None assert ( - self.get(headers={header: "blah", 'Connection': 'close'})[ + self.get(headers={header: value, 'Connection': 'close'})[ 'status' ] - == status + == 200 ) + assert self.wait_for_record(reg) is not None - self.conf_routes("\"routes/$header_foo_bar\"") - check_header('foo-bar') - check_header('Foo-Bar') - check_header('foo_bar', 404) - check_header('Foo', 404) - check_header('Bar', 404) - check_header('foobar', 404) + def check_no_header(header): + assert ( + self.get(headers={header: '0', 'Connection': 'close'})['status'] + == 200 + ) + assert self.search_in_log(r'^0$') is None - self.conf_routes("\"routes/$header_Foo_Bar\"") - check_header('Foo-Bar') - check_header('foo-bar') - check_header('foo_bar', 404) - check_header('foobar', 404) + self.set_format('$header_foo_bar') + check_header('foo-bar', '1') + check_header('Foo-Bar', '2') + check_no_header('foo_bar') + check_no_header('foobar') - self.conf_routes("\"routes/$header_foo-bar\"") - check_header('foo_bar', 404) - - def test_variables_dynamic_arguments(self): - self.conf_routes("\"routes/$arg_foo_bar\"") - assert self.get(url='/?foo_bar=blah')['status'] == 210 - assert self.get(url='/?foo_b%61r=blah')['status'] == 210 - assert self.get(url='/?bar&foo_bar=blah&foo')['status'] == 210 - assert self.get(url='/?Foo_bar=blah')['status'] == 404 - assert self.get(url='/?foo-bar=blah')['status'] == 404 - assert self.get()['status'] == 404 - assert self.get(url='/?foo_bar=')['status'] == 404 - assert self.get(url='/?foo_bar=l&foo_bar=blah')['status'] == 210 - assert self.get(url='/?foo_bar=blah&foo_bar=l')['status'] == 404 - - self.conf_routes("\"routes/$arg_foo_b%61r\"") - assert self.get(url='/?foo_b=blah')['status'] == 404 - assert self.get(url='/?foo_bar=blah')['status'] == 404 - - self.conf_routes("\"routes/$arg_f!~\"") - assert self.get(url='/?f=blah')['status'] == 404 - assert self.get(url='/?f!~=blah')['status'] == 404 + self.set_format('$header_Foo_Bar') + check_header('Foo-Bar', '4') + check_header('foo-bar', '5') + check_no_header('foo_bar') + check_no_header('foobar') def test_variables_dynamic_cookies(self): - def check_cookie(cookie, status=210): + def check_no_cookie(cookie): assert ( self.get( headers={ @@ -261,33 +312,47 @@ class TestVariables(TestApplicationProto): 'Connection': 'close', }, )['status'] - == status - ), 'match cookie' + == 200 + ) + assert self.search_in_log(r'^0$') is None - self.conf_routes("\"routes/$cookie_foo_bar\"") - check_cookie('foo_bar=blah', 210) - check_cookie('fOo_bar=blah', 404) - assert self.get()['status'] == 404 - check_cookie('foo_bar', 404) - check_cookie('foo_bar=', 404) + self.set_format('$cookie_foo_bar') + + reg = r'^1$' + assert self.search_in_log(reg) is None + self.get( + headers={ + 'Host': 'localhost', + 'Cookie': 'foo_bar=1', + 'Connection': 'close', + }, + )['status'] == 200 + assert self.wait_for_record(reg) is not None + + check_no_cookie('fOo_bar=0') + check_no_cookie('foo_bar=') def test_variables_invalid(self): - def check_variables(routes): + def check_variables(format): assert 'error' in self.conf( - routes, 'listeners/*:7080/pass' - ), 'invalid variables' - - check_variables("\"routes$\"") - check_variables("\"routes${\"") - check_variables("\"routes${}\"") - check_variables("\"routes$ur\"") - check_variables("\"routes$uriblah\"") - check_variables("\"routes${uri\"") - check_variables("\"routes${{uri}\"") - check_variables("\"routes$ar\"") - check_variables("\"routes$arg\"") - check_variables("\"routes$arg_\"") - check_variables("\"routes$cookie\"") - check_variables("\"routes$cookie_\"") - check_variables("\"routes$header\"") - check_variables("\"routes$header_\"") + { + 'path': option.temp_dir + '/access.log', + 'format': format, + }, + 'access_log', + ), 'access_log format' + + check_variables("$") + check_variables("${") + check_variables("${}") + check_variables("$ur") + check_variables("$uriblah") + check_variables("${uri") + check_variables("${{uri}") + check_variables("$ar") + check_variables("$arg") + check_variables("$arg_") + check_variables("$cookie") + check_variables("$cookie_") + check_variables("$header") + check_variables("$header_") -- cgit From 7aa6b0629897235684961dfb9aa16e226efa27e6 Mon Sep 17 00:00:00 2001 From: Zhidao HONG Date: Wed, 12 Oct 2022 08:21:02 +0800 Subject: HTTP: added a $request_time variable. --- docs/changes.xml | 6 ++++++ src/nxt_http.h | 2 ++ src/nxt_http_request.c | 2 ++ src/nxt_http_variables.c | 33 +++++++++++++++++++++++++++++++++ 4 files changed, 43 insertions(+) diff --git a/docs/changes.xml b/docs/changes.xml index 8f871eb1..91cf8fe3 100644 --- a/docs/changes.xml +++ b/docs/changes.xml @@ -37,6 +37,12 @@ support rack v3 in ruby applications. + + +the $request_time variable contains the request processing time. + + + fix HTTP cookie parsing when the value contains an equals sign. diff --git a/src/nxt_http.h b/src/nxt_http.h index c2e85840..d5bff712 100644 --- a/src/nxt_http.h +++ b/src/nxt_http.h @@ -140,6 +140,8 @@ struct nxt_http_request_s { nxt_buf_t *out; const nxt_http_request_state_t *state; + nxt_nsec_t start_time; + nxt_str_t host; nxt_str_t server_name; nxt_str_t target; diff --git a/src/nxt_http_request.c b/src/nxt_http_request.c index 7a790f73..5c1455bf 100644 --- a/src/nxt_http_request.c +++ b/src/nxt_http_request.c @@ -278,6 +278,8 @@ nxt_http_request_create(nxt_task_t *task) r->resp.content_length_n = -1; r->state = &nxt_http_request_init_state; + r->start_time = nxt_thread_monotonic_time(task->thread); + task->thread->engine->requests_cnt++; return r; diff --git a/src/nxt_http_variables.c b/src/nxt_http_variables.c index 5a632b24..e01bcdb9 100644 --- a/src/nxt_http_variables.c +++ b/src/nxt_http_variables.c @@ -9,6 +9,8 @@ static nxt_int_t nxt_http_var_dollar(nxt_task_t *task, nxt_str_t *str, void *ctx, uint16_t field); +static nxt_int_t nxt_http_var_request_time(nxt_task_t *task, nxt_str_t *str, + void *ctx, uint16_t field); static nxt_int_t nxt_http_var_method(nxt_task_t *task, nxt_str_t *str, void *ctx, uint16_t field); static nxt_int_t nxt_http_var_request_uri(nxt_task_t *task, nxt_str_t *str, @@ -45,6 +47,9 @@ static nxt_var_decl_t nxt_http_vars[] = { { .name = nxt_string("dollar"), .handler = nxt_http_var_dollar, + }, { + .name = nxt_string("request_time"), + .handler = nxt_http_var_request_time, }, { .name = nxt_string("method"), .handler = nxt_http_var_method, @@ -110,6 +115,34 @@ nxt_http_var_dollar(nxt_task_t *task, nxt_str_t *str, void *ctx, uint16_t field) } +static nxt_int_t +nxt_http_var_request_time(nxt_task_t *task, nxt_str_t *str, void *ctx, + uint16_t field) +{ + u_char *p; + nxt_msec_t ms; + nxt_nsec_t now; + nxt_http_request_t *r; + + r = ctx; + + now = nxt_thread_monotonic_time(task->thread); + ms = (now - r->start_time) / 1000000; + + str->start = nxt_mp_nget(r->mem_pool, NXT_TIME_T_LEN + 4); + if (nxt_slow_path(str->start == NULL)) { + return NXT_ERROR; + } + + p = nxt_sprintf(str->start, str->start + NXT_TIME_T_LEN, "%T.%03M", + (nxt_time_t) ms / 1000, ms % 1000); + + str->length = p - str->start; + + return NXT_OK; +} + + static nxt_int_t nxt_http_var_method(nxt_task_t *task, nxt_str_t *str, void *ctx, uint16_t field) { -- cgit From af5903ff4ee83a0c632a07828ed80a0b5f40aefd Mon Sep 17 00:00:00 2001 From: Andrei Zeliankou Date: Thu, 13 Oct 2022 10:13:57 +0100 Subject: Tests: added tests for the $request_time variable. --- test/test_variables.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/test/test_variables.py b/test/test_variables.py index 43c5c471..ebbc5aa2 100644 --- a/test/test_variables.py +++ b/test/test_variables.py @@ -47,6 +47,35 @@ class TestVariables(TestApplicationProto): ) check_dollar('path$dollar${dollar}', 'path$$') + def test_variables_request_time(self): + self.set_format('$uri $request_time') + + sock = self.http(b'', raw=True, no_recv=True) + + time.sleep(1) + + assert self.get(url='/r_time_1', sock=sock)['status'] == 200 + assert self.wait_for_record(r'\/r_time_1 0\.\d{3}') is not None + + sock = self.http( + b"""G""", + no_recv=True, + raw=True, + ) + + time.sleep(2) + + self.http( + b"""ET /r_time_2 HTTP/1.1 +Host: localhost +Connection: close + +""", + sock=sock, + raw=True, + ) + assert self.wait_for_record(r'\/r_time_2 [1-9]\.\d{3}') is not None + def test_variables_method(self): self.set_format('$method') -- cgit From f8b892e1fa3d6398a21ce5e183911baa11a14d6a Mon Sep 17 00:00:00 2001 From: Andrew Clayton Date: Thu, 6 Oct 2022 13:13:30 +0100 Subject: Fixed the build on MacOS (and others). @alejandro-colomar reported that the build was broken on MacOS cc -o build/unitd -pipe -fPIC -fvisibility=hidden -O -W -Wall -Wextra -Wno-unused-parameter -Wwrite-strings -fstrict-aliasing -Wstrict-overflow=5 -Wmissing-prototypes -Werror -g \ build/src/nxt_main.o build/libnxt.a \ \ \ -L/usr/local/Cellar/pcre2/10.40/lib -lpcre2-8 Undefined symbols for architecture x86_64: "_nxt_fs_mkdir_parent", referenced from: _nxt_runtime_pid_file_create in libnxt.a(nxt_runtime.o) _nxt_runtime_controller_socket in libnxt.a(nxt_controller.o) ld: symbol(s) not found for architecture x86_64 clang: error: linker command failed with exit code 1 (use -v to see invocation) make: *** [build/unitd] Error 1 This was due to commit 57fc920 ("Socket: Created control socket & pid file directories."). This happened because this commit introduced the usage of nxt_fs_mkdir_parent() in core code which uses nxt_fs_mkdir(), both of these are defined in src/nxt_fs.c. It turns out however that this file doesn't get built on MacOS (or any system that isn't Linux or that lacks a FreeBSD compatible nmount(2) system call) due to the following In auto/sources we have if [ $NXT_HAVE_ROOTFS = YES ]; then NXT_LIB_SRCS="$NXT_LIB_SRCS src/nxt_fs.c" fi NXT_HAVE_ROOTFS is set in auto/isolation If [ $NXT_HAVE_MOUNT = YES -a $NXT_HAVE_UNMOUNT = YES ]; then NXT_HAVE_ROOTFS=YES cat << END >> $NXT_AUTO_CONFIG_H #ifndef NXT_HAVE_ISOLATION_ROOTFS #define NXT_HAVE_ISOLATION_ROOTFS 1 #endif END fi While we do have a check for a generic umount(2) which is found on MacOS, for mount(2) we currently only check for the Linux mount(2) and FreeBSD nmount(2) system calls. So NXT_HAVE_ROOTFS is set to NO on MacOS and we don't build src/nxt_fs.c This fixes the immediate build issue by taking the mount/umount OS support out of nxt_fs.c into a new nxt_fs_mount.c file which is guarded by the above while we now build nxt_fs.c unconditionally. This should fix the build on any _supported_ system. Reported-by: Alejandro Colomar Fixes: 57fc920 ("Socket: Created control socket & pid file directories.") Signed-off-by: Andrew Clayton --- auto/sources | 3 +- src/nxt_fs.c | 225 --------------------------------------------------- src/nxt_fs.h | 32 -------- src/nxt_fs_mount.c | 230 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/nxt_fs_mount.h | 48 +++++++++++ src/nxt_main.h | 1 + 6 files changed, 281 insertions(+), 258 deletions(-) create mode 100644 src/nxt_fs_mount.c create mode 100644 src/nxt_fs_mount.h diff --git a/auto/sources b/auto/sources index 8548f812..8f07cc0c 100644 --- a/auto/sources +++ b/auto/sources @@ -104,6 +104,7 @@ NXT_LIB_SRCS=" \ src/nxt_websocket_accept.c \ src/nxt_http_websocket.c \ src/nxt_h1proto_websocket.c \ + src/nxt_fs.c \ " NXT_LIB_SRC0=" \ @@ -187,7 +188,7 @@ NXT_LIB_UTF8_FILE_NAME_TEST_SRCS=" \ if [ $NXT_HAVE_ROOTFS = YES ]; then - NXT_LIB_SRCS="$NXT_LIB_SRCS src/nxt_fs.c" + NXT_LIB_SRCS="$NXT_LIB_SRCS src/nxt_fs_mount.c" fi diff --git a/src/nxt_fs.c b/src/nxt_fs.c index 35850798..a467da98 100644 --- a/src/nxt_fs.c +++ b/src/nxt_fs.c @@ -4,235 +4,10 @@ #include -#if (NXT_HAVE_FREEBSD_NMOUNT) -#include -#include -#endif - static nxt_int_t nxt_fs_mkdir(const u_char *dir, mode_t mode); -#if (NXT_HAVE_LINUX_MOUNT) - -nxt_int_t -nxt_fs_mount(nxt_task_t *task, nxt_fs_mount_t *mnt) -{ - int rc; - const char *fsname; - unsigned long flags; - - flags = 0; - - switch (mnt->type) { - case NXT_FS_BIND: - if (nxt_slow_path(mnt->flags != 0)) { - nxt_log(task, NXT_LOG_WARN, - "bind mount ignores additional flags"); - } - - fsname = "bind"; - flags = MS_BIND | MS_REC; - break; - - case NXT_FS_PROC: - fsname = "proc"; - goto getflags; - - case NXT_FS_TMP: - fsname = "tmpfs"; - goto getflags; - - default: - fsname = (const char *) mnt->name; - - getflags: - - if (mnt->flags & NXT_FS_FLAGS_NODEV) { - flags |= MS_NODEV; - } - - if (mnt->flags & NXT_FS_FLAGS_NOEXEC) { - flags |= MS_NOEXEC; - } - - if (mnt->flags & NXT_FS_FLAGS_NOSUID) { - flags |= MS_NOSUID; - } - - if (!(mnt->flags & NXT_FS_FLAGS_NOTIME)) { - flags |= MS_RELATIME; - } - } - - rc = mount((const char *) mnt->src, (const char *) mnt->dst, fsname, flags, - mnt->data); - - if (nxt_slow_path(rc < 0)) { - nxt_alert(task, "mount(\"%s\", \"%s\", \"%s\", %ul, \"%s\") %E", - mnt->src, mnt->dst, fsname, flags, mnt->data, nxt_errno); - - return NXT_ERROR; - } - - return NXT_OK; -} - -#elif (NXT_HAVE_FREEBSD_NMOUNT) - -nxt_int_t -nxt_fs_mount(nxt_task_t *task, nxt_fs_mount_t *mnt) -{ - int flags; - u_char *data, *p, *end; - size_t iovlen; - nxt_int_t ret; - const char *fsname; - struct iovec iov[128]; - char errmsg[256]; - - if (nxt_slow_path((mnt->flags & NXT_FS_FLAGS_NODEV) && !mnt->builtin)) { - nxt_alert(task, "nmount(2) doesn't support \"nodev\" option"); - - return NXT_ERROR; - } - - flags = 0; - - switch (mnt->type) { - case NXT_FS_BIND: - fsname = "nullfs"; - break; - - case NXT_FS_PROC: - fsname = "procfs"; - goto getflags; - - case NXT_FS_TMP: - fsname = "tmpfs"; - goto getflags; - - default: - fsname = (const char *) mnt->name; - - getflags: - - if (mnt->flags & NXT_FS_FLAGS_NOEXEC) { - flags |= MNT_NOEXEC; - } - - if (mnt->flags & NXT_FS_FLAGS_NOSUID) { - flags |= MNT_NOSUID; - } - - if (mnt->flags & NXT_FS_FLAGS_NOTIME) { - flags |= MNT_NOATIME; - } - - if (mnt->flags & NXT_FS_FLAGS_RDONLY) { - flags |= MNT_RDONLY; - } - } - - iov[0].iov_base = (void *) "fstype"; - iov[0].iov_len = 7; - iov[1].iov_base = (void *) fsname; - iov[1].iov_len = nxt_strlen(fsname) + 1; - iov[2].iov_base = (void *) "fspath"; - iov[2].iov_len = 7; - iov[3].iov_base = (void *) mnt->dst; - iov[3].iov_len = nxt_strlen(mnt->dst) + 1; - iov[4].iov_base = (void *) "target"; - iov[4].iov_len = 7; - iov[5].iov_base = (void *) mnt->src; - iov[5].iov_len = nxt_strlen(mnt->src) + 1; - iov[6].iov_base = (void *) "errmsg"; - iov[6].iov_len = 7; - iov[7].iov_base = (void *) errmsg; - iov[7].iov_len = sizeof(errmsg); - - iovlen = 8; - - data = NULL; - - if (mnt->data != NULL) { - data = (u_char *) nxt_strdup(mnt->data); - if (nxt_slow_path(data == NULL)) { - return NXT_ERROR; - } - - end = data - 1; - - do { - p = end + 1; - end = nxt_strchr(p, '='); - if (end == NULL) { - break; - } - - *end = '\0'; - - iov[iovlen].iov_base = (void *) p; - iov[iovlen].iov_len = (end - p) + 1; - - iovlen++; - - p = end + 1; - - end = nxt_strchr(p, ','); - if (end != NULL) { - *end = '\0'; - } - - iov[iovlen].iov_base = (void *) p; - iov[iovlen].iov_len = nxt_strlen(p) + 1; - - iovlen++; - - } while (end != NULL && nxt_nitems(iov) > (iovlen + 2)); - } - - ret = NXT_OK; - - if (nxt_slow_path(nmount(iov, iovlen, flags) < 0)) { - nxt_alert(task, "nmount(%p, %d, 0) %s", iov, iovlen, errmsg); - ret = NXT_ERROR; - } - - if (data != NULL) { - free(data); - } - - return ret; -} - -#endif - - -#if (NXT_HAVE_LINUX_UMOUNT2) - -void -nxt_fs_unmount(const u_char *path) -{ - if (nxt_slow_path(umount2((const char *) path, MNT_DETACH) < 0)) { - nxt_thread_log_error(NXT_LOG_WARN, "umount2(%s, MNT_DETACH) %E", - path, nxt_errno); - } -} - -#elif (NXT_HAVE_UNMOUNT) - -void -nxt_fs_unmount(const u_char *path) -{ - if (nxt_slow_path(unmount((const char *) path, MNT_FORCE) < 0)) { - nxt_thread_log_error(NXT_LOG_WARN, "unmount(%s) %E", path, nxt_errno); - } -} - -#endif - - nxt_int_t nxt_fs_mkdir_all(const u_char *dir, mode_t mode) { diff --git a/src/nxt_fs.h b/src/nxt_fs.h index af9585b8..c8868d80 100644 --- a/src/nxt_fs.h +++ b/src/nxt_fs.h @@ -6,40 +6,8 @@ #define _NXT_FS_H_INCLUDED_ -typedef enum { - NXT_FS_UNKNOWN = 0, - NXT_FS_BIND, - NXT_FS_TMP, - NXT_FS_PROC, - NXT_FS_LAST, -} nxt_fs_type_t; - - -typedef enum { - NXT_FS_FLAGS_NOSUID = 1 << 0, - NXT_FS_FLAGS_NOEXEC = 1 << 1, - NXT_FS_FLAGS_NOTIME = 1 << 2, - NXT_FS_FLAGS_NODEV = 1 << 3, - NXT_FS_FLAGS_RDONLY = 1 << 4, -} nxt_fs_flags_t; - - -typedef struct { - u_char *src; - u_char *dst; - nxt_fs_type_t type; - u_char *name; - nxt_fs_flags_t flags; - u_char *data; - nxt_uint_t builtin; /* 1-bit */ - nxt_uint_t deps; /* 1-bit */ -} nxt_fs_mount_t; - - nxt_int_t nxt_fs_mkdir_parent(const u_char *path, mode_t mode); nxt_int_t nxt_fs_mkdir_all(const u_char *dir, mode_t mode); -nxt_int_t nxt_fs_mount(nxt_task_t *task, nxt_fs_mount_t *mnt); -void nxt_fs_unmount(const u_char *path); #endif /* _NXT_FS_H_INCLUDED_ */ diff --git a/src/nxt_fs_mount.c b/src/nxt_fs_mount.c new file mode 100644 index 00000000..d9b384e4 --- /dev/null +++ b/src/nxt_fs_mount.c @@ -0,0 +1,230 @@ +/* + * Copyright (C) NGINX, Inc. + */ + +#include + +#if (NXT_HAVE_FREEBSD_NMOUNT) +#include +#include +#endif + + +#if (NXT_HAVE_LINUX_MOUNT) + +nxt_int_t +nxt_fs_mount(nxt_task_t *task, nxt_fs_mount_t *mnt) +{ + int rc; + const char *fsname; + unsigned long flags; + + flags = 0; + + switch (mnt->type) { + case NXT_FS_BIND: + if (nxt_slow_path(mnt->flags != 0)) { + nxt_log(task, NXT_LOG_WARN, + "bind mount ignores additional flags"); + } + + fsname = "bind"; + flags = MS_BIND | MS_REC; + break; + + case NXT_FS_PROC: + fsname = "proc"; + goto getflags; + + case NXT_FS_TMP: + fsname = "tmpfs"; + goto getflags; + + default: + fsname = (const char *) mnt->name; + + getflags: + + if (mnt->flags & NXT_FS_FLAGS_NODEV) { + flags |= MS_NODEV; + } + + if (mnt->flags & NXT_FS_FLAGS_NOEXEC) { + flags |= MS_NOEXEC; + } + + if (mnt->flags & NXT_FS_FLAGS_NOSUID) { + flags |= MS_NOSUID; + } + + if (!(mnt->flags & NXT_FS_FLAGS_NOTIME)) { + flags |= MS_RELATIME; + } + } + + rc = mount((const char *) mnt->src, (const char *) mnt->dst, fsname, flags, + mnt->data); + + if (nxt_slow_path(rc < 0)) { + nxt_alert(task, "mount(\"%s\", \"%s\", \"%s\", %ul, \"%s\") %E", + mnt->src, mnt->dst, fsname, flags, mnt->data, nxt_errno); + + return NXT_ERROR; + } + + return NXT_OK; +} + +#elif (NXT_HAVE_FREEBSD_NMOUNT) + +nxt_int_t +nxt_fs_mount(nxt_task_t *task, nxt_fs_mount_t *mnt) +{ + int flags; + u_char *data, *p, *end; + size_t iovlen; + nxt_int_t ret; + const char *fsname; + struct iovec iov[128]; + char errmsg[256]; + + if (nxt_slow_path((mnt->flags & NXT_FS_FLAGS_NODEV) && !mnt->builtin)) { + nxt_alert(task, "nmount(2) doesn't support \"nodev\" option"); + + return NXT_ERROR; + } + + flags = 0; + + switch (mnt->type) { + case NXT_FS_BIND: + fsname = "nullfs"; + break; + + case NXT_FS_PROC: + fsname = "procfs"; + goto getflags; + + case NXT_FS_TMP: + fsname = "tmpfs"; + goto getflags; + + default: + fsname = (const char *) mnt->name; + + getflags: + + if (mnt->flags & NXT_FS_FLAGS_NOEXEC) { + flags |= MNT_NOEXEC; + } + + if (mnt->flags & NXT_FS_FLAGS_NOSUID) { + flags |= MNT_NOSUID; + } + + if (mnt->flags & NXT_FS_FLAGS_NOTIME) { + flags |= MNT_NOATIME; + } + + if (mnt->flags & NXT_FS_FLAGS_RDONLY) { + flags |= MNT_RDONLY; + } + } + + iov[0].iov_base = (void *) "fstype"; + iov[0].iov_len = 7; + iov[1].iov_base = (void *) fsname; + iov[1].iov_len = nxt_strlen(fsname) + 1; + iov[2].iov_base = (void *) "fspath"; + iov[2].iov_len = 7; + iov[3].iov_base = (void *) mnt->dst; + iov[3].iov_len = nxt_strlen(mnt->dst) + 1; + iov[4].iov_base = (void *) "target"; + iov[4].iov_len = 7; + iov[5].iov_base = (void *) mnt->src; + iov[5].iov_len = nxt_strlen(mnt->src) + 1; + iov[6].iov_base = (void *) "errmsg"; + iov[6].iov_len = 7; + iov[7].iov_base = (void *) errmsg; + iov[7].iov_len = sizeof(errmsg); + + iovlen = 8; + + data = NULL; + + if (mnt->data != NULL) { + data = (u_char *) nxt_strdup(mnt->data); + if (nxt_slow_path(data == NULL)) { + return NXT_ERROR; + } + + end = data - 1; + + do { + p = end + 1; + end = nxt_strchr(p, '='); + if (end == NULL) { + break; + } + + *end = '\0'; + + iov[iovlen].iov_base = (void *) p; + iov[iovlen].iov_len = (end - p) + 1; + + iovlen++; + + p = end + 1; + + end = nxt_strchr(p, ','); + if (end != NULL) { + *end = '\0'; + } + + iov[iovlen].iov_base = (void *) p; + iov[iovlen].iov_len = nxt_strlen(p) + 1; + + iovlen++; + + } while (end != NULL && nxt_nitems(iov) > (iovlen + 2)); + } + + ret = NXT_OK; + + if (nxt_slow_path(nmount(iov, iovlen, flags) < 0)) { + nxt_alert(task, "nmount(%p, %d, 0) %s", iov, iovlen, errmsg); + ret = NXT_ERROR; + } + + if (data != NULL) { + free(data); + } + + return ret; +} + +#endif + + +#if (NXT_HAVE_LINUX_UMOUNT2) + +void +nxt_fs_unmount(const u_char *path) +{ + if (nxt_slow_path(umount2((const char *) path, MNT_DETACH) < 0)) { + nxt_thread_log_error(NXT_LOG_WARN, "umount2(%s, MNT_DETACH) %E", + path, nxt_errno); + } +} + +#elif (NXT_HAVE_UNMOUNT) + +void +nxt_fs_unmount(const u_char *path) +{ + if (nxt_slow_path(unmount((const char *) path, MNT_FORCE) < 0)) { + nxt_thread_log_error(NXT_LOG_WARN, "unmount(%s) %E", path, nxt_errno); + } +} + +#endif diff --git a/src/nxt_fs_mount.h b/src/nxt_fs_mount.h new file mode 100644 index 00000000..e0fe6eb2 --- /dev/null +++ b/src/nxt_fs_mount.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) NGINX, Inc. + */ + +#ifndef _NXT_FS_MOUNT_H_INCLUDED_ +#define _NXT_FS_MOUNT_H_INCLUDED_ + + +typedef enum nxt_fs_type_s nxt_fs_type_t; +typedef enum nxt_fs_flags_s nxt_fs_flags_t; +typedef struct nxt_fs_mount_s nxt_fs_mount_t; + + +enum nxt_fs_type_s { + NXT_FS_UNKNOWN = 0, + NXT_FS_BIND, + NXT_FS_TMP, + NXT_FS_PROC, + NXT_FS_LAST, +}; + + +enum nxt_fs_flags_s { + NXT_FS_FLAGS_NOSUID = 1 << 0, + NXT_FS_FLAGS_NOEXEC = 1 << 1, + NXT_FS_FLAGS_NOTIME = 1 << 2, + NXT_FS_FLAGS_NODEV = 1 << 3, + NXT_FS_FLAGS_RDONLY = 1 << 4, +}; + + +struct nxt_fs_mount_s { + u_char *src; + u_char *dst; + nxt_fs_type_t type; + u_char *name; + nxt_fs_flags_t flags; + u_char *data; + nxt_uint_t builtin; /* 1-bit */ + nxt_uint_t deps; /* 1-bit */ +}; + + +nxt_int_t nxt_fs_mount(nxt_task_t *task, nxt_fs_mount_t *mnt); +void nxt_fs_unmount(const u_char *path); + + +#endif /* _NXT_FS_MOUNT_H_INCLUDED_ */ diff --git a/src/nxt_main.h b/src/nxt_main.h index dca4b6dc..a51a1ce7 100644 --- a/src/nxt_main.h +++ b/src/nxt_main.h @@ -59,6 +59,7 @@ typedef uint16_t nxt_port_id_t; #include #include #include +#include #include #include #include -- cgit From f2213dbd1b51838ec6b59073d9d071a75def7858 Mon Sep 17 00:00:00 2001 From: Alex Colomar Date: Sat, 10 Sep 2022 18:00:27 +0200 Subject: Added missing error checking in the C API. pthread_mutex_init(3) may fail for several reasons, and failing to check will cause Undefined Behavior when those errors happen. Add missing checks, and correctly deinitialize previously created stuff before exiting from the API. Signed-off-by: Alejandro Colomar Reviewed-by: Andrew Clayton Reviewed-by: Zhidao HONG --- docs/changes.xml | 2 +- src/nxt_unit.c | 38 ++++++++++++++++++++++++++++---------- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/docs/changes.xml b/docs/changes.xml index 91cf8fe3..5da00d88 100644 --- a/docs/changes.xml +++ b/docs/changes.xml @@ -177,7 +177,7 @@ the Ruby application process could crash on SIGINT. -mutex leak in the C API. +mutex leaks in the C API. diff --git a/src/nxt_unit.c b/src/nxt_unit.c index e5cb0b58..3932b2ab 100644 --- a/src/nxt_unit.c +++ b/src/nxt_unit.c @@ -116,7 +116,7 @@ static int nxt_unit_incoming_mmap(nxt_unit_ctx_t *ctx, pid_t pid, int fd); static void nxt_unit_awake_ctx(nxt_unit_ctx_t *ctx, nxt_unit_ctx_impl_t *ctx_impl); -static void nxt_unit_mmaps_init(nxt_unit_mmaps_t *mmaps); +static int nxt_unit_mmaps_init(nxt_unit_mmaps_t *mmaps); nxt_inline void nxt_unit_process_use(nxt_unit_process_t *process); nxt_inline void nxt_unit_process_release(nxt_unit_process_t *process); static void nxt_unit_mmaps_destroy(nxt_unit_mmaps_t *mmaps); @@ -606,7 +606,7 @@ nxt_unit_create(nxt_unit_init_t *init) if (nxt_slow_path(rc != 0)) { nxt_unit_alert(NULL, "failed to initialize mutex (%d)", rc); - goto fail; + goto out_unit_free; } lib->unit.data = init->data; @@ -631,17 +631,35 @@ nxt_unit_create(nxt_unit_init_t *init) rc = nxt_unit_ctx_init(lib, &lib->main_ctx, init->ctx_data); if (nxt_slow_path(rc != NXT_UNIT_OK)) { - pthread_mutex_destroy(&lib->mutex); - goto fail; + goto out_mutex_destroy; + } + + rc = nxt_unit_mmaps_init(&lib->incoming); + if (nxt_slow_path(rc != 0)) { + nxt_unit_alert(NULL, "failed to initialize mutex (%d)", rc); + + goto out_ctx_free; } - nxt_unit_mmaps_init(&lib->incoming); - nxt_unit_mmaps_init(&lib->outgoing); + rc = nxt_unit_mmaps_init(&lib->outgoing); + if (nxt_slow_path(rc != 0)) { + nxt_unit_alert(NULL, "failed to initialize mutex (%d)", rc); + + goto out_mmaps_destroy; + } return lib; -fail: +out_mmaps_destroy: + nxt_unit_mmaps_destroy(&lib->incoming); + +out_ctx_free: + nxt_unit_ctx_free(&lib->main_ctx); + +out_mutex_destroy: + pthread_mutex_destroy(&lib->mutex); +out_unit_free: nxt_unit_free(NULL, lib); return NULL; @@ -4093,15 +4111,15 @@ nxt_unit_awake_ctx(nxt_unit_ctx_t *ctx, nxt_unit_ctx_impl_t *ctx_impl) } -static void +static int nxt_unit_mmaps_init(nxt_unit_mmaps_t *mmaps) { - pthread_mutex_init(&mmaps->mutex, NULL); - mmaps->size = 0; mmaps->cap = 0; mmaps->elts = NULL; mmaps->allocated_chunks = 0; + + return pthread_mutex_init(&mmaps->mutex, NULL); } -- cgit From 48b6a7b311272896be9212e170fcee8d1da25e79 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Thu, 2 Jun 2022 16:16:35 +0200 Subject: PHP: Fixed php_module_startup() call for PHP 8.2. PHP 8.2 changed the prototype of the function, removing the last parameter. Signed-off-by: Remi Collet Cc: Timo Stark Cc: George Peter Banyard Tested-by: Andy Postnikov Acked-by: Andy Postnikov Reviewed-by: Andrew Clayton Signed-off-by: Alejandro Colomar --- auto/modules/php | 4 ++++ docs/changes.xml | 6 ++++++ src/nxt_php_sapi.c | 4 ++++ 3 files changed, 14 insertions(+) diff --git a/auto/modules/php b/auto/modules/php index e92a67cd..7d224ec1 100644 --- a/auto/modules/php +++ b/auto/modules/php @@ -149,7 +149,11 @@ nxt_feature_test=" #include int main() { + #if (PHP_VERSION_ID < 80200) php_module_startup(NULL, NULL, 0); + #else + php_module_startup(NULL, NULL); + #endif return 0; }" diff --git a/docs/changes.xml b/docs/changes.xml index 5da00d88..96877044 100644 --- a/docs/changes.xml +++ b/docs/changes.xml @@ -31,6 +31,12 @@ NGINX Unit updated to 1.29.0. date="" time="" packager="Nginx Packaging <nginx-packaging@f5.com>"> + + +compatibility with PHP 8.2. + + + support rack v3 in ruby applications. diff --git a/src/nxt_php_sapi.c b/src/nxt_php_sapi.c index 42fdbc68..b7eb29d1 100644 --- a/src/nxt_php_sapi.c +++ b/src/nxt_php_sapi.c @@ -1150,7 +1150,11 @@ nxt_php_vcwd_chdir(nxt_unit_request_info_t *req, u_char *dir) static int nxt_php_startup(sapi_module_struct *sapi_module) { +#if (PHP_VERSION_ID < 80200) return php_module_startup(sapi_module, &nxt_php_unit_module, 1); +#else + return php_module_startup(sapi_module, &nxt_php_unit_module); +#endif } -- cgit From db86dc0586ac6c7184eeacc3e8769816f90dfddd Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Fri, 14 Oct 2022 12:16:43 +0200 Subject: Added parentheses for consistency. Reported-by: Andrew Clayton Signed-off-by: Remi Collet Reviewed-by: Andrew Clayton Signed-off-by: Alejandro Colomar --- src/nxt_php_sapi.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/nxt_php_sapi.c b/src/nxt_php_sapi.c index b7eb29d1..ca49b289 100644 --- a/src/nxt_php_sapi.c +++ b/src/nxt_php_sapi.c @@ -16,20 +16,20 @@ #include -#if PHP_VERSION_ID >= 50400 +#if (PHP_VERSION_ID >= 50400) #define NXT_HAVE_PHP_IGNORE_CWD 1 #endif -#if PHP_VERSION_ID >= 70100 +#if (PHP_VERSION_ID >= 70100) #define NXT_HAVE_PHP_LOG_MESSAGE_WITH_SYSLOG_TYPE 1 #else #define NXT_HAVE_PHP_INTERRUPTS 1 #endif -#if PHP_VERSION_ID >= 70000 +#if (PHP_VERSION_ID >= 70000) #define NXT_PHP7 1 #endif -#if PHP_VERSION_ID >= 80000 +#if (PHP_VERSION_ID >= 80000) #define NXT_PHP8 1 #endif @@ -73,7 +73,7 @@ typedef int (*nxt_php_disable_t)(char *p, size_t size); typedef int (*nxt_php_disable_t)(char *p, uint TSRMLS_DC); #endif -#if PHP_VERSION_ID < 70200 +#if (PHP_VERSION_ID < 70200) typedef void (*zif_handler)(INTERNAL_FUNCTION_PARAMETERS); #endif @@ -139,7 +139,7 @@ static int nxt_php_read_post(char *buffer, uint count_bytes TSRMLS_DC); #ifdef NXT_PHP7 -#if PHP_VERSION_ID < 70200 +#if (PHP_VERSION_ID < 70200) ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_fastcgi_finish_request, 0, 0, _IS_BOOL, NULL, 0) #else @@ -355,7 +355,7 @@ static nxt_php_target_t *nxt_php_targets; static nxt_int_t nxt_php_last_target = -1; static nxt_unit_ctx_t *nxt_php_unit_ctx; -#if defined(ZTS) && PHP_VERSION_ID < 70400 +#if defined(ZTS) && (PHP_VERSION_ID < 70400) static void ***tsrm_ls; #endif @@ -377,7 +377,7 @@ nxt_php_setup(nxt_task_t *task, nxt_process_t *process, #ifdef ZTS -#if PHP_VERSION_ID >= 70400 +#if (PHP_VERSION_ID >= 70400) php_tsrm_startup(); #else tsrm_startup(1, 1, 0, NULL); -- cgit From c03ebf7ffec285c040450a1882687d762c7b1b4f Mon Sep 17 00:00:00 2001 From: Zhidao HONG Date: Fri, 14 Oct 2022 14:00:02 +0800 Subject: Configuration: stopped automatic migration to the "share" behavior. This commit removed the $uri auto-append for the "share" option introduced in rev be6409cdb028. The main reason is that it causes problems when preparing Unit configurations to be loaded at startup from the state directory. E.g. Docker. A valid conf.json file with $uri references will end up with $uri$uri due to the auto-append. --- docs/changes.xml | 6 ++++++ src/nxt_conf_validation.c | 21 --------------------- 2 files changed, 6 insertions(+), 21 deletions(-) diff --git a/docs/changes.xml b/docs/changes.xml index 96877044..5a57f19e 100644 --- a/docs/changes.xml +++ b/docs/changes.xml @@ -31,6 +31,12 @@ NGINX Unit updated to 1.29.0. date="" time="" packager="Nginx Packaging <nginx-packaging@f5.com>"> + + +removed $uri auto-append for "share" when loading configuration. + + + compatibility with PHP 8.2. diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index fe6c22e5..f8242f6e 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -1690,11 +1690,6 @@ static nxt_int_t nxt_conf_vldt_share(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data) { - u_char *p; - nxt_str_t name, temp; - - static nxt_str_t uri = nxt_string("$uri"); - if (nxt_conf_type(value) == NXT_CONF_ARRAY) { if (nxt_conf_array_elements_count(value) == 0) { return nxt_conf_vldt_error(vldt, "The \"share\" array " @@ -1707,22 +1702,6 @@ nxt_conf_vldt_share(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, /* NXT_CONF_STRING */ - if (vldt->ver < 12600) { - nxt_conf_get_string(value, &name); - - temp.length = name.length + uri.length; - - temp.start = nxt_mp_get(vldt->conf_pool, temp.length); - if (nxt_slow_path(temp.start == NULL)) { - return NXT_ERROR; - } - - p = nxt_cpymem(temp.start, name.start, name.length); - nxt_memcpy(p, uri.start, uri.length); - - nxt_conf_set_string(value, &temp); - } - return nxt_conf_vldt_share_element(vldt, value); } -- cgit From bbf1f4da0fe19c51253400a2cef58b8bac28fb25 Mon Sep 17 00:00:00 2001 From: Zhidao HONG Date: Thu, 20 Oct 2022 12:02:27 +0800 Subject: Configuration: added the regex status in configure summary. --- auto/summary | 1 + docs/changes.xml | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/auto/summary b/auto/summary index 84bfbb7f..79e7ce34 100644 --- a/auto/summary +++ b/auto/summary @@ -27,6 +27,7 @@ Unit configuration summary: IPv6 support: .............. $NXT_INET6 Unix domain sockets support: $NXT_UNIX_DOMAIN TLS support: ............... $NXT_OPENSSL + Regex support: ............. $NXT_REGEX process isolation: ......... $NXT_ISOLATION diff --git a/docs/changes.xml b/docs/changes.xml index 5a57f19e..d1c71fdb 100644 --- a/docs/changes.xml +++ b/docs/changes.xml @@ -55,6 +55,12 @@ the $request_time variable contains the request processing time. + + +report the regex status in configure summary. + + + fix HTTP cookie parsing when the value contains an equals sign. -- cgit From f93361979a9f612b59470640c3566f5cb66c3eaf Mon Sep 17 00:00:00 2001 From: Alex Colomar Date: Tue, 11 Oct 2022 16:00:06 +0200 Subject: Avoided modifying existing directories at 'make install'. 'install -d' has an issue compared to 'mkdir -p': it doesn't respect existing directories. It will set the ownership, file mode, and SELinux contexts (and any other property that would be set by install(1) to a newly-created directory), overwriting any existing properties of the existing directory. 'mkdir -p' doesn't have this issue: it is a no-op if the directory exists. However, it's not an ideal solution either, since it can't be used to set the properties (owner, mode, ...) of a newly-created directory. Therefore, the best solution is to use install(1), but only after making sure that the directory doesn't exist with test(1). Reported-by: Andrew Clayton Reported-by: Alejandro Colomar Closes: Signed-off-by: Alejandro Colomar Tested-by: Andrew Clayton Reviewed-by: Andrew Clayton --- auto/make | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/auto/make b/auto/make index cb1d1f49..5324f49c 100644 --- a/auto/make +++ b/auto/make @@ -363,12 +363,15 @@ install-check: exit 1) ${NXT_DAEMON}-install: $NXT_DAEMON install-check - install -d \$(DESTDIR)$NXT_SBINDIR + test -d \$(DESTDIR)$NXT_SBINDIR \ + || install -d \$(DESTDIR)$NXT_SBINDIR install -p $NXT_BUILD_DIR/$NXT_DAEMON \$(DESTDIR)$NXT_SBINDIR/ - install -d \$(DESTDIR)$NXT_STATE + test -d \$(DESTDIR)$NXT_STATE \ + || install -d \$(DESTDIR)$NXT_STATE manpage-install: manpage install-check - install -d \$(DESTDIR)$NXT_MANDIR/man8 + test -d \$(DESTDIR)$NXT_MANDIR/man8 \ + || install -d \$(DESTDIR)$NXT_MANDIR/man8 install -p -m644 $NXT_BUILD_DIR/unitd.8 \$(DESTDIR)$NXT_MANDIR/man8/ .PHONY: uninstall ${NXT_DAEMON}-uninstall manpage-uninstall @@ -390,10 +393,12 @@ cat << END >> $NXT_MAKEFILE .PHONY: libunit-install libunit-uninstall libunit-install: $NXT_BUILD_DIR/$NXT_LIB_UNIT_STATIC - install -d \$(DESTDIR)$NXT_LIBDIR + test -d \$(DESTDIR)$NXT_LIBDIR \ + || install -d \$(DESTDIR)$NXT_LIBDIR install -p -m u=rw,go=r $NXT_BUILD_DIR/$NXT_LIB_UNIT_STATIC \ \$(DESTDIR)$NXT_LIBDIR/ - install -d \$(DESTDIR)$NXT_INCDIR + test -d \$(DESTDIR)$NXT_INCDIR \ + || install -d \$(DESTDIR)$NXT_INCDIR install -p -m u=rw,go=r src/nxt_unit.h \ src/nxt_unit_field.h \ src/nxt_unit_request.h \ -- cgit From 93d24bb1144bebedb79bec8745c42aa0dc778aef Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Wed, 16 Jan 2019 08:38:53 +0100 Subject: Preferring system crypto policy. If we don't call SSL_CTX_set_cipher_list(), then it uses the system's default. Link: Link: Link: Signed-off-by: Remi Collet Acked-by: Andrei Belov [ alx: add changelog and tweak commit message ] Signed-off-by: Alejandro Colomar --- docs/changes.xml | 6 ++++++ src/nxt_openssl.c | 14 +++++++------- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/docs/changes.xml b/docs/changes.xml index d1c71fdb..f4e3f65e 100644 --- a/docs/changes.xml +++ b/docs/changes.xml @@ -37,6 +37,12 @@ removed $uri auto-append for "share" when loading configuration. + + +prefer system crypto policy, instead of hardcoding a default. + + + compatibility with PHP 8.2. diff --git a/src/nxt_openssl.c b/src/nxt_openssl.c index e19b1381..32904660 100644 --- a/src/nxt_openssl.c +++ b/src/nxt_openssl.c @@ -295,7 +295,7 @@ 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; + const char *ca_certificate; nxt_tls_conf_t *conf; STACK_OF(X509_NAME) *list; nxt_tls_bundle_conf_t *bundle; @@ -361,13 +361,13 @@ nxt_openssl_server_init(nxt_task_t *task, nxt_mp_t *mp, } */ - ciphers = (conf->ciphers != NULL) ? conf->ciphers : "HIGH:!aNULL:!MD5"; - - if (SSL_CTX_set_cipher_list(ctx, ciphers) == 0) { - nxt_openssl_log_error(task, NXT_LOG_ALERT, + if (conf->ciphers) { /* else use system crypto policy */ + if (SSL_CTX_set_cipher_list(ctx, conf->ciphers) == 0) { + nxt_openssl_log_error(task, NXT_LOG_ALERT, "SSL_CTX_set_cipher_list(\"%s\") failed", - ciphers); - goto fail; + conf->ciphers); + goto fail; + } } #if (NXT_HAVE_OPENSSL_CONF_CMD) -- cgit From e096f19d47e45803d12a9c60ee51f79d2613281c Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Thu, 18 Nov 2021 17:47:39 +0100 Subject: TLS: Using ERR_get_error_all() with OpenSSL 3. Link: Cc: Andy Postnikov Cc: Andrew Clayton Signed-off-by: Remi Collet Signed-off-by: Alejandro Colomar --- docs/changes.xml | 6 ++++++ src/nxt_openssl.c | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/docs/changes.xml b/docs/changes.xml index f4e3f65e..59c55fea 100644 --- a/docs/changes.xml +++ b/docs/changes.xml @@ -43,6 +43,12 @@ prefer system crypto policy, instead of hardcoding a default. + + +compatibility with OpenSSL 3. + + + compatibility with PHP 8.2. diff --git a/src/nxt_openssl.c b/src/nxt_openssl.c index 32904660..55611511 100644 --- a/src/nxt_openssl.c +++ b/src/nxt_openssl.c @@ -1777,7 +1777,11 @@ nxt_openssl_copy_error(u_char *p, u_char *end) clear = 0; for ( ;; ) { +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + err = ERR_get_error_all(NULL, NULL, NULL, &data, &flags); +#else err = ERR_get_error_line_data(NULL, NULL, &data, &flags); +#endif if (err == 0) { break; } -- cgit From 7a928b33b475f726fab2f9d1397db87756da6135 Mon Sep 17 00:00:00 2001 From: Andrew Clayton Date: Tue, 25 Oct 2022 20:11:32 +0100 Subject: Set git diff driver for C source code files. Git can be told to apply language-specific rules when generating diffs. Enable this for C source code files (*.c and *.h) so that function names are printed right. Specifically, doing so prevents "git diff" from mistakenly considering unindented goto labels as function names. This has the same effect as adding [diff "default"] xfuncname = "^[[:alpha:]$_].*[^:]$" to your git config file. e.g get @@ -10,7 +10,7 @@ int main(void) instead of @@ -10,7 +10,7 @@ again: This makes use of the gitattributes(5) infrastructure. Link: Link: Reviewed-by: Alejandro Colomar Signed-off-by: Andrew Clayton --- .gitattributes | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..45ec5156 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +*.c diff=cpp +*.h diff=cpp -- cgit From ac64ffde5718d6199c632831744765afd04fd740 Mon Sep 17 00:00:00 2001 From: javad mnjd Date: Tue, 25 Oct 2022 03:07:52 +0330 Subject: Improved readability of . Cc: Konstantin Pavlov Signed-off-by: Alejandro Colomar --- pkg/docker/docker-entrypoint.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/docker/docker-entrypoint.sh b/pkg/docker/docker-entrypoint.sh index 59529925..c07ef94c 100755 --- a/pkg/docker/docker-entrypoint.sh +++ b/pkg/docker/docker-entrypoint.sh @@ -1,11 +1,11 @@ -#!/usr/bin/env bash +#!/bin/sh set -e curl_put() { - RET=`/usr/bin/curl -s -w '%{http_code}' -X PUT --data-binary @$1 --unix-socket /var/run/control.unit.sock http://localhost/$2` - RET_BODY=${RET::-3} + RET=$(/usr/bin/curl -s -w '%{http_code}' -X PUT --data-binary @$1 --unix-socket /var/run/control.unit.sock http://localhost/$2) + RET_BODY=$(echo $RET | /usr/bin/sed '$ s/...$//') RET_STATUS=$(echo $RET | /usr/bin/tail -c 4) if [ "$RET_STATUS" -ne "200" ]; then echo "$0: Error: HTTP response status code is '$RET_STATUS'" @@ -18,7 +18,7 @@ curl_put() return 0 } -if [ "$1" = "unitd" -o "$1" = "unitd-debug" ]; then +if [ "$1" = "unitd" ] || [ "$1" = "unitd-debug" ]; then if /usr/bin/find "/var/lib/unit/" -mindepth 1 -print -quit 2>/dev/null | /bin/grep -q .; then echo "$0: /var/lib/unit/ is not empty, skipping initial configuration..." else @@ -55,7 +55,7 @@ if [ "$1" = "unitd" -o "$1" = "unitd-debug" ]; then done echo "$0: Stopping Unit daemon after initial configuration..." - kill -TERM `/bin/cat /var/run/unit.pid` + kill -TERM $(/bin/cat /var/run/unit.pid) while [ -S /var/run/control.unit.sock ]; do echo "$0: Waiting for control socket to be removed..."; /bin/sleep 0.1; done -- cgit From a3cb07df20541939335fe42c75d0d479c2092b88 Mon Sep 17 00:00:00 2001 From: Alejandro Colomar Date: Thu, 27 Oct 2022 14:30:47 +0200 Subject: Fixed path for sed(1). Some distros provide it in /bin/sed and others in both /bin/sed and /usr/bin/sed. Use the more available one. Reported-by: Konstantin Pavlov Fixes: ac64ffde5718 "Improved readability of ." Signed-off-by: Alejandro Colomar --- pkg/docker/docker-entrypoint.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/docker/docker-entrypoint.sh b/pkg/docker/docker-entrypoint.sh index c07ef94c..7f65d94a 100755 --- a/pkg/docker/docker-entrypoint.sh +++ b/pkg/docker/docker-entrypoint.sh @@ -5,7 +5,7 @@ set -e curl_put() { RET=$(/usr/bin/curl -s -w '%{http_code}' -X PUT --data-binary @$1 --unix-socket /var/run/control.unit.sock http://localhost/$2) - RET_BODY=$(echo $RET | /usr/bin/sed '$ s/...$//') + RET_BODY=$(echo $RET | /bin/sed '$ s/...$//') RET_STATUS=$(echo $RET | /usr/bin/tail -c 4) if [ "$RET_STATUS" -ne "200" ]; then echo "$0: Error: HTTP response status code is '$RET_STATUS'" -- cgit From 8f0dd9478e164121e31bebaf1c10dd6e537d2918 Mon Sep 17 00:00:00 2001 From: Andrew Clayton Date: Fri, 28 Oct 2022 00:17:51 +0100 Subject: Fixed main() prototypes in auto tests. Future releases of GCC are planning to remove[0] default support for some old features that were removed from C99 but GCC still accepts. We can test for these changes by using the following -Werror= directives -Werror=implicit-int -Werror=implicit-function-declaration -Werror=int-conversion -Werror=strict-prototypes -Werror=old-style-definition Doing so revealed an issue with the auto/ tests in that the test programs always define main as int main() rather than int main(void) which results in a bunch of errors like build/autotest.c:3:23: error: function declaration isn't a prototype [-Werror=strict-prototypes] 3 | int main() { | ^~~~ build/autotest.c: In function 'main': build/autotest.c:3:23: error: old-style function definition [-Werror=old-style-definition] The fix was easy, it only required fixing the main prototype with find -type f -exec sed -i 's/int main() {/int main(void) {/g' {} \; Regardless of these upcoming GCC changes, this is probably a good thing to do anyway for correctness. [0]: https://fedoraproject.org/wiki/Changes/PortingToModernC Link: Link: Reviewed-by: Alejandro Colomar Signed-off-by: Andrew Clayton --- auto/atomic | 8 ++++---- auto/capability | 2 +- auto/clang | 20 ++++++++++---------- auto/events | 16 ++++++++-------- auto/files | 8 ++++---- auto/isolation | 16 ++++++++-------- auto/malloc | 16 ++++++++-------- auto/mmap | 8 ++++---- auto/modules/java | 2 +- auto/modules/perl | 4 ++-- auto/modules/php | 8 ++++---- auto/modules/python | 4 ++-- auto/modules/ruby | 6 +++--- auto/pcre | 2 +- auto/sendfile | 12 ++++++------ auto/shmem | 8 ++++---- auto/sockets | 32 ++++++++++++++++---------------- auto/ssltls | 18 +++++++++--------- auto/threads | 16 ++++++++-------- auto/time | 20 ++++++++++---------- auto/types | 14 +++++++------- auto/unix | 18 +++++++++--------- 22 files changed, 129 insertions(+), 129 deletions(-) diff --git a/auto/atomic b/auto/atomic index 31259fc5..f99adf7e 100644 --- a/auto/atomic +++ b/auto/atomic @@ -10,7 +10,7 @@ nxt_feature_name=NXT_HAVE_GCC_ATOMIC nxt_feature_run=yes nxt_feature_incs= nxt_feature_libs= -nxt_feature_test="int main() { +nxt_feature_test="int main(void) { long n = 0; if (!__sync_bool_compare_and_swap(&n, 0, 3)) @@ -44,7 +44,7 @@ if [ $nxt_found = no ]; then nxt_feature_libs= nxt_feature_test="#include - int main() { + int main(void) { ulong_t n = 0; if (atomic_cas_ulong(&n, 0, 3) != 0) @@ -70,7 +70,7 @@ fi if [ $nxt_found = no ]; then if [ $NXT_64BIT = 1 ]; then - nxt_feature_test="int main() { + nxt_feature_test="int main(void) { long n = 0; long o = 0; @@ -87,7 +87,7 @@ if [ $nxt_found = no ]; then return 0; }" else - nxt_feature_test="int main() { + nxt_feature_test="int main(void) { int n = 0; int o = 0; diff --git a/auto/capability b/auto/capability index 48777665..c0410b34 100644 --- a/auto/capability +++ b/auto/capability @@ -10,7 +10,7 @@ nxt_feature_test="#include #include #include - int main() { + int main(void) { struct __user_cap_header_struct hdr; hdr.version = _LINUX_CAPABILITY_VERSION; syscall(SYS_capget, &hdr, 0); diff --git a/auto/clang b/auto/clang index 1a05b5a3..975a6468 100644 --- a/auto/clang +++ b/auto/clang @@ -14,7 +14,7 @@ nxt_feature_libs= nxt_feature_test="#include #define set(dummy, ...) sprintf(__VA_ARGS__) - int main() { + int main(void) { char buf[4]; buf[0] = '0'; @@ -37,7 +37,7 @@ if [ $nxt_found = no ]; then nxt_feature_test="#include #define set(dummy, args...) sprintf(args) - int main() { + int main(void) { char buf[4]; buf[0] = '0'; @@ -70,7 +70,7 @@ nxt_feature_name=NXT_HAVE_BUILTIN_UNREACHABLE nxt_feature_run=no nxt_feature_incs= nxt_feature_libs= -nxt_feature_test="int main() { +nxt_feature_test="int main(void) { __builtin_unreachable(); }" . auto/feature @@ -81,7 +81,7 @@ nxt_feature_name=NXT_HAVE_BUILTIN_PREFETCH nxt_feature_run=no nxt_feature_incs= nxt_feature_libs= -nxt_feature_test="int main() { +nxt_feature_test="int main(void) { __builtin_prefetch(0); return 0; }" @@ -93,7 +93,7 @@ nxt_feature_name=NXT_HAVE_BUILTIN_CLZ nxt_feature_run= nxt_feature_incs= nxt_feature_libs= -nxt_feature_test="int main() { +nxt_feature_test="int main(void) { if (__builtin_clz(1) == 31) return 0; return 1; @@ -106,7 +106,7 @@ nxt_feature_name=NXT_HAVE_BUILTIN_POPCOUNT nxt_feature_run= nxt_feature_incs= nxt_feature_libs= -nxt_feature_test="int main() { +nxt_feature_test="int main(void) { if (__builtin_popcount(5) == 2) return 0; return 1; @@ -121,7 +121,7 @@ nxt_feature_incs= nxt_feature_libs= nxt_feature_test="int n __attribute__ ((visibility(\"default\"))); - int main() { + int main(void) { return 1; }" . auto/feature @@ -134,7 +134,7 @@ nxt_feature_incs= nxt_feature_libs= nxt_feature_test="int n __attribute__ ((aligned(64))); - int main() { + int main(void) { return 1; }" . auto/feature @@ -153,7 +153,7 @@ nxt_feature_test="#include return malloc(1); } - int main() { + int main(void) { if (f() != NULL) { return 1; } @@ -172,7 +172,7 @@ nxt_feature_test="struct s { int i; } __attribute__ ((__packed__)); - int main() { + int main(void) { return 1; }" . auto/feature diff --git a/auto/events b/auto/events index 700dc20c..1ca6e7cd 100644 --- a/auto/events +++ b/auto/events @@ -13,7 +13,7 @@ nxt_feature_libs= nxt_feature_test="#include #include - int main() { + int main(void) { int n; n = epoll_create(1); @@ -34,7 +34,7 @@ if [ $nxt_found = yes ]; then #include #include - int main() { + int main(void) { int n; sigset_t mask; @@ -54,7 +54,7 @@ if [ $nxt_found = yes ]; then nxt_feature_test="#include #include - int main() { + int main(void) { int n; n = eventfd(0, 0); @@ -79,7 +79,7 @@ nxt_feature_test="#include #include #include - int main() { + int main(void) { int n; n = kqueue(); @@ -100,7 +100,7 @@ if [ $nxt_found = yes ]; then #include #include - int main() { + int main(void) { struct kevent kev; kev.filter = EVFILT_USER; @@ -124,7 +124,7 @@ nxt_feature_libs= nxt_feature_test="#include #include - int main() { + int main(void) { int n; n = port_create(); @@ -152,7 +152,7 @@ nxt_feature_test="#include #include #include - int main() { + int main(void) { int n; n = open(\"/dev/poll\", O_RDWR); @@ -180,7 +180,7 @@ nxt_feature_test="#include #include #include - int main() { + int main(void) { pollset_t n; n = pollset_create(-1); diff --git a/auto/files b/auto/files index 591c5ee1..1fa6ca28 100644 --- a/auto/files +++ b/auto/files @@ -12,7 +12,7 @@ nxt_feature_incs= nxt_feature_libs= nxt_feature_test="#include - int main() { + int main(void) { (void) posix_fadvise(0, 0, 0, POSIX_FADV_WILLNEED); return 0; }" @@ -28,7 +28,7 @@ nxt_feature_incs= nxt_feature_libs= nxt_feature_test="#include - int main() { + int main(void) { (void) fcntl(0, F_READAHEAD, 1024); return 0; }" @@ -44,7 +44,7 @@ nxt_feature_incs= nxt_feature_libs= nxt_feature_test="#include - int main() { + int main(void) { (void) fcntl(0, F_RDAHEAD, 1); return 0; }" @@ -62,7 +62,7 @@ nxt_feature_test="#include #include #include - int main() { + int main(void) { struct open_how how; memset(&how, 0, sizeof(how)); diff --git a/auto/isolation b/auto/isolation index 384f7ef1..cbf42d9d 100644 --- a/auto/isolation +++ b/auto/isolation @@ -20,7 +20,7 @@ nxt_feature_libs= nxt_feature_test="#include #include - int main() { + int main(void) { return SYS_clone | SIGCHLD; }" . auto/feature @@ -40,7 +40,7 @@ if [ $nxt_found = yes ]; then #include #include - int main() { + int main(void) { return CLONE_NEW$flag; }" . auto/feature @@ -70,7 +70,7 @@ nxt_feature_test="#include # error #endif - int main() { + int main(void) { return SYS_pivot_root; }" . auto/feature @@ -96,7 +96,7 @@ nxt_feature_incs= nxt_feature_libs= nxt_feature_test="#include - int main() { + int main(void) { return PR_SET_NO_NEW_PRIVS; }" . auto/feature @@ -109,7 +109,7 @@ nxt_feature_incs= nxt_feature_libs= nxt_feature_test="#include - int main() { + int main(void) { return mount(\"/\", \"/\", \"bind\", MS_BIND | MS_REC, \"\"); }" @@ -128,7 +128,7 @@ if [ $nxt_found = no ]; then nxt_feature_libs= nxt_feature_test="#include - int main() { + int main(void) { return nmount((void *)0, 0, 0); }" . auto/feature @@ -146,7 +146,7 @@ nxt_feature_incs= nxt_feature_libs= nxt_feature_test="#include - int main() { + int main(void) { return umount2((void *)0, 0); }" . auto/feature @@ -163,7 +163,7 @@ if [ $nxt_found = no ]; then nxt_feature_libs= nxt_feature_test="#include - int main() { + int main(void) { return unmount((void *)0, 0); }" . auto/feature diff --git a/auto/malloc b/auto/malloc index 06a16b54..cf6fc59e 100644 --- a/auto/malloc +++ b/auto/malloc @@ -13,7 +13,7 @@ nxt_feature_incs= nxt_feature_libs= nxt_feature_test="#include - int main() { + int main(void) { void *p; if (posix_memalign(&p, 4096, 4096) != 0) @@ -36,7 +36,7 @@ if [ $nxt_found = no ]; then nxt_feature_libs= nxt_feature_test="#include - int main() { + int main(void) { void *p; p = memalign(4096, 4096); @@ -59,7 +59,7 @@ nxt_feature_incs= nxt_feature_libs= nxt_feature_test="#include - int main() { + int main(void) { void *p; p = malloc(4096); @@ -82,7 +82,7 @@ if [ $nxt_found = no ]; then nxt_feature_test="#include #include - int main() { + int main(void) { void *p; p = malloc(4096); @@ -105,7 +105,7 @@ if [ $nxt_found = no ]; then nxt_feature_libs= nxt_feature_test="#include - int main() { + int main(void) { if (malloc_good_size(4096) < 4096) return 1; return 0; @@ -125,7 +125,7 @@ nxt_feature_incs= nxt_feature_libs= nxt_feature_test="#include - int main() { + int main(void) { void *p; p = alloca(256); @@ -147,7 +147,7 @@ if [ $nxt_found = no ]; then nxt_feature_libs= nxt_feature_test="#include - int main() { + int main(void) { void *p; p = alloca(256); @@ -168,7 +168,7 @@ nxt_feature_incs= nxt_feature_libs= nxt_feature_test="#include - int main() { + int main(void) { mallopt(M_PERTURB, 0x55); return 0; }" diff --git a/auto/mmap b/auto/mmap index 8ecdf670..e8ffac78 100644 --- a/auto/mmap +++ b/auto/mmap @@ -13,7 +13,7 @@ nxt_feature_libs= nxt_feature_test="#include #include - int main() { + int main(void) { if (mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0) == MAP_FAILED) @@ -35,7 +35,7 @@ if [ $nxt_found = no ]; then nxt_feature_test="#include #include - int main() { + int main(void) { if (mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0) == MAP_FAILED) @@ -56,7 +56,7 @@ nxt_feature_libs= nxt_feature_test="#include #include - int main() { + int main(void) { if (mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, -1, 0) == MAP_FAILED) @@ -76,7 +76,7 @@ nxt_feature_libs= nxt_feature_test="#include #include - int main() { + int main(void) { if (mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_PREFAULT_READ, -1, 0) diff --git a/auto/modules/java b/auto/modules/java index e8137217..691060b3 100644 --- a/auto/modules/java +++ b/auto/modules/java @@ -191,7 +191,7 @@ nxt_feature_libs="${NXT_JAVA_LDFLAGS}" nxt_feature_test=" #include - int main() { + int main(void) { JNI_CreateJavaVM(NULL, NULL, NULL); return 0; }" diff --git a/auto/modules/perl b/auto/modules/perl index e9d7e109..2daebd0d 100644 --- a/auto/modules/perl +++ b/auto/modules/perl @@ -79,7 +79,7 @@ if /bin/sh -c "$NXT_PERL -MConfig -e 'print \"Perl version: \", static PerlInterpreter *my_perl; - int main() { + int main(void) { char argv[] = \"\\0-e\\00\"; char *embedding[] = { &argv[0], &argv[1], &argv[4] }; @@ -124,7 +124,7 @@ nxt_feature_test=" #include #include - int main() { + int main(void) { printf(\"%s\", PERL_VERSION_STRING); return 0; }" diff --git a/auto/modules/php b/auto/modules/php index 7d224ec1..f0ecb709 100644 --- a/auto/modules/php +++ b/auto/modules/php @@ -131,7 +131,7 @@ nxt_feature_libs="${NXT_PHP_LIB} ${NXT_PHP_LDFLAGS}" nxt_feature_test=" #include - int main() { + int main(void) { printf(\"%s\", PHP_VERSION); return 0; }" @@ -148,7 +148,7 @@ nxt_feature_test=" #include #include - int main() { + int main(void) { #if (PHP_VERSION_ID < 80200) php_module_startup(NULL, NULL, 0); #else @@ -176,7 +176,7 @@ nxt_feature_test=" #include #include - int main() { + int main(void) { #ifndef ZTS #error ZTS is not defined. #endif @@ -197,7 +197,7 @@ nxt_feature_test=" #include #include - int main() { + int main(void) { zend_signal_startup(); return 0; }" diff --git a/auto/modules/python b/auto/modules/python index 9be6b370..480ae1da 100644 --- a/auto/modules/python +++ b/auto/modules/python @@ -86,7 +86,7 @@ if /bin/sh -c "$NXT_PYTHON_CONFIG --prefix" >> $NXT_AUTOCONF_ERR 2>&1; then nxt_feature_test=" #include - int main() { + int main(void) { Py_Initialize(); return 0; }" @@ -114,7 +114,7 @@ nxt_feature_test=" #include #include - int main() { + int main(void) { printf(\"%s\", PY_VERSION); return 0; }" diff --git a/auto/modules/ruby b/auto/modules/ruby index dbedfd72..7c2f0102 100644 --- a/auto/modules/ruby +++ b/auto/modules/ruby @@ -106,7 +106,7 @@ if /bin/sh -c "$NXT_RUBY -v" >> $NXT_AUTOCONF_ERR 2>&1; then nxt_feature_test=" #include - int main() { + int main(void) { static const char *argv[3] = { \"NGINX_Unit\", \"-rrbconfig\", \"-eprint RbConfig::CONFIG['libdir']\" @@ -130,7 +130,7 @@ if /bin/sh -c "$NXT_RUBY -v" >> $NXT_AUTOCONF_ERR 2>&1; then nxt_feature_test=" #include - int main() { + int main(void) { ruby_init(); return ruby_cleanup(0); }" @@ -159,7 +159,7 @@ nxt_feature_test=" #include #include - int main() { + int main(void) { printf(\"%s\", ruby_version); return 0; }" diff --git a/auto/pcre b/auto/pcre index 955e4baf..27205118 100644 --- a/auto/pcre +++ b/auto/pcre @@ -50,7 +50,7 @@ if [ $nxt_found = no ]; then nxt_feature_libs=$NXT_PCRE_LIB nxt_feature_test="#include - int main() { + int main(void) { pcre *re; re = pcre_compile(NULL, 0, NULL, 0, NULL); diff --git a/auto/sendfile b/auto/sendfile index 1c20db06..abbda6c7 100644 --- a/auto/sendfile +++ b/auto/sendfile @@ -17,7 +17,7 @@ nxt_feature="Linux sendfile()" nxt_feature_name=NXT_HAVE_LINUX_SENDFILE nxt_feature_test="#include - int main() { + int main(void) { off_t offset; sendfile(-1, -1, &offset, 0); @@ -43,7 +43,7 @@ if [ $nxt_found = no ]; then #include #include - int main() { + int main(void) { off_t sent; sendfile(-1, -1, 0, 0, NULL, &sent, 0); @@ -69,7 +69,7 @@ if [ $nxt_found = no ]; then #include #include - int main() { + int main(void) { off_t sent; sendfile(-1, -1, 0, &sent, NULL, 0); @@ -100,7 +100,7 @@ if [ $nxt_found = no ]; then nxt_feature_libs="-lsendfile" nxt_feature_test="#include - int main() { + int main(void) { size_t sent; struct sendfilevec vec; @@ -124,7 +124,7 @@ if [ $nxt_found = no ]; then nxt_feature_name=NXT_HAVE_AIX_SEND_FILE nxt_feature_test="#include - int main() { + int main(void) { int s; struct sf_parms sf_iobuf; @@ -152,7 +152,7 @@ if [ $nxt_found = no ]; then sbsize_t sendfile(int s, int fd, off_t offset, bsize_t nbytes, const struct iovec *hdtrl, int flags); - int main() { + int main(void) { sendfile(-1, -1, 0, 0, NULL, 0); return 0; }" diff --git a/auto/shmem b/auto/shmem index bfe0ee4a..c434a58f 100644 --- a/auto/shmem +++ b/auto/shmem @@ -18,7 +18,7 @@ nxt_feature_test="#include #include #include - int main() { + int main(void) { int ret; static char name[] = \"/unit.configure\"; @@ -62,7 +62,7 @@ if [ $nxt_found = no ]; then #include #include - int main() { + int main(void) { static char name[] = \"/tmp/unit.configure\"; shm_unlink(name); @@ -94,7 +94,7 @@ nxt_feature_test="#include #include #include - int main() { + int main(void) { int fd = shm_open(SHM_ANON, O_RDWR, S_IRUSR | S_IWUSR); if (fd == -1) return 1; @@ -119,7 +119,7 @@ nxt_feature_test="#include #include #include - int main() { + int main(void) { static char name[] = \"/unit.configure\"; int fd = syscall(SYS_memfd_create, name, MFD_CLOEXEC); diff --git a/auto/sockets b/auto/sockets index e344a3db..241b88eb 100644 --- a/auto/sockets +++ b/auto/sockets @@ -15,7 +15,7 @@ if [ $NXT_INET6 = YES ]; then #include #include - int main() { + int main(void) { struct sockaddr_in6 sin6; sin6.sin6_family = AF_INET6; @@ -36,7 +36,7 @@ nxt_feature_libs= nxt_feature_test="#include #include - int main() { + int main(void) { struct sockaddr sa; sa.sa_len = 0; @@ -54,7 +54,7 @@ nxt_feature_libs= nxt_feature_test="#include #include - int main() { + int main(void) { printf(\"%d\", (int) sizeof(struct sockaddr)); return 0; }" @@ -70,7 +70,7 @@ nxt_feature_test="#include #include #include - int main() { + int main(void) { printf(\"%d\", (int) sizeof(struct sockaddr_in)); return 0; }" @@ -86,7 +86,7 @@ nxt_feature_test="#include #include #include - int main() { + int main(void) { printf(\"%d\", (int) sizeof(struct sockaddr_in6)); return 0; }" @@ -102,7 +102,7 @@ nxt_feature_test="#include #include #include - int main() { + int main(void) { printf(\"%d\", (int) sizeof(struct sockaddr_un)); return 0; }" @@ -117,7 +117,7 @@ nxt_feature_libs= nxt_feature_test="#include #include - int main() { + int main(void) { printf(\"%d\", (int) sizeof(struct sockaddr_storage)); return 0; }" @@ -132,7 +132,7 @@ nxt_feature_libs= nxt_feature_test="#include #include - int main() { + int main(void) { int pair[2]; if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) != 0) @@ -150,7 +150,7 @@ nxt_feature_libs= nxt_feature_test="#include #include - int main() { + int main(void) { struct msghdr msg; printf(\"%d\", (int) sizeof(msg.msg_control)); @@ -175,7 +175,7 @@ if [ $NXT_SYSTEM != DragonFly ]; then nxt_feature_test="#define _GNU_SOURCE #include - int main() { + int main(void) { return SO_PASSCRED == 0; }" . auto/feature @@ -191,7 +191,7 @@ if [ $NXT_SYSTEM != DragonFly ]; then #include #include - int main() { + int main(void) { return sizeof(struct ucred); }" . auto/feature @@ -206,7 +206,7 @@ if [ $NXT_SYSTEM != DragonFly ]; then nxt_feature_test="#define _GNU_SOURCE #include - int main() { + int main(void) { return sizeof(struct cmsgcred); }" . auto/feature @@ -220,7 +220,7 @@ nxt_feature_incs= nxt_feature_libs= nxt_feature_test="#include - int main() { + int main(void) { return 0; }" . auto/feature @@ -235,7 +235,7 @@ nxt_feature_test="#include #include #include - int main() { + int main(void) { int nb; nb = 0; @@ -255,7 +255,7 @@ nxt_feature_libs= nxt_feature_test="#define _GNU_SOURCE #include - int main() { + int main(void) { socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); return 0; }" @@ -273,7 +273,7 @@ nxt_feature_test="#define _GNU_SOURCE #include #include - int main() { + int main(void) { accept4(0, NULL, NULL, SOCK_NONBLOCK); return 0; }" diff --git a/auto/ssltls b/auto/ssltls index d678ba74..6512d330 100644 --- a/auto/ssltls +++ b/auto/ssltls @@ -23,7 +23,7 @@ if [ $NXT_OPENSSL = YES ]; then nxt_feature_libs="-lssl -lcrypto" nxt_feature_test="#include - int main() { + int main(void) { SSL_library_init(); return 0; }" @@ -39,7 +39,7 @@ if [ $NXT_OPENSSL = YES ]; then nxt_feature_run=value nxt_feature_test="#include - int main() { + int main(void) { printf(\"\\\"%s\\\"\", SSLeay_version(SSLEAY_VERSION)); return 0; @@ -61,7 +61,7 @@ if [ $NXT_OPENSSL = YES ]; then nxt_feature_libs="$NXT_OPENSSL_LIBS" nxt_feature_test="#include - int main() { + int main(void) { SSL_CONF_cmd(NULL, NULL, NULL); return 0; }" @@ -75,7 +75,7 @@ if [ $NXT_OPENSSL = YES ]; then nxt_feature_libs="$NXT_OPENSSL_LIBS" nxt_feature_test="#include - int main() { + int main(void) { #if (OPENSSL_NO_TLSEXT) #error OpenSSL: no tlsext support. #else @@ -100,7 +100,7 @@ if [ $NXT_GNUTLS = YES ]; then nxt_feature_libs=$NXT_GNUTLS_LIBS nxt_feature_test="#include - int main() { + int main(void) { gnutls_global_init(); gnutls_global_deinit(); return 0; @@ -121,7 +121,7 @@ if [ $NXT_GNUTLS = YES ]; then nxt_feature_libs=$NXT_GNUTLS_LIBS nxt_feature_test="#include - int main() { + int main(void) { gnutls_transport_set_vec_push_function(NULL, NULL); return 0; }" @@ -135,7 +135,7 @@ if [ $NXT_GNUTLS = YES ]; then nxt_feature_libs=$NXT_GNUTLS_LIBS nxt_feature_test="#include - int main() { + int main(void) { gnutls_global_set_time_function(NULL); return 0; }" @@ -160,7 +160,7 @@ if [ $NXT_CYASSL = YES ]; then nxt_feature_libs="-lcyassl" nxt_feature_test="#include - int main() { + int main(void) { CyaSSL_Init(); CyaSSL_Cleanup(); return 0; @@ -191,7 +191,7 @@ if [ $NXT_POLARSSL = YES ]; then nxt_feature_libs="-lpolarssl" nxt_feature_test="#include - int main() { + int main(void) { ssl_context ssl; memset(&ssl, '\0', sizeof(ssl)); ssl_init(&ssl); diff --git a/auto/threads b/auto/threads index ff33eaac..67b46690 100644 --- a/auto/threads +++ b/auto/threads @@ -47,7 +47,7 @@ nxt_feature_libs=$NXT_PTHREAD nxt_feature_test="#define _GNU_SOURCE #include - int main() { + int main(void) { pthread_yield(); return 0; }" @@ -65,7 +65,7 @@ if [ $nxt_found = no ]; then nxt_feature_libs=$NXT_PTHREAD nxt_feature_test="#include - int main() { + int main(void) { pthread_yield_np(); return 0; }" @@ -82,7 +82,7 @@ nxt_feature_incs= nxt_feature_libs=$NXT_PTHREAD nxt_feature_test="#include - int main() { + int main(void) { pthread_spinlock_t lock; if (pthread_spin_init(&lock, PTHREAD_PROCESS_PRIVATE) != 0) @@ -112,7 +112,7 @@ if [ $nxt_found = yes ]; then pthread_spinlock_t lock = 0; - int main() { + int main(void) { if (pthread_spin_trylock(&lock) != 0) return 1; if (pthread_spin_unlock(&lock) != 0) @@ -130,7 +130,7 @@ nxt_feature_incs= nxt_feature_libs= nxt_feature_test="#include - int main() { + int main(void) { sem_t sem; struct timespec ts; @@ -199,7 +199,7 @@ nxt_feature_test="#include return NULL; } - int main() { + int main(void) { void *n; pthread_t pt; @@ -227,7 +227,7 @@ if [ $nxt_found = no ]; then nxt_feature_libs=$NXT_PTHREAD nxt_feature_test="#include - int main() { + int main(void) { pthread_key_t key = -1; if (pthread_key_create(&key, NULL)) @@ -250,7 +250,7 @@ if [ $nxt_found = no ]; then #include #include - int main() { + int main(void) { printf(\"%d\", PTHREAD_KEYS_MAX); return 0; }" diff --git a/auto/time b/auto/time index 7663e62f..402a219c 100644 --- a/auto/time +++ b/auto/time @@ -13,7 +13,7 @@ nxt_feature_incs= nxt_feature_libs="-lrt" nxt_feature_test="#include - int main() { + int main(void) { struct timespec ts; if (clock_gettime(CLOCK_REALTIME_COARSE, &ts) == -1) @@ -36,7 +36,7 @@ nxt_feature_incs= nxt_feature_libs= nxt_feature_test="#include - int main() { + int main(void) { struct timespec ts; if (clock_gettime(CLOCK_REALTIME_FAST, &ts) == -1) @@ -53,7 +53,7 @@ nxt_feature_incs= nxt_feature_libs= nxt_feature_test="#include - int main() { + int main(void) { struct timespec ts; if (clock_gettime(CLOCK_REALTIME, &ts) == -1) @@ -87,7 +87,7 @@ nxt_feature_incs= nxt_feature_libs="-lrt" nxt_feature_test="#include - int main() { + int main(void) { struct timespec ts; if (clock_gettime(CLOCK_MONOTONIC_COARSE, &ts) == -1) @@ -110,7 +110,7 @@ nxt_feature_incs= nxt_feature_libs= nxt_feature_test="#include - int main() { + int main(void) { struct timespec ts; if (clock_gettime(CLOCK_MONOTONIC_FAST, &ts) == -1) @@ -127,7 +127,7 @@ nxt_feature_incs= nxt_feature_libs= nxt_feature_test="#include - int main() { + int main(void) { struct timespec ts; if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) @@ -163,7 +163,7 @@ nxt_feature_libs="-lhg" nxt_feature_test="#include #include - int main() { + int main(void) { hg_gethrtime(); return 0; }" @@ -181,7 +181,7 @@ nxt_feature_incs= nxt_feature_libs= nxt_feature_test="#include - int main() { + int main(void) { time_t t; struct tm tm; @@ -199,7 +199,7 @@ nxt_feature_incs= nxt_feature_libs= nxt_feature_test="#include - int main() { + int main(void) { altzone = 0; return 0; }" @@ -213,7 +213,7 @@ nxt_feature_incs= nxt_feature_libs= nxt_feature_test="#include - int main() { + int main(void) { time_t t; struct tm tm; diff --git a/auto/types b/auto/types index 91d53b6f..c0a871dd 100644 --- a/auto/types +++ b/auto/types @@ -18,7 +18,7 @@ nxt_feature_incs= nxt_feature_libs= nxt_feature_test="#include - int main() { + int main(void) { printf(\"%d\", (int) sizeof(int)); return 0; }" @@ -32,7 +32,7 @@ nxt_feature_incs= nxt_feature_libs= nxt_feature_test="#include - int main() { + int main(void) { printf(\"%d\", (int) sizeof(long)); return 0; }" @@ -46,7 +46,7 @@ nxt_feature_incs= nxt_feature_libs= nxt_feature_test="#include - int main() { + int main(void) { printf(\"%d\", (int) sizeof(long long)); return 0; }" @@ -60,7 +60,7 @@ nxt_feature_incs= nxt_feature_libs= nxt_feature_test="#include - int main() { + int main(void) { printf(\"%d\", (int) sizeof(void *)); return 0; }" @@ -80,7 +80,7 @@ nxt_feature_incs= nxt_feature_libs= nxt_feature_test="#include - int main() { + int main(void) { printf(\"%d\", (int) sizeof(size_t)); return 0; }" @@ -96,7 +96,7 @@ nxt_feature_test="#define _FILE_OFFSET_BITS 64 #include #include - int main() { + int main(void) { printf(\"%d\", (int) sizeof(off_t)); return 0; }" @@ -111,7 +111,7 @@ nxt_feature_libs= nxt_feature_test="#include #include - int main() { + int main(void) { printf(\"%d\", (int) sizeof(time_t)); return 0; }" diff --git a/auto/unix b/auto/unix index 45c6a139..1307bdbd 100644 --- a/auto/unix +++ b/auto/unix @@ -13,7 +13,7 @@ nxt_feature_libs= nxt_feature_test="#include #include - int main() { + int main(void) { char buf[4]; if (getrandom(buf, 4, 0) < 0) { @@ -35,7 +35,7 @@ if [ $nxt_found = no ]; then #include #include - int main() { + int main(void) { char buf[4]; if (syscall(SYS_getrandom, buf, 4, 0) < 0) { @@ -56,7 +56,7 @@ if [ $nxt_found = no ]; then nxt_feature_name=NXT_HAVE_GETENTROPY nxt_feature_test="#include - int main() { + int main(void) { char buf[4]; if (getentropy(buf, 4) == -1) { @@ -78,7 +78,7 @@ if [ $nxt_found = no ]; then nxt_feature_test="#include #include - int main() { + int main(void) { char buf[4]; if (getentropy(buf, 4) == -1) { @@ -99,7 +99,7 @@ nxt_feature_libs= nxt_feature_test="#include #include - int main() { + int main(void) { ucontext_t uc; if (getcontext(&uc) == 0) { @@ -126,7 +126,7 @@ if [ $nxt_found = no ]; then #include #include - int main() { + int main(void) { ucontext_t uc; if (getcontext(&uc) == 0) { @@ -155,7 +155,7 @@ nxt_feature_libs= nxt_feature_test="#include #include - int main() { + int main(void) { void *h = dlopen(NULL, RTLD_NOW | RTLD_GLOBAL); dlsym(h, \"\"); dlclose(h); @@ -188,7 +188,7 @@ nxt_feature_libs= nxt_feature_test="#include #include - int main() { + int main(void) { setproctitle(\"%s\", \"title\"); return 0; }" @@ -204,7 +204,7 @@ nxt_feature_libs= nxt_feature_test="#include #include - int main() { + int main(void) { getgrouplist(\"root\", 0, NULL, NULL); return 0; }" -- cgit From 58248a6220540db89e69c928a5d8ad6be2a326fb Mon Sep 17 00:00:00 2001 From: Andrew Clayton Date: Fri, 28 Oct 2022 00:54:01 +0100 Subject: Fixed some function definitions. Future releases of GCC will render function definitions like func() invalid by default. See the previous commit 09f88c9 ("Fixed main() prototypes in auto tests.") for details. Such functions should be defined like func(void) This is a good thing to do regardless of the upcoming GCC changes. Reviewed-by: Alejandro Colomar Signed-off-by: Andrew Clayton --- src/nxt_capability.c | 2 +- src/nxt_port.c | 4 ++-- src/test/nxt_unit_websocket_chat.c | 2 +- src/test/nxt_unit_websocket_echo.c | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/nxt_capability.c b/src/nxt_capability.c index 24fd55d0..9f36ab99 100644 --- a/src/nxt_capability.c +++ b/src/nxt_capability.c @@ -50,7 +50,7 @@ nxt_capability_set(nxt_task_t *task, nxt_capabilities_t *cap) #if (NXT_HAVE_LINUX_CAPABILITY) static uint32_t -nxt_capability_linux_get_version() +nxt_capability_linux_get_version(void) { struct __user_cap_header_struct hdr; diff --git a/src/nxt_port.c b/src/nxt_port.c index ed7050f3..b9964df4 100644 --- a/src/nxt_port.c +++ b/src/nxt_port.c @@ -144,14 +144,14 @@ nxt_port_release(nxt_task_t *task, nxt_port_t *port) nxt_port_id_t -nxt_port_get_next_id() +nxt_port_get_next_id(void) { return nxt_atomic_fetch_add(&nxt_port_last_id, 1); } void -nxt_port_reset_next_id() +nxt_port_reset_next_id(void) { nxt_port_last_id = 1; } diff --git a/src/test/nxt_unit_websocket_chat.c b/src/test/nxt_unit_websocket_chat.c index 39f8a440..ec7c2cc3 100644 --- a/src/test/nxt_unit_websocket_chat.c +++ b/src/test/nxt_unit_websocket_chat.c @@ -201,7 +201,7 @@ ws_chat_close_handler(nxt_unit_request_info_t *req) int -main() +main(void) { nxt_unit_ctx_t *ctx; nxt_unit_init_t init; diff --git a/src/test/nxt_unit_websocket_echo.c b/src/test/nxt_unit_websocket_echo.c index 2a89cdc0..eab2e45f 100644 --- a/src/test/nxt_unit_websocket_echo.c +++ b/src/test/nxt_unit_websocket_echo.c @@ -83,7 +83,7 @@ ws_echo_websocket_handler(nxt_unit_websocket_frame_t *ws) int -main() +main(void) { nxt_unit_ctx_t *ctx; nxt_unit_init_t init; -- cgit From a03274456b54cbc39e220b9dd73c3fc3fb935e46 Mon Sep 17 00:00:00 2001 From: Andrew Clayton Date: Fri, 16 Sep 2022 14:38:53 +0100 Subject: PHP: allowed to specify URLs without a trailing '/'. Both @lucatacconi & @mwoodpatrick reported what appears to be the same issue on GitHub. Namely that when using the PHP language module and trying to access a URL that is a directory but without specifying the trailing '/', they were getting a '503 Service Unavailable' error. Note: This is when _not_ using the 'script' option. E.g with the following config { "listeners": { "[::1]:8080": { "pass": "applications/php" } }, "applications": { "php": { "type": "php", "root": "/var/tmp/unit-php" } } } and with a directory path of /var/tmp/unit-php/foo containing an index.php, you would see the following $ curl http://localhost/foo Error 503 Error 503 However $ curl http://localhost/foo/ would work and serve up the index.php This commit fixes the above so you get the desired behaviour without specifying the trailing '/' by doing the following 1] If the URL doesn't end in .php and doesn't have a trailing '/' then check if the requested path is a directory. 2) If it is a directory then create a 301 re-direct pointing to it. This matches the behaviour of the likes of nginx, Apache and lighttpd. This also matches the behaviour of the "share" action in Unit. This doesn't effect the behaviour of the 'script' option which bypasses the nxt_php_dynamic_request() function. This also adds a couple of tests to test/test_php_application.py to ensure this continues to work. Closes: Closes: Signed-off-by: Andrew Clayton --- docs/changes.xml | 7 ++++ src/nxt_php_sapi.c | 90 +++++++++++++++++++++++++++++++++++++++++--- src/nxt_router.c | 6 +++ src/nxt_unit_request.h | 2 + test/test_php_application.py | 44 ++++++++++++++++++++++ 5 files changed, 143 insertions(+), 6 deletions(-) diff --git a/docs/changes.xml b/docs/changes.xml index 59c55fea..014f243d 100644 --- a/docs/changes.xml +++ b/docs/changes.xml @@ -85,6 +85,13 @@ fix error in connection statistics when using proxy. + + +PHP directory URLs without a trailing '/' would give a 503 error (fixed with +a 301 re-direct). + + + diff --git a/src/nxt_php_sapi.c b/src/nxt_php_sapi.c index ca49b289..924dd4e6 100644 --- a/src/nxt_php_sapi.c +++ b/src/nxt_php_sapi.c @@ -14,6 +14,7 @@ #include #include #include +#include #if (PHP_VERSION_ID >= 50400) @@ -100,6 +101,8 @@ static void nxt_php_str_trim_trail(nxt_str_t *str, u_char t); static void nxt_php_str_trim_lead(nxt_str_t *str, u_char t); nxt_inline u_char *nxt_realpath(const void *c); +static nxt_int_t nxt_php_do_301(nxt_unit_request_info_t *req); + static void nxt_php_request_handler(nxt_unit_request_info_t *req); static void nxt_php_dynamic_request(nxt_php_run_ctx_t *ctx, nxt_unit_request_t *r); @@ -920,6 +923,63 @@ nxt_realpath(const void *c) } +static nxt_int_t +nxt_php_do_301(nxt_unit_request_info_t *req) +{ + char *p, *url, *port; + uint32_t size; + const char *proto; + nxt_unit_request_t *r; + + r = req->request; + + url = nxt_malloc(sizeof("https://") - 1 + + r->server_name_length + + r->local_port_length + 1 + + r->path_length + 1 + + r->query_length + 1 + + 1); + if (nxt_slow_path(url == NULL)) { + return NXT_UNIT_ERROR; + } + + proto = r->tls ? "https://" : "http://"; + p = nxt_cpymem(url, proto, strlen(proto)); + p = nxt_cpymem(p, nxt_unit_sptr_get(&r->server_name), + r->server_name_length); + + port = nxt_unit_sptr_get(&r->local_port); + if (r->local_port_length > 0 + && !(r->tls && strcmp(port, "443") == 0) + && !(!r->tls && strcmp(port, "80") == 0)) + { + *p++ = ':'; + p = nxt_cpymem(p, port, r->local_port_length); + } + + p = nxt_cpymem(p, nxt_unit_sptr_get(&r->path), r->path_length); + *p++ = '/'; + + if (r->query_length > 0) { + *p++ = '?'; + p = nxt_cpymem(p, nxt_unit_sptr_get(&r->query), r->query_length); + } + + *p = '\0'; + + size = p - url; + + nxt_unit_response_init(req, NXT_HTTP_MOVED_PERMANENTLY, 1, + nxt_length("Location") + size); + nxt_unit_response_add_field(req, "Location", nxt_length("Location"), + url, size); + + nxt_free(url); + + return NXT_UNIT_OK; +} + + static void nxt_php_request_handler(nxt_unit_request_info_t *req) { @@ -975,15 +1035,33 @@ nxt_php_dynamic_request(nxt_php_run_ctx_t *ctx, nxt_unit_request_t *r) } else if (path.start[path.length - 1] == '/') { script_name = *ctx->index; - } else { - if (nxt_slow_path(path.length < 4 - || nxt_memcmp(path.start + (path.length - 4), - ".php", 4))) - { - nxt_unit_request_done(ctx->req, NXT_UNIT_ERROR); + } else if (path.length < 4 + || nxt_memcmp(path.start + (path.length - 4), ".php", 4) != 0) + { + char tpath[PATH_MAX]; + nxt_int_t ec; + struct stat sb; + + ec = NXT_UNIT_ERROR; + + if (ctx->root->length + path.length + 1 > PATH_MAX) { + nxt_unit_request_done(ctx->req, ec); return; } + + p = nxt_cpymem(tpath, ctx->root->start, ctx->root->length); + p = nxt_cpymem(p, path.start, path.length); + *p = '\0'; + + ret = stat(tpath, &sb); + if (ret == 0 && S_ISDIR(sb.st_mode)) { + ec = nxt_php_do_301(ctx->req); + } + + nxt_unit_request_done(ctx->req, ec); + + return; } ctx->script_filename.length = ctx->root->length diff --git a/src/nxt_router.c b/src/nxt_router.c index 6430aea4..cfd79bf5 100644 --- a/src/nxt_router.c +++ b/src/nxt_router.c @@ -5254,6 +5254,12 @@ nxt_router_prepare_msg(nxt_task_t *task, nxt_http_request_t *r, p = nxt_cpymem(p, nxt_sockaddr_address(r->local), r->local->address_length); *p++ = '\0'; + req->local_port_length = nxt_sockaddr_port_length(r->local); + nxt_unit_sptr_set(&req->local_port, p); + p = nxt_cpymem(p, nxt_sockaddr_port(r->local), + nxt_sockaddr_port_length(r->local)); + *p++ = '\0'; + req->tls = r->tls; req->websocket_handshake = r->websocket_handshake; diff --git a/src/nxt_unit_request.h b/src/nxt_unit_request.h index f6b96838..a6ebf0b6 100644 --- a/src/nxt_unit_request.h +++ b/src/nxt_unit_request.h @@ -19,6 +19,7 @@ struct nxt_unit_request_s { uint8_t version_length; uint8_t remote_length; uint8_t local_addr_length; + uint8_t local_port_length; uint8_t tls; uint8_t websocket_handshake; uint8_t app_target; @@ -39,6 +40,7 @@ struct nxt_unit_request_s { nxt_unit_sptr_t version; nxt_unit_sptr_t remote; nxt_unit_sptr_t local_addr; + nxt_unit_sptr_t local_port; nxt_unit_sptr_t server_name; nxt_unit_sptr_t target; nxt_unit_sptr_t path; diff --git a/test/test_php_application.py b/test/test_php_application.py index f1dcc995..f442f551 100644 --- a/test/test_php_application.py +++ b/test/test_php_application.py @@ -4,6 +4,7 @@ import re import shutil import signal import time +from pathlib import Path import pytest from unit.applications.lang.php import TestApplicationPHP @@ -620,6 +621,49 @@ opcache.preload_user = %(user)s assert resp['status'] == 200, 'status' assert resp['body'] != '', 'body not empty' + def test_php_application_trailing_slash(self, temp_dir): + new_root = temp_dir + "/php-root" + os.makedirs(new_root + '/path') + + Path(new_root + '/path/index.php').write_text('') + + addr = temp_dir + '/sock' + + assert 'success' in self.conf( + { + "listeners": { + "*:7080": {"pass": "applications/php-path"}, + "unix:" + addr: {"pass": "applications/php-path"}, + }, + "applications": { + "php-path": { + "type": self.get_application_type(), + "processes": {"spare": 0}, + "root": new_root, + } + }, + } + ), 'configure trailing slash' + + assert self.get(url='/path/')['status'] == 200, 'uri with trailing /' + + resp = self.get(url='/path?q=a') + assert resp['status'] == 301, 'uri without trailing /' + assert ( + resp['headers']['Location'] == 'http://localhost:7080/path/?q=a' + ), 'Location with query string' + + resp = self.get( + sock_type='unix', + addr=addr, + url='/path', + headers={'Host': 'foo', 'Connection': 'close'}, + ) + assert resp['status'] == 301, 'uri without trailing /' + assert ( + resp['headers']['Location'] == 'http://foo/path/' + ), 'Location with custom Host over UDS' + def test_php_application_extension_check(self, temp_dir): self.load('phpinfo') -- cgit From 1b05161107112f09c15b128090284bb6de5b4f70 Mon Sep 17 00:00:00 2001 From: Alejandro Colomar Date: Wed, 2 Nov 2022 21:45:40 +0100 Subject: Removed the unsafe nxt_memcmp() wrapper for memcmp(3). The casts are unnecessary, since memcmp(3)'s arguments are 'void *'. It might have been necessary in the times of K&R, where 'void *' didn't exist. Nowadays, it's unnecessary, and _very_ unsafe, since casts can hide all classes of bugs by silencing most compiler warnings. The changes from nxt_memcmp() to memcmp(3) were scripted: $ find src/ -type f \ | grep '\.[ch]$' \ | xargs sed -i 's/nxt_memcmp/memcmp/' Reviewed-by: Andrew Clayton Signed-off-by: Alejandro Colomar --- src/nxt_application.c | 2 +- src/nxt_conf.c | 6 +++--- src/nxt_controller.c | 4 ++-- src/nxt_errno.c | 6 +++--- src/nxt_h1proto.c | 6 +++--- src/nxt_http_parse.c | 6 +++--- src/nxt_http_route.c | 18 +++++++++--------- src/nxt_http_route_addr.c | 4 ++-- src/nxt_http_variables.c | 4 ++-- src/nxt_openssl.c | 2 +- src/nxt_php_sapi.c | 2 +- src/nxt_sockaddr.c | 6 +++--- src/nxt_string.c | 4 ++-- src/nxt_string.h | 10 +++------- src/test/nxt_http_parse_test.c | 2 +- src/test/nxt_tests.c | 2 +- src/test/nxt_utf8_file_name_test.c | 2 +- src/test/nxt_utf8_test.c | 2 +- 18 files changed, 42 insertions(+), 46 deletions(-) diff --git a/src/nxt_application.c b/src/nxt_application.c index 556f1ffb..786c768b 100644 --- a/src/nxt_application.c +++ b/src/nxt_application.c @@ -366,7 +366,7 @@ nxt_discovery_module(nxt_task_t *task, nxt_mp_t *mp, nxt_array_t *modules, &app->type, app->version, name); if (app->compat_length != sizeof(compat) - || nxt_memcmp(app->compat, compat, sizeof(compat)) != 0) + || memcmp(app->compat, compat, sizeof(compat)) != 0) { nxt_log(task, NXT_LOG_NOTICE, "incompatible module %s", name); diff --git a/src/nxt_conf.c b/src/nxt_conf.c index c6312f3d..d04aa45c 100644 --- a/src/nxt_conf.c +++ b/src/nxt_conf.c @@ -1389,7 +1389,7 @@ nxt_conf_json_parse_value(nxt_mp_t *mp, nxt_conf_value_t *value, u_char *start, case 't': if (nxt_fast_path(end - start >= 4 - && nxt_memcmp(start, "true", 4) == 0)) + && memcmp(start, "true", 4) == 0)) { value->u.boolean = 1; value->type = NXT_CONF_VALUE_BOOLEAN; @@ -1401,7 +1401,7 @@ nxt_conf_json_parse_value(nxt_mp_t *mp, nxt_conf_value_t *value, u_char *start, case 'f': if (nxt_fast_path(end - start >= 5 - && nxt_memcmp(start, "false", 5) == 0)) + && memcmp(start, "false", 5) == 0)) { value->u.boolean = 0; value->type = NXT_CONF_VALUE_BOOLEAN; @@ -1413,7 +1413,7 @@ nxt_conf_json_parse_value(nxt_mp_t *mp, nxt_conf_value_t *value, u_char *start, case 'n': if (nxt_fast_path(end - start >= 4 - && nxt_memcmp(start, "null", 4) == 0)) + && memcmp(start, "null", 4) == 0)) { value->type = NXT_CONF_VALUE_NULL; return start + 4; diff --git a/src/nxt_controller.c b/src/nxt_controller.c index 99c7ca10..5f3e55ef 100644 --- a/src/nxt_controller.c +++ b/src/nxt_controller.c @@ -1263,7 +1263,7 @@ nxt_controller_process_config(nxt_task_t *task, nxt_controller_request_t *req, /* Skip UTF-8 BOM. */ if (nxt_buf_mem_used_size(mbuf) >= 3 - && nxt_memcmp(mbuf->pos, "\xEF\xBB\xBF", 3) == 0) + && memcmp(mbuf->pos, "\xEF\xBB\xBF", 3) == 0) { mbuf->pos += 3; } @@ -1940,7 +1940,7 @@ nxt_controller_process_control(nxt_task_t *task, } if (!nxt_str_start(path, "applications/", 13) - || nxt_memcmp(path->start + path->length - 8, "/restart", 8) != 0) + || memcmp(path->start + path->length - 8, "/restart", 8) != 0) { goto not_found; } diff --git a/src/nxt_errno.c b/src/nxt_errno.c index 869a970f..47479a82 100644 --- a/src/nxt_errno.c +++ b/src/nxt_errno.c @@ -78,7 +78,7 @@ nxt_strerror_start(void) if (length == 0 /* HP-UX empty strings. */ || nxt_errno == NXT_EINVAL - || nxt_memcmp(msg, "Unknown error", 13) == 0) + || memcmp(msg, "Unknown error", 13) == 0) { invalid++; continue; @@ -86,8 +86,8 @@ nxt_strerror_start(void) #if (NXT_AIX) - if (nxt_memcmp(msg, "Error ", 6) == 0 - && nxt_memcmp(msg + length - 10, " occurred.", 9) == 0) + if (memcmp(msg, "Error ", 6) == 0 + && memcmp(msg + length - 10, " occurred.", 9) == 0) { invalid++; continue; diff --git a/src/nxt_h1proto.c b/src/nxt_h1proto.c index 852b4866..3acba088 100644 --- a/src/nxt_h1proto.c +++ b/src/nxt_h1proto.c @@ -823,7 +823,7 @@ nxt_h1p_transfer_encoding(void *ctx, nxt_http_field_t *field, uintptr_t data) field->hopbyhop = 1; if (field->value_length == 7 - && nxt_memcmp(field->value, "chunked", 7) == 0) + && memcmp(field->value, "chunked", 7) == 0) { te = NXT_HTTP_TE_CHUNKED; @@ -2594,7 +2594,7 @@ nxt_h1p_peer_header_parse(nxt_http_peer_t *peer, nxt_buf_mem_t *bm) p = bm->pos; - if (nxt_slow_path(nxt_memcmp(p, "HTTP/1.", 7) != 0 + if (nxt_slow_path(memcmp(p, "HTTP/1.", 7) != 0 || (p[7] != '0' && p[7] != '1'))) { return NXT_ERROR; @@ -2868,7 +2868,7 @@ nxt_h1p_peer_transfer_encoding(void *ctx, nxt_http_field_t *field, field->skip = 1; if (field->value_length == 7 - && nxt_memcmp(field->value, "chunked", 7) == 0) + && memcmp(field->value, "chunked", 7) == 0) { r->peer->proto.h1->chunked = 1; } diff --git a/src/nxt_http_parse.c b/src/nxt_http_parse.c index 1bb4291f..f39d8f67 100644 --- a/src/nxt_http_parse.c +++ b/src/nxt_http_parse.c @@ -357,7 +357,7 @@ space_after_target: } while (*p == ' '); - if (nxt_memcmp(p, "HTTP/", nxt_min(end - p, 5)) == 0) { + if (memcmp(p, "HTTP/", nxt_min(end - p, 5)) == 0) { switch (end - p) { case 8: @@ -412,7 +412,7 @@ space_after_target: if (nxt_fast_path(ver.ui64 == http11.ui64 || ver.ui64 == http10.ui64 - || (nxt_memcmp(ver.str, "HTTP/1.", 7) == 0 + || (memcmp(ver.str, "HTTP/1.", 7) == 0 && ver.s.minor >= '0' && ver.s.minor <= '9'))) { rp->version.ui64 = ver.ui64; @@ -464,7 +464,7 @@ space_after_target: return nxt_http_parse_field_name(rp, pos, end); } - if (nxt_memcmp(ver.s.prefix, "HTTP/", 5) == 0 + if (memcmp(ver.s.prefix, "HTTP/", 5) == 0 && ver.s.major >= '0' && ver.s.major <= '9' && ver.s.point == '.' && ver.s.minor >= '0' && ver.s.minor <= '9') diff --git a/src/nxt_http_route.c b/src/nxt_http_route.c index cdc9077f..9aa23899 100644 --- a/src/nxt_http_route.c +++ b/src/nxt_http_route.c @@ -1740,15 +1740,15 @@ nxt_http_route_addr_pattern_match(nxt_http_route_addr_pattern_t *p, break; case NXT_HTTP_ROUTE_ADDR_EXACT: - match = (nxt_memcmp(&sin->sin_addr, &p->addr.v4.start, + match = (memcmp(&sin->sin_addr, &p->addr.v4.start, sizeof(struct in_addr)) == 0); break; case NXT_HTTP_ROUTE_ADDR_RANGE: - match = (nxt_memcmp(&sin->sin_addr, &p->addr.v4.start, + match = (memcmp(&sin->sin_addr, &p->addr.v4.start, sizeof(struct in_addr)) >= 0 - && nxt_memcmp(&sin->sin_addr, &p->addr.v4.end, + && memcmp(&sin->sin_addr, &p->addr.v4.end, sizeof(struct in_addr)) <= 0); break; @@ -1786,15 +1786,15 @@ nxt_http_route_addr_pattern_match(nxt_http_route_addr_pattern_t *p, break; case NXT_HTTP_ROUTE_ADDR_EXACT: - match = (nxt_memcmp(&sin6->sin6_addr, &p->addr.v6.start, + match = (memcmp(&sin6->sin6_addr, &p->addr.v6.start, sizeof(struct in6_addr)) == 0); break; case NXT_HTTP_ROUTE_ADDR_RANGE: - match = (nxt_memcmp(&sin6->sin6_addr, &p->addr.v6.start, + match = (memcmp(&sin6->sin6_addr, &p->addr.v6.start, sizeof(struct in6_addr)) >= 0 - && nxt_memcmp(&sin6->sin6_addr, &p->addr.v6.end, + && memcmp(&sin6->sin6_addr, &p->addr.v6.end, sizeof(struct in6_addr)) <= 0); break; @@ -1937,7 +1937,7 @@ nxt_http_route_test_argument(nxt_http_request_t *r, if (rule->u.name.hash == nv->hash && rule->u.name.length == nv->name_length - && nxt_memcmp(rule->u.name.start, nv->name, nv->name_length) == 0) + && memcmp(rule->u.name.start, nv->name, nv->name_length) == 0) { ret = nxt_http_route_test_rule(r, rule, nv->value, nv->value_length); @@ -2015,7 +2015,7 @@ nxt_http_route_test_cookie(nxt_http_request_t *r, if (rule->u.name.hash == nv->hash && rule->u.name.length == nv->name_length - && nxt_memcmp(rule->u.name.start, nv->name, nv->name_length) == 0) + && memcmp(rule->u.name.start, nv->name, nv->name_length) == 0) { ret = nxt_http_route_test_rule(r, rule, nv->value, nv->value_length); @@ -2158,7 +2158,7 @@ nxt_http_route_memcmp(u_char *start, u_char *test, size_t test_length, nxt_int_t n; if (case_sensitive) { - n = nxt_memcmp(start, test, test_length); + n = memcmp(start, test, test_length); } else { n = nxt_memcasecmp(start, test, test_length); diff --git a/src/nxt_http_route_addr.c b/src/nxt_http_route_addr.c index 34455af4..06b30d29 100644 --- a/src/nxt_http_route_addr.c +++ b/src/nxt_http_route_addr.c @@ -115,7 +115,7 @@ nxt_http_route_addr_pattern_parse(nxt_mp_t *mp, return NXT_ADDR_PATTERN_FORMAT_ERROR; } - if (nxt_slow_path(nxt_memcmp(&inet6->start, &inet6->end, + if (nxt_slow_path(memcmp(&inet6->start, &inet6->end, sizeof(struct in6_addr)) > 0)) { return NXT_ADDR_PATTERN_RANGE_OVERLAP_ERROR; @@ -223,7 +223,7 @@ nxt_http_route_addr_pattern_parse(nxt_mp_t *mp, return NXT_ADDR_PATTERN_FORMAT_ERROR; } - if (nxt_slow_path(nxt_memcmp(&inet->start, &inet->end, + if (nxt_slow_path(memcmp(&inet->start, &inet->end, sizeof(struct in_addr)) > 0)) { return NXT_ADDR_PATTERN_RANGE_OVERLAP_ERROR; diff --git a/src/nxt_http_variables.c b/src/nxt_http_variables.c index e01bcdb9..eb341716 100644 --- a/src/nxt_http_variables.c +++ b/src/nxt_http_variables.c @@ -421,7 +421,7 @@ nxt_http_var_arg(nxt_task_t *task, nxt_str_t *str, void *ctx, uint16_t field) if (vf->hash == nv->hash && vf->name.length == nv->name_length - && nxt_memcmp(vf->name.start, nv->name, nv->name_length) == 0) + && memcmp(vf->name.start, nv->name, nv->name_length) == 0) { str->start = nv->value; str->length = nv->value_length; @@ -499,7 +499,7 @@ nxt_http_var_cookie(nxt_task_t *task, nxt_str_t *str, void *ctx, uint16_t field) if (vf->hash == nv->hash && vf->name.length == nv->name_length - && nxt_memcmp(vf->name.start, nv->name, nv->name_length) == 0) + && memcmp(vf->name.start, nv->name, nv->name_length) == 0) { str->start = nv->value; str->length = nv->value_length; diff --git a/src/nxt_openssl.c b/src/nxt_openssl.c index 55611511..f56135f3 100644 --- a/src/nxt_openssl.c +++ b/src/nxt_openssl.c @@ -780,7 +780,7 @@ nxt_tls_ticket_key_callback(SSL *s, unsigned char *name, unsigned char *iv, /* decrypt session ticket */ do { - if (nxt_memcmp(name, ticket[i].name, 16) == 0) { + if (memcmp(name, ticket[i].name, 16) == 0) { goto found; } diff --git a/src/nxt_php_sapi.c b/src/nxt_php_sapi.c index 924dd4e6..126a4684 100644 --- a/src/nxt_php_sapi.c +++ b/src/nxt_php_sapi.c @@ -1036,7 +1036,7 @@ nxt_php_dynamic_request(nxt_php_run_ctx_t *ctx, nxt_unit_request_t *r) script_name = *ctx->index; } else if (path.length < 4 - || nxt_memcmp(path.start + (path.length - 4), ".php", 4) != 0) + || memcmp(path.start + (path.length - 4), ".php", 4) != 0) { char tpath[PATH_MAX]; nxt_int_t ec; diff --git a/src/nxt_sockaddr.c b/src/nxt_sockaddr.c index 86c3335e..a076f286 100644 --- a/src/nxt_sockaddr.c +++ b/src/nxt_sockaddr.c @@ -382,7 +382,7 @@ nxt_sockaddr_cmp(nxt_sockaddr_t *sa1, nxt_sockaddr_t *sa2) return 0; } - if (nxt_memcmp(&sa1->u.sockaddr_in6.sin6_addr, + if (memcmp(&sa1->u.sockaddr_in6.sin6_addr, &sa2->u.sockaddr_in6.sin6_addr, 16) != 0) { @@ -401,7 +401,7 @@ nxt_sockaddr_cmp(nxt_sockaddr_t *sa1, nxt_sockaddr_t *sa2) length = sa1->socklen - offsetof(struct sockaddr_un, sun_path); - if (nxt_memcmp(&sa1->u.sockaddr_un.sun_path, + if (memcmp(&sa1->u.sockaddr_un.sun_path, &sa2->u.sockaddr_un.sun_path, length) != 0) { @@ -550,7 +550,7 @@ nxt_sockaddr_parse_optport(nxt_mp_t *mp, nxt_str_t *addr) return NULL; } - if (addr->length > 6 && nxt_memcmp(addr->start, "unix:", 5) == 0) { + if (addr->length > 6 && memcmp(addr->start, "unix:", 5) == 0) { sa = nxt_sockaddr_unix_parse(mp, addr); } else if (addr->start[0] == '[' || nxt_inet6_probe(addr)) { diff --git a/src/nxt_string.c b/src/nxt_string.c index 4d89c23c..1ca595a1 100644 --- a/src/nxt_string.c +++ b/src/nxt_string.c @@ -257,7 +257,7 @@ nxt_memstrn(const u_char *s, const u_char *end, const char *ss, size_t length) return NULL; } - if (nxt_memcmp(s, s2, length) == 0) { + if (memcmp(s, s2, length) == 0) { return (u_char *) s - 1; } } @@ -325,7 +325,7 @@ nxt_rmemstrn(const u_char *s, const u_char *end, const char *ss, size_t length) c1 = *s1; if (c1 == c2) { - if (nxt_memcmp(s1 + 1, s2, length) == 0) { + if (memcmp(s1 + 1, s2, length) == 0) { return (u_char *) s1; } } diff --git a/src/nxt_string.h b/src/nxt_string.h index a8673c61..d021df69 100644 --- a/src/nxt_string.h +++ b/src/nxt_string.h @@ -66,10 +66,6 @@ nxt_cpymem(void *dst, const void *src, size_t length) (void) memmove(dst, src, length) -#define nxt_memcmp(s1, s2, length) \ - memcmp((char *) s1, (char *) s2, length) - - #define nxt_memchr(s, c, length) \ memchr((char *) s, c, length) @@ -132,7 +128,7 @@ NXT_EXPORT char *nxt_str_cstrz(nxt_mp_t *mp, const nxt_str_t *src); #define nxt_strstr_eq(s1, s2) \ (((s1)->length == (s2)->length) \ - && (nxt_memcmp((s1)->start, (s2)->start, (s1)->length) == 0)) + && (memcmp((s1)->start, (s2)->start, (s1)->length) == 0)) #define nxt_strcasestr_eq(s1, s2) \ @@ -141,11 +137,11 @@ NXT_EXPORT char *nxt_str_cstrz(nxt_mp_t *mp, const nxt_str_t *src); #define nxt_str_eq(s, p, _length) \ - (((s)->length == _length) && (nxt_memcmp((s)->start, p, _length) == 0)) + (((s)->length == _length) && (memcmp((s)->start, p, _length) == 0)) #define nxt_str_start(s, p, _length) \ - (((s)->length >= _length) && (nxt_memcmp((s)->start, p, _length) == 0)) + (((s)->length >= _length) && (memcmp((s)->start, p, _length) == 0)) #define nxt_strchr_eq(s, c) \ diff --git a/src/test/nxt_http_parse_test.c b/src/test/nxt_http_parse_test.c index 540309c1..5f1a518c 100644 --- a/src/test/nxt_http_parse_test.c +++ b/src/test/nxt_http_parse_test.c @@ -753,7 +753,7 @@ nxt_http_parse_test_request_line(nxt_http_request_parse_t *rp, return NXT_ERROR; } - if (nxt_memcmp(rp->version.str, test->version, 8) != 0) { + if (memcmp(rp->version.str, test->version, 8) != 0) { nxt_log_alert(log, "http parse test case failed:\n" " - request:\n\"%V\"\n" " - version: \"%*s\" (expected: \"%*s\")", request, diff --git a/src/test/nxt_tests.c b/src/test/nxt_tests.c index f5a1cbd4..03a2a1df 100644 --- a/src/test/nxt_tests.c +++ b/src/test/nxt_tests.c @@ -41,7 +41,7 @@ main(int argc, char **argv) #if (NXT_TEST_RTDTSC) if (nxt_process_argv[1] != NULL - && nxt_memcmp(nxt_process_argv[1], "rbm", 3) == 0) + && memcmp(nxt_process_argv[1], "rbm", 3) == 0) { if (nxt_rbtree1_mb_start(thr) != NXT_OK) { return 1; diff --git a/src/test/nxt_utf8_file_name_test.c b/src/test/nxt_utf8_file_name_test.c index 30546ffb..5723e19b 100644 --- a/src/test/nxt_utf8_file_name_test.c +++ b/src/test/nxt_utf8_file_name_test.c @@ -129,7 +129,7 @@ nxt_utf8_file_name_test(nxt_thread_t *thr) nxt_file_close(&task, &lc_file); - if (n != 4 || nxt_memcmp(utf8, test, 4) != 0) { + if (n != 4 || memcmp(utf8, test, 4) != 0) { nxt_log_alert(thr->log, "nxt_file_read() mismatch"); nxt_file_delete(lc_file.name); diff --git a/src/test/nxt_utf8_test.c b/src/test/nxt_utf8_test.c index bca9a737..31e5bff9 100644 --- a/src/test/nxt_utf8_test.c +++ b/src/test/nxt_utf8_test.c @@ -59,7 +59,7 @@ nxt_utf8_overlong(nxt_thread_t *thr, u_char *overlong, size_t len) size = (p != NULL) ? p - utf8 : 0; - if (len != size || nxt_memcmp(overlong, utf8, size) != 0) { + if (len != size || memcmp(overlong, utf8, size) != 0) { u = 0; for (i = 0; i < len; i++) { -- cgit From ebf02266a2cd663ad4744d3b8c07e211b8f38da1 Mon Sep 17 00:00:00 2001 From: Alejandro Colomar Date: Wed, 2 Nov 2022 21:45:40 +0100 Subject: Removed the unsafe nxt_memchr() wrapper for memchr(3). The casts are unnecessary, since memchr(3)'s argument is 'const void *'. It might have been necessary in the times of K&R, where 'void *' didn't exist. Nowadays, it's unnecessary, and _very_ unsafe, since casts can hide all classes of bugs by silencing most compiler warnings. The changes from nxt_memchr() to memchr(3) were scripted: $ find src/ -type f \ | grep '\.[ch]$' \ | xargs sed -i 's/nxt_memchr/memchr/' Reviewed-by: Andrew Clayton Signed-off-by: Alejandro Colomar --- src/nxt_conf_validation.c | 12 ++++++------ src/nxt_controller.c | 2 +- src/nxt_h1proto.c | 2 +- src/nxt_http_route.c | 4 ++-- src/nxt_http_route_addr.c | 12 ++++++------ src/nxt_sockaddr.c | 8 ++++---- src/nxt_string.h | 4 ---- src/nxt_var.c | 2 +- src/nxt_var.h | 2 +- src/perl/nxt_perl_psgi.c | 2 +- 10 files changed, 23 insertions(+), 27 deletions(-) diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index f8242f6e..7fbe7e29 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -2710,12 +2710,12 @@ nxt_conf_vldt_environment(nxt_conf_validation_t *vldt, nxt_str_t *name, "The environment name must not be empty."); } - if (nxt_memchr(name->start, '\0', name->length) != NULL) { + if (memchr(name->start, '\0', name->length) != NULL) { return nxt_conf_vldt_error(vldt, "The environment name must not " "contain null character."); } - if (nxt_memchr(name->start, '=', name->length) != NULL) { + if (memchr(name->start, '=', name->length) != NULL) { return nxt_conf_vldt_error(vldt, "The environment name must not " "contain '=' character."); } @@ -2727,7 +2727,7 @@ nxt_conf_vldt_environment(nxt_conf_validation_t *vldt, nxt_str_t *name, nxt_conf_get_string(value, &str); - if (nxt_memchr(str.start, '\0', str.length) != NULL) { + if (memchr(str.start, '\0', str.length) != NULL) { return nxt_conf_vldt_error(vldt, "The \"%V\" environment value must " "not contain null character.", name); } @@ -2926,7 +2926,7 @@ nxt_conf_vldt_argument(nxt_conf_validation_t *vldt, nxt_conf_value_t *value) nxt_conf_get_string(value, &str); - if (nxt_memchr(str.start, '\0', str.length) != NULL) { + if (memchr(str.start, '\0', str.length) != NULL) { return nxt_conf_vldt_error(vldt, "The \"arguments\" array must not " "contain strings with null character."); } @@ -2985,7 +2985,7 @@ nxt_conf_vldt_java_classpath(nxt_conf_validation_t *vldt, nxt_conf_get_string(value, &str); - if (nxt_memchr(str.start, '\0', str.length) != NULL) { + if (memchr(str.start, '\0', str.length) != NULL) { return nxt_conf_vldt_error(vldt, "The \"classpath\" array must not " "contain strings with null character."); } @@ -3006,7 +3006,7 @@ nxt_conf_vldt_java_option(nxt_conf_validation_t *vldt, nxt_conf_value_t *value) nxt_conf_get_string(value, &str); - if (nxt_memchr(str.start, '\0', str.length) != NULL) { + if (memchr(str.start, '\0', str.length) != NULL) { return nxt_conf_vldt_error(vldt, "The \"options\" array must not " "contain strings with null character."); } diff --git a/src/nxt_controller.c b/src/nxt_controller.c index 5f3e55ef..b5e0d831 100644 --- a/src/nxt_controller.c +++ b/src/nxt_controller.c @@ -1637,7 +1637,7 @@ nxt_controller_process_cert(nxt_task_t *task, name.length = path->length - 1; name.start = path->start + 1; - p = nxt_memchr(name.start, '/', name.length); + p = memchr(name.start, '/', name.length); if (p != NULL) { name.length = p - name.start; diff --git a/src/nxt_h1proto.c b/src/nxt_h1proto.c index 3acba088..1e37273f 100644 --- a/src/nxt_h1proto.c +++ b/src/nxt_h1proto.c @@ -2609,7 +2609,7 @@ nxt_h1p_peer_header_parse(nxt_http_peer_t *peer, nxt_buf_mem_t *bm) p += 12; length -= 12; - p = nxt_memchr(p, '\n', length); + p = memchr(p, '\n', length); if (nxt_slow_path(p == NULL)) { return NXT_AGAIN; diff --git a/src/nxt_http_route.c b/src/nxt_http_route.c index 9aa23899..1f2fe883 100644 --- a/src/nxt_http_route.c +++ b/src/nxt_http_route.c @@ -1016,7 +1016,7 @@ nxt_http_route_pattern_create(nxt_task_t *task, nxt_mp_t *mp, if (type == NXT_HTTP_ROUTE_PATTERN_EXACT) { tmp.start = test.start; - p = nxt_memchr(test.start, '*', test.length); + p = memchr(test.start, '*', test.length); if (p == NULL) { /* No '*' found - EXACT pattern. */ @@ -1414,7 +1414,7 @@ nxt_http_pass_segments(nxt_mp_t *mp, nxt_str_t *pass, nxt_str_t *segments, nxt_memzero(segments, n * sizeof(nxt_str_t)); do { - p = nxt_memchr(rest.start, '/', rest.length); + p = memchr(rest.start, '/', rest.length); if (p != NULL) { n--; diff --git a/src/nxt_http_route_addr.c b/src/nxt_http_route_addr.c index 06b30d29..5a0d7679 100644 --- a/src/nxt_http_route_addr.c +++ b/src/nxt_http_route_addr.c @@ -93,7 +93,7 @@ nxt_http_route_addr_pattern_parse(nxt_mp_t *mp, inet6 = &pattern->addr.v6; - delim = nxt_memchr(addr.start, '-', addr.length); + delim = memchr(addr.start, '-', addr.length); if (delim != NULL) { len = delim - addr.start; if (nxt_slow_path(!nxt_valid_ipv6_blocks(addr.start, len))) { @@ -126,7 +126,7 @@ nxt_http_route_addr_pattern_parse(nxt_mp_t *mp, goto parse_port; } - delim = nxt_memchr(addr.start, '/', addr.length); + delim = memchr(addr.start, '/', addr.length); if (delim != NULL) { cidr_prefix = nxt_int_parse(delim + 1, addr.start + addr.length - (delim + 1)); @@ -201,7 +201,7 @@ nxt_http_route_addr_pattern_parse(nxt_mp_t *mp, base->addr_family = AF_INET; - delim = nxt_memchr(addr.start, ':', addr.length); + delim = memchr(addr.start, ':', addr.length); if (delim != NULL) { port.start = delim + 1; port.length = addr.start + addr.length - port.start; @@ -210,7 +210,7 @@ nxt_http_route_addr_pattern_parse(nxt_mp_t *mp, inet = &pattern->addr.v4; - delim = nxt_memchr(addr.start, '-', addr.length); + delim = memchr(addr.start, '-', addr.length); if (delim != NULL) { inet->start = nxt_inet_addr(addr.start, delim - addr.start); if (nxt_slow_path(inet->start == INADDR_NONE)) { @@ -234,7 +234,7 @@ nxt_http_route_addr_pattern_parse(nxt_mp_t *mp, goto parse_port; } - delim = nxt_memchr(addr.start, '/', addr.length); + delim = memchr(addr.start, '/', addr.length); if (delim != NULL) { cidr_prefix = nxt_int_parse(delim + 1, addr.start + addr.length - (delim + 1)); @@ -283,7 +283,7 @@ parse_port: return NXT_OK; } - delim = nxt_memchr(port.start, '-', port.length - 1); + delim = memchr(port.start, '-', port.length - 1); if (delim != NULL) { ret = nxt_int_parse(port.start, delim - port.start); if (nxt_slow_path(ret < 0 || ret > 65535)) { diff --git a/src/nxt_sockaddr.c b/src/nxt_sockaddr.c index a076f286..32941893 100644 --- a/src/nxt_sockaddr.c +++ b/src/nxt_sockaddr.c @@ -653,7 +653,7 @@ nxt_sockaddr_inet6_parse(nxt_mp_t *mp, nxt_str_t *addr) length = addr->length - 1; start = addr->start + 1; - end = nxt_memchr(start, ']', length); + end = memchr(start, ']', length); if (nxt_slow_path(end == NULL)) { return NULL; } @@ -723,7 +723,7 @@ nxt_sockaddr_inet_parse(nxt_mp_t *mp, nxt_str_t *addr) in_addr_t inaddr; nxt_sockaddr_t *sa; - p = nxt_memchr(addr->start, ':', addr->length); + p = memchr(addr->start, ':', addr->length); if (p == NULL) { length = addr->length; @@ -964,11 +964,11 @@ nxt_inet6_probe(nxt_str_t *str) { u_char *colon, *end; - colon = nxt_memchr(str->start, ':', str->length); + colon = memchr(str->start, ':', str->length); if (colon != NULL) { end = str->start + str->length; - colon = nxt_memchr(colon + 1, ':', end - (colon + 1)); + colon = memchr(colon + 1, ':', end - (colon + 1)); } return (colon != NULL); diff --git a/src/nxt_string.h b/src/nxt_string.h index d021df69..18ea5490 100644 --- a/src/nxt_string.h +++ b/src/nxt_string.h @@ -66,10 +66,6 @@ nxt_cpymem(void *dst, const void *src, size_t length) (void) memmove(dst, src, length) -#define nxt_memchr(s, c, length) \ - memchr((char *) s, c, length) - - #define nxt_strcmp(s1, s2) \ strcmp((char *) s1, (char *) s2) diff --git a/src/nxt_var.c b/src/nxt_var.c index f55a2d30..15759eb4 100644 --- a/src/nxt_var.c +++ b/src/nxt_var.c @@ -456,7 +456,7 @@ nxt_var_next_part(u_char *start, size_t length, nxt_str_t *part, end = start + length; - p = nxt_memchr(start, '$', length); + p = memchr(start, '$', length); if (p == start) { *is_var = 1; diff --git a/src/nxt_var.h b/src/nxt_var.h index cc7ff502..34c8857a 100644 --- a/src/nxt_var.h +++ b/src/nxt_var.h @@ -41,7 +41,7 @@ typedef enum { nxt_inline nxt_bool_t nxt_is_var(nxt_str_t *str) { - return (nxt_memchr(str->start, '$', str->length) != NULL); + return (memchr(str->start, '$', str->length) != NULL); } diff --git a/src/perl/nxt_perl_psgi.c b/src/perl/nxt_perl_psgi.c index 0c1c1222..5e8d1aee 100644 --- a/src/perl/nxt_perl_psgi.c +++ b/src/perl/nxt_perl_psgi.c @@ -765,7 +765,7 @@ nxt_perl_psgi_result_status(PerlInterpreter *my_perl, SV *result) status.start = (u_char *) SvPV(*sv_status, status.length); - space = nxt_memchr(status.start, ' ', status.length); + space = memchr(status.start, ' ', status.length); if (space != NULL) { status.length = space - status.start; } -- cgit From 11f416878a9f5f7ec13d548b85982c5de5b55a11 Mon Sep 17 00:00:00 2001 From: Konstantin Pavlov Date: Fri, 4 Nov 2022 13:55:16 +0400 Subject: Packages: added Python 3.8 and Python 3.9 modules on RHEL 8 clones. Refs: https://github.com/nginx/unit/issues/778 --- pkg/rpm/Makefile | 2 + pkg/rpm/Makefile.python38 | 53 ++++++++++++++++++++++ .../rpmbuild/SOURCES/unit.example-python38-config | 16 +++++++ 3 files changed, 71 insertions(+) create mode 100644 pkg/rpm/Makefile.python38 create mode 100644 pkg/rpm/rpmbuild/SOURCES/unit.example-python38-config diff --git a/pkg/rpm/Makefile b/pkg/rpm/Makefile index bbe44fe5..ef824c65 100644 --- a/pkg/rpm/Makefile +++ b/pkg/rpm/Makefile @@ -53,6 +53,8 @@ ifeq ($(OSVER), centos8) include Makefile.php include Makefile.python27 include Makefile.python36 +include Makefile.python38 +include Makefile.python39 include Makefile.go include Makefile.perl include Makefile.jsc-common diff --git a/pkg/rpm/Makefile.python38 b/pkg/rpm/Makefile.python38 new file mode 100644 index 00000000..3f3657e2 --- /dev/null +++ b/pkg/rpm/Makefile.python38 @@ -0,0 +1,53 @@ +MODULES+= python38 +MODULE_SUFFIX_python38= python3.8 + +MODULE_SUMMARY_python38= Python 3.8 module for NGINX Unit + +MODULE_VERSION_python38= $(VERSION) +MODULE_RELEASE_python38= 1 + +MODULE_CONFARGS_python38= python --config=python3.8-config +MODULE_MAKEARGS_python38= python3.8 +MODULE_INSTARGS_python38= python3.8-install + +MODULE_SOURCES_python38= unit.example-python-app \ + unit.example-python38-config + +BUILD_DEPENDS_python38= python38-devel + +BUILD_DEPENDS+= $(BUILD_DEPENDS_python38) + +define MODULE_PREINSTALL_python38 +%{__mkdir} -p %{buildroot}%{_datadir}/doc/unit-python38/examples/python-app +%{__install} -m 644 -p %{SOURCE100} \ + %{buildroot}%{_datadir}/doc/unit-python38/examples/python-app/wsgi.py +%{__install} -m 644 -p %{SOURCE101} \ + %{buildroot}%{_datadir}/doc/unit-python38/examples/unit.config +endef +export MODULE_PREINSTALL_python38 + +define MODULE_FILES_python38 +%{_libdir}/unit/modules/* +%{_libdir}/unit/debug-modules/* +endef +export MODULE_FILES_python38 + +define MODULE_POST_python38 +cat < Date: Tue, 15 Nov 2022 00:17:32 +0000 Subject: Optimization for the "--no-unix-sockets" case. --- src/python/nxt_python_asgi.c | 40 +++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/src/python/nxt_python_asgi.c b/src/python/nxt_python_asgi.c index 40ea5e24..256bcdcc 100644 --- a/src/python/nxt_python_asgi.c +++ b/src/python/nxt_python_asgi.c @@ -738,42 +738,40 @@ fail: static PyObject * nxt_py_asgi_create_address(nxt_unit_sptr_t *sptr, uint8_t len, uint16_t port) { +#if (NXT_HAVE_UNIX_DOMAIN) size_t prefix_len; - nxt_str_t addr; PyObject *pair, *v; + nxt_str_t addr; addr.length = len; addr.start = nxt_unit_sptr_get(sptr); prefix_len = nxt_length("unix:"); - if (!nxt_str_start(&addr, "unix:", prefix_len)) { - return nxt_py_asgi_create_ip_address(sptr, len, port); - } + if (nxt_str_start(&addr, "unix:", prefix_len)) { -#if NXT_HAVE_UNIX_DOMAIN - pair = PyTuple_New(2); - if (nxt_slow_path(pair == NULL)) { - return NULL; - } + pair = PyTuple_New(2); + if (nxt_slow_path(pair == NULL)) { + return NULL; + } - addr.start += prefix_len; - addr.length -= prefix_len; + addr.start += prefix_len; + addr.length -= prefix_len; - v = PyString_FromStringAndSize((const char *) addr.start, addr.length); - if (nxt_slow_path(v == NULL)) { - Py_DECREF(pair); + v = PyString_FromStringAndSize((const char *) addr.start, addr.length); + if (nxt_slow_path(v == NULL)) { + Py_DECREF(pair); - return NULL; - } + return NULL; + } - PyTuple_SET_ITEM(pair, 0, v); - PyTuple_SET_ITEM(pair, 1, Py_None); + PyTuple_SET_ITEM(pair, 0, v); + PyTuple_SET_ITEM(pair, 1, Py_None); - return pair; + return pair; + } -#else - return NULL; #endif + return nxt_py_asgi_create_ip_address(sptr, len, port); } -- cgit From 0d3b31e6710afe4348eb25f1602f5271c92b9a77 Mon Sep 17 00:00:00 2001 From: Andrei Zeliankou Date: Tue, 15 Nov 2022 00:39:21 +0000 Subject: Tests: features and options checks improved. Now version output evaluates only once. OpenSSL checks more carefully. --- test/conftest.py | 7 +++++-- test/unit/check/regex.py | 9 ++------- test/unit/check/tls.py | 13 ++++++------- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/test/conftest.py b/test/conftest.py index 18851baa..ac88675a 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -176,6 +176,9 @@ def pytest_sessionstart(session): option.available = {'modules': {}, 'features': {}} unit = unit_run() + output_version = subprocess.check_output( + [unit['unitd'], '--version'], stderr=subprocess.STDOUT + ).decode() # read unit.log @@ -202,10 +205,10 @@ def pytest_sessionstart(session): # discover modules from check - option.available['modules']['openssl'] = check_openssl(unit['unitd']) option.available['modules']['go'] = check_go() option.available['modules']['node'] = check_node(option.current_dir) - option.available['modules']['regex'] = check_regex(unit['unitd']) + option.available['modules']['openssl'] = check_openssl(output_version) + option.available['modules']['regex'] = check_regex(output_version) # remove None values diff --git a/test/unit/check/regex.py b/test/unit/check/regex.py index 734c0150..51cf966b 100644 --- a/test/unit/check/regex.py +++ b/test/unit/check/regex.py @@ -1,13 +1,8 @@ import re -import subprocess -def check_regex(unitd): - output = subprocess.check_output( - [unitd, '--version'], stderr=subprocess.STDOUT - ) - - if re.search('--no-regex', output.decode()): +def check_regex(output_version): + if re.search('--no-regex', output_version): return False return True diff --git a/test/unit/check/tls.py b/test/unit/check/tls.py index b878ff7d..53ce5ffc 100644 --- a/test/unit/check/tls.py +++ b/test/unit/check/tls.py @@ -2,12 +2,11 @@ import re import subprocess -def check_openssl(unitd): - subprocess.check_output(['which', 'openssl']) +def check_openssl(output_version): + try: + subprocess.check_output(['which', 'openssl']) + except subprocess.CalledProcessError: + return None - output = subprocess.check_output( - [unitd, '--version'], stderr=subprocess.STDOUT - ) - - if re.search('--openssl', output.decode()): + if re.search('--openssl', output_version): return True -- cgit From 2c2156e236c18a67922617a4e43fb9eb1da0ab9e Mon Sep 17 00:00:00 2001 From: Andrei Zeliankou Date: Tue, 15 Nov 2022 00:42:12 +0000 Subject: Tests: fixed assertion in test_variables_dynamic. --- test/test_variables.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/test/test_variables.py b/test/test_variables.py index ebbc5aa2..80236f65 100644 --- a/test/test_variables.py +++ b/test/test_variables.py @@ -261,10 +261,13 @@ Connection: close def test_variables_dynamic(self): self.set_format('$header_foo$cookie_foo$arg_foo') - self.get( - url='/?foo=h', - headers={'Foo': 'b', 'Cookie': 'foo=la', 'Connection': 'close'}, - )['status'] = 200 + assert ( + self.get( + url='/?foo=h', + headers={'Foo': 'b', 'Cookie': 'foo=la', 'Connection': 'close'}, + )['status'] + == 200 + ) assert self.wait_for_record(r'^blah$') is not None def test_variables_dynamic_arguments(self): -- cgit From bb11ef694ce49343a11bafee836addb6dd125848 Mon Sep 17 00:00:00 2001 From: Andrei Zeliankou Date: Tue, 15 Nov 2022 00:56:49 +0000 Subject: Tests: removed migration test. Migration of "share" behaviour was dropped after b57b4749b993. --- test/test_static.py | 46 ---------------------------------------------- 1 file changed, 46 deletions(-) diff --git a/test/test_static.py b/test/test_static.py index 43d8b7cc..9013b5c0 100644 --- a/test/test_static.py +++ b/test/test_static.py @@ -1,10 +1,7 @@ import os -import shutil import socket import pytest -from conftest import unit_run -from conftest import unit_stop from unit.applications.proto import TestApplicationProto from unit.option import option from unit.utils import waitforfiles @@ -43,49 +40,6 @@ class TestStatic(TestApplicationProto): } ) - def test_static_migration(self, skip_fds_check, temp_dir): - skip_fds_check(True, True, True) - - def set_conf_version(path, version): - with open(path, 'w+') as f: - f.write(str(version)) - - with open(temp_dir + '/state/version', 'r') as f: - assert int(f.read().rstrip()) > 12500, 'current version' - - assert 'success' in self.conf( - {"share": temp_dir + "/assets"}, 'routes/0/action' - ), 'configure migration 12500' - - shutil.copytree(temp_dir + '/state', temp_dir + '/state_copy_12500') - set_conf_version(temp_dir + '/state_copy_12500/version', 12500) - - assert 'success' in self.conf( - {"share": temp_dir + "/assets$uri"}, 'routes/0/action' - ), 'configure migration 12600' - shutil.copytree(temp_dir + '/state', temp_dir + '/state_copy_12600') - set_conf_version(temp_dir + '/state_copy_12600/version', 12600) - - assert 'success' in self.conf( - {"share": temp_dir + "/assets"}, 'routes/0/action' - ), 'configure migration no version' - shutil.copytree( - temp_dir + '/state', temp_dir + '/state_copy_no_version' - ) - os.remove(temp_dir + '/state_copy_no_version/version') - - unit_stop() - unit_run(temp_dir + '/state_copy_12500') - assert self.get(url='/')['body'] == '0123456789', 'before 1.26.0' - - unit_stop() - unit_run(temp_dir + '/state_copy_12600') - assert self.get(url='/')['body'] == '0123456789', 'after 1.26.0' - - unit_stop() - unit_run(temp_dir + '/state_copy_no_version') - assert self.get(url='/')['body'] == '0123456789', 'before 1.26.0 2' - def test_static_index(self): def set_index(index): assert 'success' in self.conf( -- cgit From 9ea5ed2813c7dc57c8997ef21d779baae19d784c Mon Sep 17 00:00:00 2001 From: Andrei Zeliankou Date: Tue, 15 Nov 2022 01:07:41 +0000 Subject: Tests: fixed _check_processes() checks in "--restart" mode. --- test/conftest.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/conftest.py b/test/conftest.py index ac88675a..bf2951db 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -574,6 +574,10 @@ def _check_processes(): time.sleep(0.1) + if option.restart: + assert len(out) == 0, 'all termimated' + return + assert len(out) == 3, 'main, router, and controller expected' out = [l for l in out if 'unit: main' not in l] -- cgit From 2ac4a7527de8fb7ce8cfac05563a542078d65f4b Mon Sep 17 00:00:00 2001 From: Konstantin Pavlov Date: Fri, 4 Nov 2022 16:52:19 +0400 Subject: Packages: relaxed dependencies between modules and base package. This allows us to update base or single modules packages without updating the whole set. --- pkg/deb/Makefile | 1 + pkg/deb/debian.module/control.in | 2 +- pkg/deb/debian/control.in | 1 + pkg/rpm/unit.module.spec.in | 2 +- pkg/rpm/unit.spec.in | 2 ++ 5 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pkg/deb/Makefile b/pkg/deb/Makefile index c4f085f8..6595cb45 100644 --- a/pkg/deb/Makefile +++ b/pkg/deb/Makefile @@ -156,6 +156,7 @@ debuild/$(SRCDIR)/debian: echo '3.0 (quilt)' > debuild/$(SRCDIR)/debian/source/format ; \ cat debian/control.in | sed \ -e "s#%%PACKAGE_VENDOR%%#$(PACKAGE_VENDOR)#g" \ + -e "s#%%UNIT_VERSION%%#$(VERSION)#g" \ > debuild/$(SRCDIR)/debian/control ; \ cat debian/rules.in | sed \ -e "s#%%CONFIGURE_ARGS%%#$(CONFIGURE_ARGS)#g" \ diff --git a/pkg/deb/debian.module/control.in b/pkg/deb/debian.module/control.in index f5ce8ae4..f82362d1 100644 --- a/pkg/deb/debian.module/control.in +++ b/pkg/deb/debian.module/control.in @@ -14,7 +14,7 @@ Section: admin Architecture: any Depends: lsb-base, ${misc:Depends}, ${shlibs:Depends}, - unit (= %%UNIT_VERSION%%-%%UNIT_RELEASE%%~%%CODENAME%%)%%MODULE_DEPENDS%% + unit-r%%UNIT_VERSION%%%%MODULE_DEPENDS%% Description: %%SUMMARY%% NGINX Unit is a runtime and delivery environment for modern distributed applications. It runs the application code in multiple languages diff --git a/pkg/deb/debian/control.in b/pkg/deb/debian/control.in index 691bafed..acf834bc 100644 --- a/pkg/deb/debian/control.in +++ b/pkg/deb/debian/control.in @@ -14,6 +14,7 @@ Section: admin Architecture: any Depends: lsb-base, ${misc:Depends}, ${shlibs:Depends} +Provides: unit-r%%UNIT_VERSION%% Description: NGINX Unit NGINX Unit is a runtime and delivery environment for modern distributed applications. It runs the application code in multiple languages diff --git a/pkg/rpm/unit.module.spec.in b/pkg/rpm/unit.module.spec.in index 88a1c33e..bc68a254 100644 --- a/pkg/rpm/unit.module.spec.in +++ b/pkg/rpm/unit.module.spec.in @@ -39,7 +39,7 @@ BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) BuildRequires: pcre2-devel -Requires: unit == %%UNIT_VERSION%%-%%UNIT_RELEASE%%%{?dist}.ngx +Requires: unit-r%%UNIT_VERSION%% %description NGINX Unit is a runtime and delivery environment for modern distributed diff --git a/pkg/rpm/unit.spec.in b/pkg/rpm/unit.spec.in index 01c08bb8..50eee876 100644 --- a/pkg/rpm/unit.spec.in +++ b/pkg/rpm/unit.spec.in @@ -47,6 +47,8 @@ Requires(postun): systemd BuildRequires: pcre2-devel +Provides: unit-r%{version} + %description NGINX Unit is a runtime and delivery environment for modern distributed applications. It runs the application code in multiple languages -- cgit From 3b970ed9345ee2fe27f15b43ff827630d38cc124 Mon Sep 17 00:00:00 2001 From: OutOfFocus4 Date: Tue, 13 Sep 2022 10:36:46 -0400 Subject: Removed dead code. Signed-off-by: Alejandro Colomar --- src/nxt_application.h | 1 - src/nxt_main_process.c | 12 ------------ src/python/nxt_python.c | 4 +--- 3 files changed, 1 insertion(+), 16 deletions(-) diff --git a/src/nxt_application.h b/src/nxt_application.h index 30a1a12f..239f357b 100644 --- a/src/nxt_application.h +++ b/src/nxt_application.h @@ -49,7 +49,6 @@ typedef struct { typedef struct { char *home; nxt_conf_value_t *path; - nxt_str_t module; char *callable; nxt_str_t protocol; uint32_t threads; diff --git a/src/nxt_main_process.c b/src/nxt_main_process.c index 39a8e112..f21482d5 100644 --- a/src/nxt_main_process.c +++ b/src/nxt_main_process.c @@ -190,18 +190,6 @@ static nxt_conf_map_t nxt_python_app_conf[] = { offsetof(nxt_common_app_conf_t, u.python.path), }, - { - nxt_string("module"), - NXT_CONF_MAP_STR, - offsetof(nxt_common_app_conf_t, u.python.module), - }, - - { - nxt_string("callable"), - NXT_CONF_MAP_CSTRZ, - offsetof(nxt_common_app_conf_t, u.python.callable), - }, - { nxt_string("protocol"), NXT_CONF_MAP_STR, diff --git a/src/python/nxt_python.c b/src/python/nxt_python.c index 188c4920..37204051 100644 --- a/src/python/nxt_python.c +++ b/src/python/nxt_python.c @@ -70,7 +70,7 @@ nxt_python_start(nxt_task_t *task, nxt_process_data_t *data) int rc; size_t len, size; uint32_t next; - PyObject *obj, *module; + PyObject *obj; nxt_str_t proto, probe_proto, name; nxt_int_t ret, n, i; nxt_unit_ctx_t *unit_ctx; @@ -154,7 +154,6 @@ nxt_python_start(nxt_task_t *task, nxt_process_data_t *data) } #endif - module = NULL; obj = NULL; python_init.ctx_data = NULL; @@ -307,7 +306,6 @@ fail: } Py_XDECREF(obj); - Py_XDECREF(module); nxt_python_atexit(); -- cgit From 6902cd14ad4e322be1c29564bc2e238d0a225e92 Mon Sep 17 00:00:00 2001 From: OutOfFocus4 Date: Sun, 6 Nov 2022 09:08:55 -0500 Subject: Refactored functions that set WSGI variables. Splitting `nxt_python_add_sptr` into several functions will make future additions easier. Signed-off-by: Alejandro Colomar --- src/python/nxt_python_wsgi.c | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/src/python/nxt_python_wsgi.c b/src/python/nxt_python_wsgi.c index 3fb6ac3b..34afd9a9 100644 --- a/src/python/nxt_python_wsgi.c +++ b/src/python/nxt_python_wsgi.c @@ -63,6 +63,10 @@ static PyObject *nxt_python_copy_environ(nxt_unit_request_info_t *req); static PyObject *nxt_python_get_environ(nxt_python_ctx_t *pctx); static int nxt_python_add_sptr(nxt_python_ctx_t *pctx, PyObject *name, nxt_unit_sptr_t *sptr, uint32_t size); +static int nxt_python_add_char(nxt_python_ctx_t *pctx, PyObject *name, + char *src, uint32_t size); +static int nxt_python_add_py_string(nxt_python_ctx_t *pctx, PyObject *name, + PyObject *value); static int nxt_python_add_field(nxt_python_ctx_t *pctx, nxt_unit_field_t *field, int n, uint32_t vl); static PyObject *nxt_python_field_name(const char *name, uint8_t len); @@ -692,10 +696,16 @@ static int nxt_python_add_sptr(nxt_python_ctx_t *pctx, PyObject *name, nxt_unit_sptr_t *sptr, uint32_t size) { - char *src; - PyObject *value; + return nxt_python_add_char(pctx, name, nxt_unit_sptr_get(sptr), size); +} - src = nxt_unit_sptr_get(sptr); + +static int +nxt_python_add_char(nxt_python_ctx_t *pctx, PyObject *name, + char *src, uint32_t size) +{ + int res; + PyObject *value; value = PyString_FromStringAndSize(src, size); if (nxt_slow_path(value == NULL)) { @@ -707,17 +717,25 @@ nxt_python_add_sptr(nxt_python_ctx_t *pctx, PyObject *name, return NXT_UNIT_ERROR; } + res = nxt_python_add_py_string(pctx, name, value); + + Py_DECREF(value); + + return res; +} + + +static int nxt_python_add_py_string(nxt_python_ctx_t *pctx, PyObject *name, + PyObject *value) +{ if (nxt_slow_path(PyDict_SetItem(pctx->environ, name, value) != 0)) { nxt_unit_req_error(pctx->req, "Python failed to set the \"%s\" environ value", PyUnicode_AsUTF8(name)); - Py_DECREF(value); return NXT_UNIT_ERROR; } - Py_DECREF(value); - return NXT_UNIT_OK; } -- cgit From 894a2620a7c9992ac009e3e35b0a95124b89f3cb Mon Sep 17 00:00:00 2001 From: Konstantin Pavlov Date: Wed, 16 Nov 2022 18:37:35 +0400 Subject: Propagated NXT_RUBY_CFLAGS to Ruby checks. This fixes an issue addressed in 651f5a37f5b8 on FreeBSD 12. The problem manifested itself as: configuring Ruby module checking for -fdeclspec ... found checking for Ruby library ... not found checking for Ruby library in /usr/local/lib ... not found ./configure: error: no Ruby found. --- auto/modules/ruby | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/auto/modules/ruby b/auto/modules/ruby index 7c2f0102..608193a6 100644 --- a/auto/modules/ruby +++ b/auto/modules/ruby @@ -101,7 +101,7 @@ if /bin/sh -c "$NXT_RUBY -v" >> $NXT_AUTOCONF_ERR 2>&1; then nxt_feature="Ruby library" nxt_feature_name="" nxt_feature_run=value - nxt_feature_incs="${NXT_RUBY_INCPATH}" + nxt_feature_incs="${NXT_RUBY_INCPATH} ${NXT_RUBY_CFLAGS}" nxt_feature_libs="${NXT_RUBY_LIBS}" nxt_feature_test=" #include @@ -125,7 +125,7 @@ if /bin/sh -c "$NXT_RUBY -v" >> $NXT_AUTOCONF_ERR 2>&1; then nxt_feature="Ruby library in $NXT_RUBY_LIBPATH" nxt_feature_name="" nxt_feature_run=no - nxt_feature_incs="${NXT_RUBY_INCPATH}" + nxt_feature_incs="${NXT_RUBY_INCPATH} ${NXT_RUBY_CFLAGS}" nxt_feature_libs="${NXT_RUBY_LIBS}" nxt_feature_test=" #include @@ -153,7 +153,7 @@ fi nxt_feature="Ruby version" nxt_feature_name="" nxt_feature_run=value -nxt_feature_incs="${NXT_RUBY_INCPATH}" +nxt_feature_incs="${NXT_RUBY_INCPATH} ${NXT_RUBY_CFLAGS}" nxt_feature_libs="${NXT_RUBY_LIBS}" nxt_feature_test=" #include -- cgit From 3711632c00f8538fd87ff1a14a028756f57239a2 Mon Sep 17 00:00:00 2001 From: Zhidao HONG Date: Sun, 20 Nov 2022 23:11:41 +0800 Subject: Var: improved variable parsing with empty names. Unit parsed the case of "$uri$$host" into unknown variables. This commit makes it invalid variable instead. --- src/nxt_var.c | 75 +++++++++++++++++++++----------------------------- test/test_variables.py | 1 + 2 files changed, 33 insertions(+), 43 deletions(-) diff --git a/src/nxt_var.c b/src/nxt_var.c index 15759eb4..c4133544 100644 --- a/src/nxt_var.c +++ b/src/nxt_var.c @@ -61,8 +61,7 @@ static nxt_int_t nxt_var_cache_test(nxt_lvlhsh_query_t *lhq, void *data); static nxt_str_t *nxt_var_cache_value(nxt_task_t *task, nxt_var_query_t *query, uint32_t index); -static u_char *nxt_var_next_part(u_char *start, size_t length, nxt_str_t *part, - nxt_bool_t *is_var); +static u_char *nxt_var_next_part(u_char *start, u_char *end, nxt_str_t *part); static const nxt_lvlhsh_proto_t nxt_var_hash_proto nxt_aligned(64) = { @@ -340,7 +339,6 @@ nxt_var_compile(nxt_str_t *str, nxt_mp_t *mp, nxt_array_t *fields, nxt_var_t *var; nxt_str_t part; nxt_uint_t n; - nxt_bool_t is_var; nxt_var_sub_t *subs; nxt_var_decl_t *decl; @@ -352,12 +350,12 @@ nxt_var_compile(nxt_str_t *str, nxt_mp_t *mp, nxt_array_t *fields, end = p + str->length; while (p < end) { - p = nxt_var_next_part(p, end - p, &part, &is_var); + p = nxt_var_next_part(p, end, &part); if (nxt_slow_path(p == NULL)) { return NULL; } - if (is_var) { + if (part.start != NULL) { n++; } } @@ -386,9 +384,9 @@ nxt_var_compile(nxt_str_t *str, nxt_mp_t *mp, nxt_array_t *fields, p = str->start; while (p < end) { - next = nxt_var_next_part(p, end - p, &part, &is_var); + next = nxt_var_next_part(p, end, &part); - if (is_var) { + if (part.start != NULL) { decl = nxt_var_decl_get(&part, fields, &index); if (nxt_slow_path(decl == NULL)) { return NULL; @@ -413,14 +411,13 @@ nxt_var_test(nxt_str_t *str, nxt_array_t *fields, u_char *error) { u_char *p, *end, *next; nxt_str_t part; - nxt_bool_t is_var; nxt_var_decl_t *decl; p = str->start; end = p + str->length; while (p < end) { - next = nxt_var_next_part(p, end - p, &part, &is_var); + next = nxt_var_next_part(p, end, &part); if (next == NULL) { nxt_sprintf(error, error + NXT_MAX_ERROR_STR, @@ -429,7 +426,7 @@ nxt_var_test(nxt_str_t *str, nxt_array_t *fields, u_char *error) return NXT_ERROR; } - if (is_var) { + if (part.start != NULL) { decl = nxt_var_decl_get(&part, fields, NULL); if (decl == NULL) { @@ -448,19 +445,15 @@ nxt_var_test(nxt_str_t *str, nxt_array_t *fields, u_char *error) static u_char * -nxt_var_next_part(u_char *start, size_t length, nxt_str_t *part, - nxt_bool_t *is_var) +nxt_var_next_part(u_char *start, u_char *end, nxt_str_t *part) { - u_char *p, *end, ch, c; + size_t length; + u_char *p, ch, c; nxt_bool_t bracket; - end = start + length; - - p = memchr(start, '$', length); + p = memchr(start, '$', end - start); if (p == start) { - *is_var = 1; - p++; if (p == end) { @@ -480,48 +473,44 @@ nxt_var_next_part(u_char *start, size_t length, nxt_str_t *part, bracket = 0; } + length = 0; start = p; - for ( ;; ) { + while (p < end) { ch = *p; c = (u_char) (ch | 0x20); - if ((c < 'a' || c > 'z') && ch != '_') { - - if (bracket && ch != '}') { - return NULL; - } - break; + if ((c >= 'a' && c <= 'z') || ch == '_') { + p++; + length++; + continue; } - p++; + if (bracket && ch == '}') { + p++; + bracket = 0; + } - if (p == end) { - if (bracket) { - return NULL; - } + break; + } - break; - } + if (bracket || length == 0) { + return NULL; } - length = p - start; - end = p + bracket; + part->length = length; + part->start = start; } else { - *is_var = 0; - - if (p != NULL) { - length = p - start; - end = p; + if (p == NULL) { + p = end; } - } - part->length = length; - part->start = start; + nxt_str_null(part); + } - return end; + return p; } diff --git a/test/test_variables.py b/test/test_variables.py index 80236f65..ecce5e6d 100644 --- a/test/test_variables.py +++ b/test/test_variables.py @@ -378,6 +378,7 @@ Connection: close check_variables("${") check_variables("${}") check_variables("$ur") + check_variables("$uri$$host") check_variables("$uriblah") check_variables("${uri") check_variables("${{uri}") -- cgit From e9c1954dc2a677fe36615b5ace5d638bd0bfd133 Mon Sep 17 00:00:00 2001 From: Konstantin Pavlov Date: Tue, 22 Nov 2022 12:39:20 +0100 Subject: Tools: Added setup-unit. Downloaded from . Acked-by: Artem Konev Acked-by: Konstantin Pavlov Signed-off-by: Alejandro Colomar --- tools/setup-unit | 311 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 311 insertions(+) create mode 100755 tools/setup-unit diff --git a/tools/setup-unit b/tools/setup-unit new file mode 100755 index 00000000..2e10e54d --- /dev/null +++ b/tools/setup-unit @@ -0,0 +1,311 @@ +#!/bin/sh + +##################################################################### +# +# Copyright (C) NGINX, Inc. +# +# Author: NGINX Unit Team, F5 Inc. +# Version: 0.0.1 +# Date: 2022-05-05 +# +# This script will configure the repositories for NGINX Unit on Ubuntu, +# Debian, RedHat, CentOS, Oracle Linux, Amazon Linux, Fedora. +# It must be run as root. +# +# Note: curl and awk are required by this script, so the script checks to make +# sure they are installed. +# +##################################################################### + +export LC_ALL=C + +checkOSPrereqs () { + + if ! command -v curl > /dev/null 2>&1 + then + echo "Error: curl not found in PATH. It must be installed to run this script." + exit 1 + fi + + if ! command -v awk > /dev/null 2>&1 + then + echo "Error: awk not found in PATH. It must be installed to run this script." + exit 1 + fi + + return 0 +} + +##################################################################### +# Function getOS +# +# Getting the OS is not the same on all distributions. First, we use +# uname to find out if we are running on Linux or FreeBSD. For all the +# supported versions of Debian and Ubuntu, we expect to find the +# /etc/os-release file which has multiple lines with name-value pairs +# from which we can get the OS name and version. For RedHat and its +# variants, the os-release file may or may not exist, depending on the +# version. If it doesn't, then we look for the release package and +# get the OS and version from the package name. For FreeBSD, we use the +# "uname -rs" command. +# +# A string is written to stdout with three values separated by ":": +# OS +# OS Name +# OS Version +# +# If none of these files was found, an empty string is written. +# +# Return: 0 for success, 1 for error +##################################################################### +getOS () { + + os="" + osName="" + osVersion="" + + LC_ALL=C + + os=$(uname | tr '[:upper:]' '[:lower:]') + + if [ "$os" != "linux" ] && [ "$os" != "freebsd" ]; then + echoErr "Error: Operating system is not Linux or FreeBSD, can't proceed" + echo "On macOS, try 'brew install nginx/unit/unit'" + echo + return 1 + fi + + if [ "$os" = "linux" ]; then + if [ -f "$osRelease" ]; then + # The value for the ID and VERSION_ID may or may not be in quotes + osName=$( grep "^ID=" "$osRelease" | sed s/\"//g | awk -F= '{ print $2 }') + osVersion=$(grep "^VERSION_ID=" "$osRelease" | sed s/\"//g | awk -F= '{ print $2 }') + else + # rhel or centos 6.* + if rpm -q redhat-release-server >/dev/null 2>&1; then + osName=rhel + osVersion=$(rpm -q redhat-release-server |sed 's/.*-//' | awk -F. '{print $1"."$2;}') + elif rpm -q centos-release >/dev/null 2>&1; then + osName=centos + osVersion=$(rpm -q centos-release | sed 's/centos-release-//' | sed 's/\..*//' | awk -F- '{print $1"."$2;}') + else + echoErr "Error: Unable to determine the operating system and version, or the OS is not supported" + echo + return 1 + fi + fi + else + osName=$os + osVersion=$(uname -rs | awk -F '[ -]' '{print $2}') + if [ -z "$osVersion" ]; then + echoErr "Unable to get the FreeBSD version" + echo + return 1 + fi + fi + + # Force osName to lowercase + osName=$(echo "$osName" | tr '[:upper:]' '[:lower:]') + echoDebug "getOS: os=$os osName=$osName osVersion=$osVersion" + echo "$os:$osName:$osVersion" + + return 0 +} + + +installDebian () { + + echoDebug "Install on Debian" + + curl --output /usr/share/keyrings/nginx-keyring.gpg https://unit.nginx.org/keys/nginx-keyring.gpg + + apt install -y apt-transport-https lsb-release ca-certificates + + printf "deb [signed-by=/usr/share/keyrings/nginx-keyring.gpg] https://packages.nginx.org/unit/debian/ %s unit\n" "$(lsb_release -cs)" | tee /etc/apt/sources.list.d/unit.list + printf "deb-src [signed-by=/usr/share/keyrings/nginx-keyring.gpg] https://packages.nginx.org/unit/debian/ %s unit\n" "$(lsb_release -cs)" | tee -a /etc/apt/sources.list.d/unit.list + + apt update + + return 0 +} + +installUbuntu () { + + echoDebug "Install on Ubuntu" + + curl --output /usr/share/keyrings/nginx-keyring.gpg https://unit.nginx.org/keys/nginx-keyring.gpg + + apt install -y apt-transport-https lsb-release ca-certificates + + printf "deb [signed-by=/usr/share/keyrings/nginx-keyring.gpg] https://packages.nginx.org/unit/ubuntu/ %s unit\n" "$(lsb_release -cs)" | tee /etc/apt/sources.list.d/unit.list + printf "deb-src [signed-by=/usr/share/keyrings/nginx-keyring.gpg] https://packages.nginx.org/unit/ubuntu/ %s unit\n" "$(lsb_release -cs)" | tee -a /etc/apt/sources.list.d/unit.list + + apt update + + return 0 +} + +installRedHat () { + + echoDebug "Install on RedHat/CentOS/Oracle" + + case "$osVersion" in + 6|6.*|7|7.*|8|8.*) + cat << __EOF__ > /etc/yum.repos.d/unit.repo +[unit] +name=unit repo +baseurl=https://packages.nginx.org/unit/rhel/\$releasever/\$basearch/ +gpgcheck=0 +enabled=1 +__EOF__ + ;; + *) + echo "Unsupported $osName version: $osVersion" + exit 1 + ;; + esac + + yum makecache + + return 0 +} + +installAmazon () { + + echoDebug "Install on Amazon" + + case "$osVersion" in + 2) + cat << __EOF__ > /etc/yum.repos.d/unit.repo +[unit] +name=unit repo +baseurl=https://packages.nginx.org/unit/amzn2/\$releasever/\$basearch/ +gpgcheck=0 +enabled=1 +__EOF__ + ;; + *) + cat << __EOF__ > /etc/yum.repos.d/unit.repo +[unit] +name=unit repo +baseurl=https://packages.nginx.org/unit/amzn/\$releasever/\$basearch/ +gpgcheck=0 +enabled=1 +__EOF__ + ;; + esac + + yum makecache + + return 0 +} + +installFedora () { + + echoDebug "Install on Fedora" + + cat << __EOF__ > /etc/yum.repos.d/unit.repo +[unit] +name=unit repo +baseurl=https://packages.nginx.org/unit/fedora/\$releasever/\$basearch/ +gpgcheck=0 +enabled=1 +__EOF__ + + dnf makecache + + return 0 +} + + +am_i_root() { + + USERID=$(id -u) + if [ 0 -ne "$USERID" ]; then + echoErr "This script requires root privileges to run; now exiting." + exit 1 + fi + + return 0 +} + +echoErr () { + + echo "$*" 1>&2; +} + +echoDebug () { + + if [ "$debug" -eq 1 ]; then + echo "$@" 1>&2; + fi +} + +main() { +debug=0 # If set to 1, debug message will be displayed + +checkOSPrereqs + +# The name and location of the files that will be used to get Linux +# release info +osRelease="/etc/os-release" + +os="" # Will be "linux" or "freebsd" +osName="" # Will be "ubuntu", "debian", "rhel", + # "centos", "suse", "amzn", or "freebsd" +osVersion="" + +am_i_root + +echo "This script will setup repositories for NGINX Unit" + +# Check the OS +osNameVersion=$(getOS) +if [ -z "$osNameVersion" ]; then + echoErr "Error getting the operating system information" + exit 1 +fi + +# Break out the OS, name, and version +os=$(echo "$osNameVersion" | awk -F: '{print $1}') +osName=$(echo "$osNameVersion" | awk -F: '{print $2}') +osVersion=$(echo "$osNameVersion" | awk -F: '{print $3}') + +# Call the appropriate installation function +case "$osName" in + debian) + installDebian + ;; + ubuntu) + installUbuntu + ;; + rhel) + installRedHat + ;; + centos) + installRedHat + ;; + ol) + installRedHat + ;; + amzn) + installAmazon + ;; + fedora) + installFedora + ;; + *) + echo "$osName is not supported" + exit 1 + ;; +esac + +echo +echo "All done - NGINX Unit repositories for "$osName" "$osVersion" are set up" +echo "Further steps: https://unit.nginx.org/installation/#official-packages" + +} + +main + +exit 0 -- cgit From 0c9f417affcef25b2ede5aaf0967eb411a247e33 Mon Sep 17 00:00:00 2001 From: Artem Konev Date: Thu, 24 Nov 2022 15:06:54 +0000 Subject: Added security.txt. --- SECURITY.txt | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 SECURITY.txt diff --git a/SECURITY.txt b/SECURITY.txt new file mode 100644 index 00000000..a705bef7 --- /dev/null +++ b/SECURITY.txt @@ -0,0 +1,30 @@ +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA256 + +# +# Please report security issues as below, specifically +# mentioning NGINX Unit in the subject. +# +Canonical: https://unit.nginx.org/.well-known/security.txt +Contact: mailto:security-alert@nginx.org +Encryption: https://nginx.org/keys/maxim.key +Encryption: https://nginx.org/keys/sb.key +Encryption: https://nginx.org/keys/thresh.key +Expires: 2024-01-01T00:00:00.000Z +Policy: https://www.first.org/cvss/v3.1/specification-document +Preferred-Languages: en + +-----BEGIN PGP SIGNATURE----- + +iQHEBAEBCAAuFiEEE8gqY7YDV2FW4wpOoOqYG2aw2WcFAmN7X/UQHGsucGF2bG92 +QGY1LmNvbQAKCRCg6pgbZrDZZ4wFDADIZz5UwVUaxQ6mAfi+3Gs28NLXQp5kBILJ +PC9Rhjlksufbby5yd4lh+JMZ8U2YRQ8OWne6Kl0NvZHDcP2OyBOdiBUXvnE+ZcNz +ujT3JMk15l1FxKbIitUzwZ+QcXOKTqsoavPs5hrGrrJNQWLhqAH8uESDdI7AUM5R +BOQ9Z6ENw3rgEtrtMNMdwt+pt2/+1cuu/4PuIuFhjYyCuS7i7tyFtbkc9BTlx03I +99g9bqKltWAvxGrMi+xfFVOnTgWp0b+oKsN8jgQji1zNMSx7UmrFq8uSpNV3eR5t +a4iVZQsIRUVVSYh8VkZagtbiw4WXaEnbwUgxj/4K2rNvkn5jFk+NkzSALN8/7ocp +U5R5ctku511bJiwFSUkTx8nkd58bzqqQ0EHr/3uTmfXSTTZYdUXuXXCSMzuUBEOi +y9n+2JFRdlEXxqwhszJxAXhs6VH2su0laX2UOMMnw6X2GFF3CU4PK0qoalWLSh47 +6aiL99zgqrq0IFibRTXCo1a3RPqiYB0= +=GiB/ +-----END PGP SIGNATURE----- -- cgit From 4735931ace321752c387dae04c8b217ef22897ee Mon Sep 17 00:00:00 2001 From: Zhidao HONG Date: Sun, 20 Nov 2022 23:15:01 +0800 Subject: Var: separating nxt_tstr_t from nxt_var_t. It's for the introduction of njs support. For each option that supports native variable and JS template literals introduced next, it's unified as template string. No functional changes. --- auto/sources | 1 + src/nxt_conf.h | 2 +- src/nxt_conf_validation.c | 6 +- src/nxt_http.h | 5 +- src/nxt_http_request.c | 4 +- src/nxt_http_return.c | 30 +++--- src/nxt_http_route.c | 44 +++++---- src/nxt_http_static.c | 52 ++++++----- src/nxt_http_variables.c | 6 +- src/nxt_main.h | 1 + src/nxt_router.c | 4 +- src/nxt_router.h | 6 +- src/nxt_router_access_log.c | 27 +++--- src/nxt_tstr.c | 223 ++++++++++++++++++++++++++++++++++++++++++++ src/nxt_tstr.h | 45 +++++++++ src/nxt_var.c | 148 +++++------------------------ src/nxt_var.h | 25 ++--- 17 files changed, 403 insertions(+), 226 deletions(-) create mode 100644 src/nxt_tstr.c create mode 100644 src/nxt_tstr.h diff --git a/auto/sources b/auto/sources index 8f07cc0c..7d3f62cf 100644 --- a/auto/sources +++ b/auto/sources @@ -34,6 +34,7 @@ NXT_LIB_SRCS=" \ src/nxt_parse.c \ src/nxt_sprintf.c \ src/nxt_var.c \ + src/nxt_tstr.c \ src/nxt_file_name.c \ src/nxt_log.c \ src/nxt_djb_hash.c \ diff --git a/src/nxt_conf.h b/src/nxt_conf.h index c8a276c0..1b13f5ae 100644 --- a/src/nxt_conf.h +++ b/src/nxt_conf.h @@ -72,7 +72,7 @@ typedef struct { nxt_mp_t *pool; nxt_str_t error; void *ctx; - nxt_array_t *var_fields; /* of nxt_var_field_t */ + nxt_tstr_state_t *tstr_state; nxt_mp_t *conf_pool; nxt_uint_t ver; } nxt_conf_validation_t; diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index 7fbe7e29..09b3be55 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -1227,8 +1227,8 @@ nxt_conf_validate(nxt_conf_validation_t *vldt) { nxt_int_t ret; - vldt->var_fields = nxt_array_create(vldt->pool, 4, sizeof(nxt_var_field_t)); - if (nxt_slow_path(vldt->var_fields == NULL)) { + vldt->tstr_state = nxt_tstr_state_new(vldt->pool); + if (nxt_slow_path(vldt->tstr_state == NULL)) { return NXT_ERROR; } @@ -1364,7 +1364,7 @@ nxt_conf_vldt_var(nxt_conf_validation_t *vldt, nxt_str_t *name, { u_char error[NXT_MAX_ERROR_STR]; - if (nxt_var_test(value, vldt->var_fields, error) != NXT_OK) { + if (nxt_tstr_test(vldt->tstr_state, value, error) != NXT_OK) { return nxt_conf_vldt_error(vldt, "%s in the \"%V\" value.", error, name); } diff --git a/src/nxt_http.h b/src/nxt_http.h index d5bff712..e29fe60a 100644 --- a/src/nxt_http.h +++ b/src/nxt_http.h @@ -169,7 +169,8 @@ struct nxt_http_request_s { nxt_timer_t timer; void *timer_data; - nxt_var_query_t *var_query; + nxt_tstr_query_t *tstr_query; + nxt_var_cache_t var_cache; void *req_rpc_data; @@ -245,7 +246,7 @@ struct nxt_http_action_s { nxt_http_route_t *route; nxt_upstream_t *upstream; uint32_t upstream_number; - nxt_var_t *var; + nxt_tstr_t *tstr; nxt_str_t *pass; } u; diff --git a/src/nxt_http_request.c b/src/nxt_http_request.c index 5c1455bf..84a67415 100644 --- a/src/nxt_http_request.c +++ b/src/nxt_http_request.c @@ -282,6 +282,8 @@ nxt_http_request_create(nxt_task_t *task) task->thread->engine->requests_cnt++; + r->var_cache.pool = mp; + return r; fail: @@ -795,7 +797,7 @@ nxt_http_request_error_handler(nxt_task_t *task, void *obj, void *data) void nxt_http_request_close_handler(nxt_task_t *task, void *obj, void *data) { - nxt_var_t *log_format; + nxt_tstr_t *log_format; nxt_http_proto_t proto; nxt_http_request_t *r; nxt_http_protocol_t protocol; diff --git a/src/nxt_http_return.c b/src/nxt_http_return.c index 9f3c4fc5..63c7e06f 100644 --- a/src/nxt_http_return.c +++ b/src/nxt_http_return.c @@ -9,7 +9,7 @@ typedef struct { nxt_http_status_t status; - nxt_var_t *location; + nxt_tstr_t *location; nxt_str_t encoded; } nxt_http_return_conf_t; @@ -25,7 +25,7 @@ static nxt_http_action_t *nxt_http_return(nxt_task_t *task, static nxt_int_t nxt_http_return_encode(nxt_mp_t *mp, nxt_str_t *encoded, const nxt_str_t *location); static void nxt_http_return_send_ready(nxt_task_t *task, void *obj, void *data); -static void nxt_http_return_var_error(nxt_task_t *task, void *obj, void *data); +static void nxt_http_return_send_error(nxt_task_t *task, void *obj, void *data); static const nxt_http_request_state_t nxt_http_return_send_state; @@ -57,13 +57,13 @@ nxt_http_return_init(nxt_router_conf_t *rtcf, nxt_http_action_t *action, nxt_conf_get_string(acf->location, &str); - conf->location = nxt_var_compile(&str, mp, rtcf->var_fields, 0); + conf->location = nxt_tstr_compile(rtcf->tstr_state, &str, 0); if (nxt_slow_path(conf->location == NULL)) { return NXT_ERROR; } - if (nxt_var_is_const(conf->location)) { - nxt_var_raw(conf->location, &str); + if (nxt_tstr_is_const(conf->location)) { + nxt_tstr_str(conf->location, &str); return nxt_http_return_encode(mp, &conf->encoded, &str); } @@ -76,6 +76,7 @@ nxt_http_return(nxt_task_t *task, nxt_http_request_t *r, nxt_http_action_t *action) { nxt_int_t ret; + nxt_router_conf_t *rtcf; nxt_http_return_ctx_t *ctx; nxt_http_return_conf_t *conf; @@ -88,7 +89,7 @@ nxt_http_return(nxt_task_t *task, nxt_http_request_t *r, nxt_str_set(&loc, ""); } else { - nxt_var_raw(conf->location, &loc); + nxt_tstr_str(conf->location, &loc); } nxt_debug(task, "http return: %d (loc: \"%V\")", conf->status, &loc); @@ -114,7 +115,7 @@ nxt_http_return(nxt_task_t *task, nxt_http_request_t *r, r->status = conf->status; r->resp.content_length_n = 0; - if (ctx == NULL || nxt_var_is_const(conf->location)) { + if (ctx == NULL || nxt_tstr_is_const(conf->location)) { if (ctx != NULL) { ctx->encoded = conf->encoded; } @@ -122,16 +123,19 @@ nxt_http_return(nxt_task_t *task, nxt_http_request_t *r, nxt_http_return_send_ready(task, r, ctx); } else { - ret = nxt_var_query_init(&r->var_query, r, r->mem_pool); + rtcf = r->conf->socket_conf->router_conf; + + ret = nxt_tstr_query_init(&r->tstr_query, rtcf->tstr_state, + &r->var_cache, r, r->mem_pool); if (nxt_slow_path(ret != NXT_OK)) { goto fail; } - nxt_var_query(task, r->var_query, conf->location, &ctx->location); + nxt_tstr_query(task, r->tstr_query, conf->location, &ctx->location); - nxt_var_query_resolve(task, r->var_query, ctx, - nxt_http_return_send_ready, - nxt_http_return_var_error); + nxt_tstr_query_resolve(task, r->tstr_query, ctx, + nxt_http_return_send_ready, + nxt_http_return_send_error); } return NULL; @@ -213,7 +217,7 @@ fail: static void -nxt_http_return_var_error(nxt_task_t *task, void *obj, void *data) +nxt_http_return_send_error(nxt_task_t *task, void *obj, void *data) { nxt_http_request_t *r; diff --git a/src/nxt_http_route.c b/src/nxt_http_route.c index 1f2fe883..77a59e9c 100644 --- a/src/nxt_http_route.c +++ b/src/nxt_http_route.c @@ -193,8 +193,8 @@ static nxt_int_t nxt_http_action_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_http_action_t *action); static nxt_http_action_t *nxt_http_pass_var(nxt_task_t *task, nxt_http_request_t *r, nxt_http_action_t *action); -static void nxt_http_pass_var_ready(nxt_task_t *task, void *obj, void *data); -static void nxt_http_pass_var_error(nxt_task_t *task, void *obj, void *data); +static void nxt_http_pass_query_ready(nxt_task_t *task, void *obj, void *data); +static void nxt_http_pass_query_error(nxt_task_t *task, void *obj, void *data); static nxt_int_t nxt_http_pass_find(nxt_mp_t *mp, nxt_router_conf_t *rtcf, nxt_str_t *pass, nxt_http_action_t *action); static nxt_int_t nxt_http_route_find(nxt_http_routes_t *routes, nxt_str_t *name, @@ -673,8 +673,8 @@ nxt_http_action_init(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_conf_get_string(acf.pass, &pass); - action->u.var = nxt_var_compile(&pass, mp, rtcf->var_fields, 0); - if (nxt_slow_path(action->u.var == NULL)) { + action->u.tstr = nxt_tstr_compile(rtcf->tstr_state, &pass, 0); + if (nxt_slow_path(action->u.tstr == NULL)) { return NXT_ERROR; } @@ -1272,8 +1272,8 @@ nxt_http_action_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, return NXT_OK; } - if (nxt_var_is_const(action->u.var)) { - nxt_var_raw(action->u.var, &pass); + if (nxt_tstr_is_const(action->u.tstr)) { + nxt_tstr_str(action->u.tstr, &pass); ret = nxt_http_pass_find(tmcf->mem_pool, tmcf->router_conf, &pass, action); @@ -1293,17 +1293,21 @@ static nxt_http_action_t * nxt_http_pass_var(nxt_task_t *task, nxt_http_request_t *r, nxt_http_action_t *action) { - nxt_int_t ret; - nxt_str_t str; - nxt_var_t *var; + nxt_int_t ret; + nxt_str_t str; + nxt_tstr_t *tstr; + nxt_router_conf_t *rtcf; - var = action->u.var; + tstr = action->u.tstr; - nxt_var_raw(var, &str); + nxt_tstr_str(tstr, &str); nxt_debug(task, "http pass: \"%V\"", &str); - ret = nxt_var_query_init(&r->var_query, r, r->mem_pool); + rtcf = r->conf->socket_conf->router_conf; + + ret = nxt_tstr_query_init(&r->tstr_query, rtcf->tstr_state, &r->var_cache, + r, r->mem_pool); if (nxt_slow_path(ret != NXT_OK)) { goto fail; } @@ -1316,10 +1320,10 @@ nxt_http_pass_var(nxt_task_t *task, nxt_http_request_t *r, action->u.pass = nxt_pointer_to(action, sizeof(nxt_http_action_t)); - nxt_var_query(task, r->var_query, var, action->u.pass); - nxt_var_query_resolve(task, r->var_query, action, - nxt_http_pass_var_ready, - nxt_http_pass_var_error); + nxt_tstr_query(task, r->tstr_query, tstr, action->u.pass); + nxt_tstr_query_resolve(task, r->tstr_query, action, + nxt_http_pass_query_ready, + nxt_http_pass_query_error); return NULL; fail: @@ -1330,7 +1334,7 @@ fail: static void -nxt_http_pass_var_ready(nxt_task_t *task, void *obj, void *data) +nxt_http_pass_query_ready(nxt_task_t *task, void *obj, void *data) { nxt_int_t ret; nxt_router_conf_t *rtcf; @@ -1359,7 +1363,7 @@ nxt_http_pass_var_ready(nxt_task_t *task, void *obj, void *data) static void -nxt_http_pass_var_error(nxt_task_t *task, void *obj, void *data) +nxt_http_pass_query_error(nxt_task_t *task, void *obj, void *data) { nxt_http_request_t *r; @@ -1497,8 +1501,8 @@ nxt_http_action_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, return NULL; } - action->u.var = nxt_var_compile(pass, mp, rtcf->var_fields, 0); - if (nxt_slow_path(action->u.var == NULL)) { + action->u.tstr = nxt_tstr_compile(rtcf->tstr_state, pass, 0); + if (nxt_slow_path(action->u.tstr == NULL)) { return NULL; } diff --git a/src/nxt_http_static.c b/src/nxt_http_static.c index 0507e038..7143ecd3 100644 --- a/src/nxt_http_static.c +++ b/src/nxt_http_static.c @@ -8,7 +8,7 @@ typedef struct { - nxt_var_t *var; + nxt_tstr_t *tstr; #if (NXT_HAVE_OPENAT2) u_char *fname; #endif @@ -21,7 +21,7 @@ typedef struct { nxt_http_static_share_t *shares; nxt_str_t index; #if (NXT_HAVE_OPENAT2) - nxt_var_t *chroot; + nxt_tstr_t *chroot; nxt_uint_t resolve; #endif nxt_http_route_rule_t *types; @@ -48,7 +48,7 @@ static nxt_http_action_t *nxt_http_static(nxt_task_t *task, static void nxt_http_static_iterate(nxt_task_t *task, nxt_http_request_t *r, nxt_http_static_ctx_t *ctx); static void nxt_http_static_send_ready(nxt_task_t *task, void *obj, void *data); -static void nxt_http_static_var_error(nxt_task_t *task, void *obj, void *data); +static void nxt_http_static_send_error(nxt_task_t *task, void *obj, void *data); static void nxt_http_static_next(nxt_task_t *task, nxt_http_request_t *r, nxt_http_static_ctx_t *ctx, nxt_http_status_t status); #if (NXT_HAVE_OPENAT2) @@ -77,7 +77,7 @@ nxt_http_static_init(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, uint32_t i; nxt_mp_t *mp; nxt_str_t str, *ret; - nxt_var_t *var; + nxt_tstr_t *tstr; nxt_conf_value_t *cv; nxt_router_conf_t *rtcf; nxt_http_static_conf_t *conf; @@ -104,13 +104,13 @@ nxt_http_static_init(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, cv = nxt_conf_get_array_element_or_itself(acf->share, i); nxt_conf_get_string(cv, &str); - var = nxt_var_compile(&str, mp, rtcf->var_fields, NXT_VAR_STRZ); - if (nxt_slow_path(var == NULL)) { + tstr = nxt_tstr_compile(rtcf->tstr_state, &str, NXT_TSTR_STRZ); + if (nxt_slow_path(tstr == NULL)) { return NXT_ERROR; } - conf->shares[i].var = var; - conf->shares[i].is_const = nxt_var_is_const(var); + conf->shares[i].tstr = tstr; + conf->shares[i].is_const = nxt_tstr_is_const(tstr); } if (acf->index == NULL) { @@ -130,20 +130,20 @@ nxt_http_static_init(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_str_t chr, shr; nxt_bool_t is_const; - conf->chroot = nxt_var_compile(&acf->chroot, mp, rtcf->var_fields, - NXT_VAR_STRZ); + conf->chroot = nxt_tstr_compile(rtcf->tstr_state, &acf->chroot, + NXT_TSTR_STRZ); if (nxt_slow_path(conf->chroot == NULL)) { return NXT_ERROR; } - is_const = nxt_var_is_const(conf->chroot); + is_const = nxt_tstr_is_const(conf->chroot); for (i = 0; i < conf->nshares; i++) { conf->shares[i].is_const &= is_const; if (conf->shares[i].is_const) { - nxt_var_raw(conf->chroot, &chr); - nxt_var_raw(conf->shares[i].var, &shr); + nxt_tstr_str(conf->chroot, &chr); + nxt_tstr_str(conf->shares[i].tstr, &shr); conf->shares[i].fname = nxt_http_static_chroot_match(chr.start, shr.start); @@ -229,6 +229,7 @@ nxt_http_static_iterate(nxt_task_t *task, nxt_http_request_t *r, nxt_http_static_ctx_t *ctx) { nxt_int_t ret; + nxt_router_conf_t *rtcf; nxt_http_static_conf_t *conf; nxt_http_static_share_t *share; @@ -240,14 +241,14 @@ nxt_http_static_iterate(nxt_task_t *task, nxt_http_request_t *r, nxt_str_t shr; nxt_str_t idx; - nxt_var_raw(share->var, &shr); + nxt_tstr_str(share->tstr, &shr); idx = conf->index; #if (NXT_HAVE_OPENAT2) nxt_str_t chr; if (conf->chroot != NULL) { - nxt_var_raw(conf->chroot, &chr); + nxt_tstr_str(conf->chroot, &chr); } else { nxt_str_set(&chr, ""); @@ -261,34 +262,37 @@ nxt_http_static_iterate(nxt_task_t *task, nxt_http_request_t *r, #endif /* NXT_DEBUG */ if (share->is_const) { - nxt_var_raw(share->var, &ctx->share); + nxt_tstr_str(share->tstr, &ctx->share); #if (NXT_HAVE_OPENAT2) if (conf->chroot != NULL && ctx->share_idx == 0) { - nxt_var_raw(conf->chroot, &ctx->chroot); + nxt_tstr_str(conf->chroot, &ctx->chroot); } #endif nxt_http_static_send_ready(task, r, ctx); } else { - ret = nxt_var_query_init(&r->var_query, r, r->mem_pool); + rtcf = r->conf->socket_conf->router_conf; + + ret = nxt_tstr_query_init(&r->tstr_query, rtcf->tstr_state, + &r->var_cache, r, r->mem_pool); if (nxt_slow_path(ret != NXT_OK)) { nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR); return; } - nxt_var_query(task, r->var_query, share->var, &ctx->share); + nxt_tstr_query(task, r->tstr_query, share->tstr, &ctx->share); #if (NXT_HAVE_OPENAT2) if (conf->chroot != NULL && ctx->share_idx == 0) { - nxt_var_query(task, r->var_query, conf->chroot, &ctx->chroot); + nxt_tstr_query(task, r->tstr_query, conf->chroot, &ctx->chroot); } #endif - nxt_var_query_resolve(task, r->var_query, ctx, - nxt_http_static_send_ready, - nxt_http_static_var_error); + nxt_tstr_query_resolve(task, r->tstr_query, ctx, + nxt_http_static_send_ready, + nxt_http_static_send_error); } } @@ -658,7 +662,7 @@ fail: static void -nxt_http_static_var_error(nxt_task_t *task, void *obj, void *data) +nxt_http_static_send_error(nxt_task_t *task, void *obj, void *data) { nxt_http_request_t *r; diff --git a/src/nxt_http_variables.c b/src/nxt_http_variables.c index eb341716..fa0244db 100644 --- a/src/nxt_http_variables.c +++ b/src/nxt_http_variables.c @@ -407,7 +407,7 @@ nxt_http_var_arg(nxt_task_t *task, nxt_str_t *str, void *ctx, uint16_t field) rtcf = r->conf->socket_conf->router_conf; - vf = nxt_var_field_get(rtcf->var_fields, field); + vf = nxt_var_field_get(rtcf->tstr_state->var_fields, field); args = nxt_http_arguments_parse(r); if (nxt_slow_path(args == NULL)) { @@ -450,7 +450,7 @@ nxt_http_var_header(nxt_task_t *task, nxt_str_t *str, void *ctx, uint16_t field) rtcf = r->conf->socket_conf->router_conf; - vf = nxt_var_field_get(rtcf->var_fields, field); + vf = nxt_var_field_get(rtcf->tstr_state->var_fields, field); nxt_list_each(f, r->fields) { @@ -485,7 +485,7 @@ nxt_http_var_cookie(nxt_task_t *task, nxt_str_t *str, void *ctx, uint16_t field) rtcf = r->conf->socket_conf->router_conf; - vf = nxt_var_field_get(rtcf->var_fields, field); + vf = nxt_var_field_get(rtcf->tstr_state->var_fields, field); cookies = nxt_http_cookies_parse(r); if (nxt_slow_path(cookies == NULL)) { diff --git a/src/nxt_main.h b/src/nxt_main.h index a51a1ce7..b0cdc2d3 100644 --- a/src/nxt_main.h +++ b/src/nxt_main.h @@ -68,6 +68,7 @@ typedef uint16_t nxt_port_id_t; #include #include #include +#include /* TODO: remove unused */ diff --git a/src/nxt_router.c b/src/nxt_router.c index cfd79bf5..3ad78fa4 100644 --- a/src/nxt_router.c +++ b/src/nxt_router.c @@ -1060,8 +1060,8 @@ nxt_router_temp_conf(nxt_task_t *task) rtcf->mem_pool = mp; - rtcf->var_fields = nxt_array_create(mp, 4, sizeof(nxt_var_field_t)); - if (nxt_slow_path(rtcf->var_fields == NULL)) { + rtcf->tstr_state = nxt_tstr_state_new(mp); + if (nxt_slow_path(rtcf->tstr_state == NULL)) { goto fail; } diff --git a/src/nxt_router.h b/src/nxt_router.h index a6add219..11094960 100644 --- a/src/nxt_router.h +++ b/src/nxt_router.h @@ -43,7 +43,7 @@ typedef struct { uint32_t threads; nxt_mp_t *mem_pool; - nxt_array_t *var_fields; /* of nxt_var_field_t */ + nxt_tstr_state_t *tstr_state; nxt_router_t *router; nxt_http_routes_t *routes; @@ -53,7 +53,7 @@ typedef struct { nxt_lvlhsh_t apps_hash; nxt_router_access_log_t *access_log; - nxt_var_t *log_format; + nxt_tstr_t *log_format; } nxt_router_conf_t; @@ -225,7 +225,7 @@ typedef struct { struct nxt_router_access_log_s { void (*handler)(nxt_task_t *task, nxt_http_request_t *r, nxt_router_access_log_t *access_log, - nxt_var_t *format); + nxt_tstr_t *format); nxt_fd_t fd; nxt_str_t path; uint32_t count; diff --git a/src/nxt_router_access_log.c b/src/nxt_router_access_log.c index dc2a6687..9da366f4 100644 --- a/src/nxt_router_access_log.c +++ b/src/nxt_router_access_log.c @@ -24,7 +24,7 @@ typedef struct { static void nxt_router_access_log_writer(nxt_task_t *task, nxt_http_request_t *r, nxt_router_access_log_t *access_log, - nxt_var_t *format); + nxt_tstr_t *format); static void nxt_router_access_log_write_ready(nxt_task_t *task, void *obj, void *data); static void nxt_router_access_log_write_error(nxt_task_t *task, void *obj, @@ -63,7 +63,7 @@ nxt_router_access_log_create(nxt_task_t *task, nxt_router_conf_t *rtcf, u_char *p; nxt_int_t ret; nxt_str_t str; - nxt_var_t *format; + nxt_tstr_t *format; nxt_router_t *router; nxt_router_access_log_t *access_log; nxt_router_access_log_conf_t alcf; @@ -125,8 +125,7 @@ nxt_router_access_log_create(nxt_task_t *task, nxt_router_conf_t *rtcf, p = nxt_cpymem(str.start, alcf.format.start, alcf.format.length); *p = '\n'; - format = nxt_var_compile(&str, rtcf->mem_pool, rtcf->var_fields, - NXT_VAR_LOGGING); + format = nxt_tstr_compile(rtcf->tstr_state, &str, NXT_TSTR_LOGGING); if (nxt_slow_path(format == NULL)) { return NXT_ERROR; } @@ -140,9 +139,10 @@ nxt_router_access_log_create(nxt_task_t *task, nxt_router_conf_t *rtcf, static void nxt_router_access_log_writer(nxt_task_t *task, nxt_http_request_t *r, - nxt_router_access_log_t *access_log, nxt_var_t *format) + nxt_router_access_log_t *access_log, nxt_tstr_t *format) { nxt_int_t ret; + nxt_router_conf_t *rtcf; nxt_router_access_log_ctx_t *ctx; ctx = nxt_mp_get(r->mem_pool, sizeof(nxt_router_access_log_ctx_t)); @@ -152,21 +152,24 @@ nxt_router_access_log_writer(nxt_task_t *task, nxt_http_request_t *r, ctx->access_log = access_log; - if (nxt_var_is_const(format)) { - nxt_var_raw(format, &ctx->text); + if (nxt_tstr_is_const(format)) { + nxt_tstr_str(format, &ctx->text); nxt_router_access_log_write_ready(task, r, ctx); } else { - ret = nxt_var_query_init(&r->var_query, r, r->mem_pool); + rtcf = r->conf->socket_conf->router_conf; + + ret = nxt_tstr_query_init(&r->tstr_query, rtcf->tstr_state, + &r->var_cache, r, r->mem_pool); if (nxt_slow_path(ret != NXT_OK)) { return; } - nxt_var_query(task, r->var_query, format, &ctx->text); - nxt_var_query_resolve(task, r->var_query, ctx, - nxt_router_access_log_write_ready, - nxt_router_access_log_write_error); + nxt_tstr_query(task, r->tstr_query, format, &ctx->text); + nxt_tstr_query_resolve(task, r->tstr_query, ctx, + nxt_router_access_log_write_ready, + nxt_router_access_log_write_error); } } diff --git a/src/nxt_tstr.c b/src/nxt_tstr.c new file mode 100644 index 00000000..dff61952 --- /dev/null +++ b/src/nxt_tstr.c @@ -0,0 +1,223 @@ + +/* + * Copyright (C) NGINX, Inc. + */ + +#include + + +typedef enum { + NXT_TSTR_CONST = 0, + NXT_TSTR_VAR, +} nxt_tstr_type_t; + + +struct nxt_tstr_s { + nxt_str_t str; + nxt_var_t *var; + nxt_tstr_flags_t flags; + nxt_tstr_type_t type; +}; + + +struct nxt_tstr_query_s { + nxt_mp_t *pool; + + nxt_tstr_state_t *state; + nxt_var_cache_t *cache; + + nxt_uint_t waiting; + nxt_uint_t failed; /* 1 bit */ + + void *ctx; + void *data; + + nxt_work_handler_t ready; + nxt_work_handler_t error; +}; + + +nxt_tstr_state_t * +nxt_tstr_state_new(nxt_mp_t *mp) +{ + nxt_tstr_state_t *state; + + state = nxt_mp_get(mp, sizeof(nxt_tstr_state_t)); + if (nxt_slow_path(state == NULL)) { + return NULL; + } + + state->pool = mp; + + state->var_fields = nxt_array_create(mp, 4, sizeof(nxt_var_field_t)); + if (nxt_slow_path(state->var_fields == NULL)) { + return NULL; + } + + return state; +} + + +nxt_tstr_t * +nxt_tstr_compile(nxt_tstr_state_t *state, nxt_str_t *str, + nxt_tstr_flags_t flags) +{ + u_char *p; + nxt_tstr_t *tstr; + nxt_bool_t strz; + + strz = (flags & NXT_TSTR_STRZ) != 0; + + tstr = nxt_mp_get(state->pool, sizeof(nxt_tstr_t)); + if (nxt_slow_path(tstr == NULL)) { + return NULL; + } + + tstr->str.length = str->length + strz; + + tstr->str.start = nxt_mp_nget(state->pool, tstr->str.length); + if (nxt_slow_path(tstr->str.start == NULL)) { + return NULL; + } + + p = nxt_cpymem(tstr->str.start, str->start, str->length); + + if (strz) { + *p = '\0'; + } + + tstr->flags = flags; + + p = nxt_memchr(str->start, '$', str->length); + + if (p != NULL) { + tstr->type = NXT_TSTR_VAR; + + tstr->var = nxt_var_compile(&tstr->str, state->pool, state->var_fields); + if (nxt_slow_path(tstr->var == NULL)) { + return NULL; + } + + } else { + tstr->type = NXT_TSTR_CONST; + } + + return tstr; +} + + +nxt_int_t +nxt_tstr_test(nxt_tstr_state_t *state, nxt_str_t *str, u_char *error) +{ + return nxt_var_test(str, state->var_fields, error); +} + + +nxt_bool_t +nxt_tstr_is_const(nxt_tstr_t *tstr) +{ + return (tstr->type == NXT_TSTR_CONST); +} + + +void +nxt_tstr_str(nxt_tstr_t *tstr, nxt_str_t *str) +{ + *str = tstr->str; + + if (tstr->flags & NXT_TSTR_STRZ) { + str->length--; + } +} + + +nxt_int_t +nxt_tstr_query_init(nxt_tstr_query_t **query_p, nxt_tstr_state_t *state, + nxt_var_cache_t *cache, void *ctx, nxt_mp_t *mp) +{ + nxt_tstr_query_t *query; + + query = *query_p; + + if (*query_p == NULL) { + query = nxt_mp_zget(mp, sizeof(nxt_tstr_query_t)); + if (nxt_slow_path(query == NULL)) { + return NXT_ERROR; + } + } + + query->pool = mp; + query->state = state; + query->cache = cache; + query->ctx = ctx; + + *query_p = query; + + return NXT_OK; +} + + +void +nxt_tstr_query(nxt_task_t *task, nxt_tstr_query_t *query, nxt_tstr_t *tstr, + nxt_str_t *val) +{ + nxt_int_t ret; + + if (nxt_tstr_is_const(tstr)) { + nxt_tstr_str(tstr, val); + return; + } + + if (nxt_slow_path(query->failed)) { + return; + } + + ret = nxt_var_interpreter(task, query->cache, tstr->var, val, query->ctx, + tstr->flags & NXT_TSTR_LOGGING); + if (nxt_slow_path(ret != NXT_OK)) { + query->failed = 1; + return; + } + + if (tstr->flags & NXT_TSTR_STRZ) { + val->length--; + } + +#if (NXT_DEBUG) + nxt_str_t str; + + nxt_tstr_str(tstr, &str); + + nxt_debug(task, "tstr: \"%V\" -> \"%V\"", &str, val); +#endif +} + + +void +nxt_tstr_query_resolve(nxt_task_t *task, nxt_tstr_query_t *query, void *data, + nxt_work_handler_t ready, nxt_work_handler_t error) +{ + query->data = data; + query->ready = ready; + query->error = error; + + if (query->waiting == 0) { + nxt_work_queue_add(&task->thread->engine->fast_work_queue, + query->failed ? query->error : query->ready, + task, query->ctx, query->data); + } +} + + +void +nxt_tstr_query_handle(nxt_task_t *task, nxt_tstr_query_t *query, + nxt_bool_t failed) +{ + query->failed |= failed; + + if (--query->waiting == 0) { + nxt_work_queue_add(&task->thread->engine->fast_work_queue, + query->failed ? query->error : query->ready, + task, query->ctx, query->data); + } +} diff --git a/src/nxt_tstr.h b/src/nxt_tstr.h new file mode 100644 index 00000000..692b9d28 --- /dev/null +++ b/src/nxt_tstr.h @@ -0,0 +1,45 @@ + +/* + * Copyright (C) NGINX, Inc. + */ + +#ifndef _NXT_TSTR_H_INCLUDED_ +#define _NXT_TSTR_H_INCLUDED_ + + +typedef struct nxt_tstr_s nxt_tstr_t; +typedef struct nxt_tstr_query_s nxt_tstr_query_t; + + +typedef struct { + nxt_mp_t *pool; + nxt_array_t *var_fields; +} nxt_tstr_state_t; + + +typedef enum { + NXT_TSTR_STRZ = 1 << 0, + NXT_TSTR_LOGGING = 1 << 1, +} nxt_tstr_flags_t; + + +nxt_tstr_state_t *nxt_tstr_state_new(nxt_mp_t *mp); +nxt_tstr_t *nxt_tstr_compile(nxt_tstr_state_t *state, nxt_str_t *str, + nxt_tstr_flags_t flags); +nxt_int_t nxt_tstr_test(nxt_tstr_state_t *state, nxt_str_t *str, u_char *error); + +nxt_bool_t nxt_tstr_is_const(nxt_tstr_t *tstr); +void nxt_tstr_str(nxt_tstr_t *tstr, nxt_str_t *str); + +nxt_int_t nxt_tstr_query_init(nxt_tstr_query_t **query_p, + nxt_tstr_state_t *state, nxt_var_cache_t *cache, void *ctx, + nxt_mp_t *mp); +void nxt_tstr_query(nxt_task_t *task, nxt_tstr_query_t *query, nxt_tstr_t *tstr, + nxt_str_t *val); +void nxt_tstr_query_resolve(nxt_task_t *task, nxt_tstr_query_t *query, + void *data, nxt_work_handler_t ready, nxt_work_handler_t error); +void nxt_tstr_query_handle(nxt_task_t *task, nxt_tstr_query_t *query, + nxt_bool_t failed); + + +#endif /* _NXT_TSTR_H_INCLUDED_ */ diff --git a/src/nxt_var.c b/src/nxt_var.c index c4133544..e113969f 100644 --- a/src/nxt_var.c +++ b/src/nxt_var.c @@ -9,7 +9,6 @@ struct nxt_var_s { size_t length; nxt_uint_t vars; - nxt_var_flags_t flags; u_char data[]; /* @@ -29,8 +28,7 @@ typedef struct { struct nxt_var_query_s { nxt_mp_t *pool; - nxt_lvlhsh_t cache; - nxt_str_t *spare; + nxt_var_cache_t cache; nxt_uint_t waiting; nxt_uint_t failed; /* 1 bit */ @@ -58,8 +56,8 @@ static nxt_var_field_t *nxt_var_field_add(nxt_array_t *fields, nxt_str_t *name, uint32_t hash); static nxt_int_t nxt_var_cache_test(nxt_lvlhsh_query_t *lhq, void *data); -static nxt_str_t *nxt_var_cache_value(nxt_task_t *task, nxt_var_query_t *query, - uint32_t index); +static nxt_str_t *nxt_var_cache_value(nxt_task_t *task, nxt_var_cache_t *cache, + uint32_t index, void *ctx); static u_char *nxt_var_next_part(u_char *start, u_char *end, nxt_str_t *part); @@ -232,21 +230,22 @@ nxt_var_cache_test(nxt_lvlhsh_query_t *lhq, void *data) static nxt_str_t * -nxt_var_cache_value(nxt_task_t *task, nxt_var_query_t *query, uint32_t index) +nxt_var_cache_value(nxt_task_t *task, nxt_var_cache_t *cache, uint32_t index, + void *ctx) { nxt_int_t ret; nxt_str_t *value; nxt_lvlhsh_query_t lhq; - value = query->spare; + value = cache->spare; if (value == NULL) { - value = nxt_mp_zget(query->pool, sizeof(nxt_str_t)); + value = nxt_mp_zget(cache->pool, sizeof(nxt_str_t)); if (nxt_slow_path(value == NULL)) { return NULL; } - query->spare = value; + cache->spare = value; } lhq.key_hash = nxt_murmur_hash2_uint32(&index); @@ -255,21 +254,20 @@ nxt_var_cache_value(nxt_task_t *task, nxt_var_query_t *query, uint32_t index) lhq.key.start = (u_char *) &index; lhq.value = value; lhq.proto = &nxt_var_cache_proto; - lhq.pool = query->pool; + lhq.pool = cache->pool; - ret = nxt_lvlhsh_insert(&query->cache, &lhq); + ret = nxt_lvlhsh_insert(&cache->hash, &lhq); if (nxt_slow_path(ret == NXT_ERROR)) { return NULL; } if (ret == NXT_OK) { - ret = nxt_var_index[index >> 16](task, value, query->ctx, - index & 0xffff); + ret = nxt_var_index[index >> 16](task, value, ctx, index & 0xffff); if (nxt_slow_path(ret != NXT_OK)) { return NULL; } - query->spare = NULL; + cache->spare = NULL; } return lhq.value; @@ -329,21 +327,17 @@ nxt_var_index_init(void) nxt_var_t * -nxt_var_compile(nxt_str_t *str, nxt_mp_t *mp, nxt_array_t *fields, - nxt_var_flags_t flags) +nxt_var_compile(nxt_str_t *str, nxt_mp_t *mp, nxt_array_t *fields) { u_char *p, *end, *next, *src; size_t size; uint32_t index; - nxt_bool_t strz; nxt_var_t *var; nxt_str_t part; nxt_uint_t n; nxt_var_sub_t *subs; nxt_var_decl_t *decl; - strz = (flags & NXT_VAR_STRZ) != 0; - n = 0; p = str->start; @@ -362,24 +356,19 @@ nxt_var_compile(nxt_str_t *str, nxt_mp_t *mp, nxt_array_t *fields, size = sizeof(nxt_var_t) + n * sizeof(nxt_var_sub_t) + str->length; - var = nxt_mp_get(mp, size + strz); + var = nxt_mp_get(mp, size); if (nxt_slow_path(var == NULL)) { return NULL; } var->length = str->length; var->vars = n; - var->flags = flags; subs = nxt_var_subs(var); src = nxt_var_raw_start(var); nxt_memcpy(src, str->start, str->length); - if (strz) { - src[str->length] = '\0'; - } - n = 0; p = str->start; @@ -514,84 +503,33 @@ nxt_var_next_part(u_char *start, u_char *end, nxt_str_t *part) } -inline void -nxt_var_raw(nxt_var_t *var, nxt_str_t *str) -{ - str->length = var->length; - str->start = nxt_var_raw_start(var); -} - - -inline nxt_bool_t -nxt_var_is_const(nxt_var_t *var) -{ - return (var->vars == 0); -} - - nxt_int_t -nxt_var_query_init(nxt_var_query_t **query_p, void *ctx, nxt_mp_t *mp) -{ - nxt_var_query_t *query; - - query = *query_p; - - if (*query_p == NULL) { - query = nxt_mp_zget(mp, sizeof(nxt_var_query_t)); - if (nxt_slow_path(query == NULL)) { - return NXT_ERROR; - } - } - - query->pool = mp; - query->ctx = ctx; - - *query_p = query; - - return NXT_OK; -} - - -void -nxt_var_query(nxt_task_t *task, nxt_var_query_t *query, nxt_var_t *var, - nxt_str_t *str) +nxt_var_interpreter(nxt_task_t *task, nxt_var_cache_t *cache, nxt_var_t *var, + nxt_str_t *str, void *ctx, nxt_bool_t logging) { u_char *p, *src; size_t length, last, next; nxt_str_t *value, **part; nxt_uint_t i; - nxt_bool_t strz, logging; nxt_array_t parts; nxt_var_sub_t *subs; - if (nxt_var_is_const(var)) { - nxt_var_raw(var, str); - return; - } - - if (nxt_slow_path(query->failed)) { - return; - } - nxt_memzero(&parts, sizeof(nxt_array_t)); - nxt_array_init(&parts, query->pool, sizeof(nxt_str_t *)); - - strz = (var->flags & NXT_VAR_STRZ) != 0; - logging = (var->flags & NXT_VAR_LOGGING) != 0; + nxt_array_init(&parts, cache->pool, sizeof(nxt_str_t *)); subs = nxt_var_subs(var); length = var->length; for (i = 0; i < var->vars; i++) { - value = nxt_var_cache_value(task, query, subs[i].index); + value = nxt_var_cache_value(task, cache, subs[i].index, ctx); if (nxt_slow_path(value == NULL)) { - goto fail; + return NXT_ERROR; } part = nxt_array_add(&parts); if (nxt_slow_path(part == NULL)) { - goto fail; + return NXT_ERROR; } *part = value; @@ -603,9 +541,9 @@ nxt_var_query(nxt_task_t *task, nxt_var_query_t *query, nxt_var_t *var, } } - p = nxt_mp_nget(query->pool, length + strz); + p = nxt_mp_nget(cache->pool, length); if (nxt_slow_path(p == NULL)) { - goto fail; + return NXT_ERROR; } str->length = length; @@ -636,45 +574,5 @@ nxt_var_query(nxt_task_t *task, nxt_var_query_t *query, nxt_var_t *var, p = nxt_cpymem(p, &src[last], var->length - last); } - if (strz) { - *p = '\0'; - } - - nxt_debug(task, "var: \"%*s\" -> \"%V\"", length, src, str); - - return; - -fail: - - query->failed = 1; -} - - -void -nxt_var_query_resolve(nxt_task_t *task, nxt_var_query_t *query, void *data, - nxt_work_handler_t ready, nxt_work_handler_t error) -{ - query->data = data; - query->ready = ready; - query->error = error; - - if (query->waiting == 0) { - nxt_work_queue_add(&task->thread->engine->fast_work_queue, - query->failed ? query->error : query->ready, - task, query->ctx, query->data); - } -} - - -void -nxt_var_query_handle(nxt_task_t *task, nxt_var_query_t *query, - nxt_bool_t failed) -{ - query->failed |= failed; - - if (--query->waiting == 0) { - nxt_work_queue_add(&task->thread->engine->fast_work_queue, - query->failed ? query->error : query->ready, - task, query->ctx, query->data); - } + return NXT_OK; } diff --git a/src/nxt_var.h b/src/nxt_var.h index 34c8857a..2c6f13bd 100644 --- a/src/nxt_var.h +++ b/src/nxt_var.h @@ -32,10 +32,11 @@ typedef struct { } nxt_var_field_t; -typedef enum { - NXT_VAR_STRZ = 1 << 0, - NXT_VAR_LOGGING = 1 << 1, -} nxt_var_flags_t; +typedef struct { + nxt_mp_t *pool; + nxt_lvlhsh_t hash; + nxt_str_t *spare; +} nxt_var_cache_t; nxt_inline nxt_bool_t @@ -50,21 +51,11 @@ nxt_int_t nxt_var_index_init(void); nxt_var_field_t *nxt_var_field_get(nxt_array_t *fields, uint16_t index); -nxt_var_t *nxt_var_compile(nxt_str_t *str, nxt_mp_t *mp, nxt_array_t *fields, - nxt_var_flags_t flags); +nxt_var_t *nxt_var_compile(nxt_str_t *str, nxt_mp_t *mp, nxt_array_t *fields); nxt_int_t nxt_var_test(nxt_str_t *str, nxt_array_t *fields, u_char *error); -nxt_bool_t nxt_var_is_const(nxt_var_t *var); -void nxt_var_raw(nxt_var_t *var, nxt_str_t *str); - -nxt_int_t nxt_var_query_init(nxt_var_query_t **query_p, void *ctx, - nxt_mp_t *mp); -void nxt_var_query(nxt_task_t *task, nxt_var_query_t *query, - nxt_var_t *var, nxt_str_t *str); -void nxt_var_query_resolve(nxt_task_t *task, nxt_var_query_t *query, void *data, - nxt_work_handler_t ready, nxt_work_handler_t error); -void nxt_var_query_handle(nxt_task_t *task, nxt_var_query_t *query, - nxt_bool_t failed); +nxt_int_t nxt_var_interpreter(nxt_task_t *task, nxt_var_cache_t *cache, + nxt_var_t *var, nxt_str_t *str, void *ctx, nxt_bool_t logging); #endif /* _NXT_VAR_H_INCLUDED_ */ -- cgit From 4d6d146e920667a8afeacd355e4fb6a94387066e Mon Sep 17 00:00:00 2001 From: Zhidao HONG Date: Sun, 20 Nov 2022 23:16:51 +0800 Subject: Basic njs support. --- auto/help | 2 + auto/njs | 41 ++++++++ auto/options | 4 + auto/sources | 4 + auto/summary | 1 + configure | 4 + docs/changes.xml | 6 ++ src/nxt_conf_validation.c | 33 ++++--- src/nxt_http.h | 2 +- src/nxt_http_request.c | 2 +- src/nxt_http_return.c | 2 +- src/nxt_http_route.c | 2 +- src/nxt_http_static.c | 2 +- src/nxt_js.c | 230 ++++++++++++++++++++++++++++++++++++++++++++ src/nxt_js.h | 34 +++++++ src/nxt_router.c | 7 +- src/nxt_router_access_log.c | 2 +- src/nxt_tstr.c | 128 ++++++++++++++++++++---- src/nxt_tstr.h | 38 +++++++- src/nxt_var.h | 7 -- 20 files changed, 507 insertions(+), 44 deletions(-) create mode 100644 auto/njs create mode 100644 src/nxt_js.c create mode 100644 src/nxt_js.h diff --git a/auto/help b/auto/help index e2b81bc7..a3884679 100644 --- a/auto/help +++ b/auto/help @@ -41,6 +41,8 @@ cat << END --openssl enable OpenSSL library usage + --njs enable NJS library usage + --debug enable debug logging diff --git a/auto/njs b/auto/njs new file mode 100644 index 00000000..72304793 --- /dev/null +++ b/auto/njs @@ -0,0 +1,41 @@ + +# Copyright (C) NGINX, Inc. + + +nxt_found=no +NXT_HAVE_NJS=NO + +NXT_NJS_CFLAGS= +NXT_NJS_AUX_CFLAGS= +NXT_NJS_LIBS="-lnjs" +NXT_NJS_AUX_LIBS="$NXT_LIBM $NXT_LIB_AUX_LIBS" + +nxt_feature="NJS" +nxt_feature_name=NXT_HAVE_NJS +nxt_feature_run=no +nxt_feature_incs="$NXT_NJS_CFLAGS $NXT_NJS_AUX_CFLAGS" +nxt_feature_libs="$NXT_NJS_LIBS $NXT_NJS_AUX_LIBS" +nxt_feature_test="#include + + int main(void) { + njs_vm_t *vm; + njs_vm_opt_t opts; + + njs_vm_opt_init(&opts); + + vm = njs_vm_create(&opts); + if (vm == NULL) + return 1; + return 0; + }" +. auto/feature + +if [ $nxt_found = no ]; then + $echo + $echo $0: error: no NJS library found. + $echo + exit 1; +fi + +NXT_LIB_AUX_CFLAGS="$NXT_LIB_AUX_CFLAGS $NXT_NJS_CFLAGS" +NXT_LIB_AUX_LIBS="$NXT_NJS_LIBS $NXT_LIB_AUX_LIBS" diff --git a/auto/options b/auto/options index 572d8a9b..abcf531d 100644 --- a/auto/options +++ b/auto/options @@ -28,6 +28,8 @@ NXT_GNUTLS=NO NXT_CYASSL=NO NXT_POLARSSL=NO +NXT_NJS=NO + NXT_TEST_BUILD_EPOLL=NO NXT_TEST_BUILD_EVENTPORT=NO NXT_TEST_BUILD_DEVPOLL=NO @@ -85,6 +87,8 @@ do --cyassl) NXT_CYASSL=YES ;; --polarssl) NXT_POLARSSL=YES ;; + --njs) NXT_NJS=YES ;; + --test-build-epoll) NXT_TEST_BUILD_EPOLL=YES ;; --test-build-eventport) NXT_TEST_BUILD_EVENTPORT=YES ;; --test-build-devpoll) NXT_TEST_BUILD_DEVPOLL=YES ;; diff --git a/auto/sources b/auto/sources index 7d3f62cf..9f9a27f7 100644 --- a/auto/sources +++ b/auto/sources @@ -135,6 +135,10 @@ NXT_LIB_POLARSSL_SRCS="src/nxt_polarssl.c" NXT_LIB_PCRE_SRCS="src/nxt_pcre.c" NXT_LIB_PCRE2_SRCS="src/nxt_pcre2.c" +if [ "$NXT_NJS" != "NO" ]; then + NXT_LIB_SRCS="$NXT_LIB_SRCS src/nxt_js.c" +fi + NXT_LIB_EPOLL_SRCS="src/nxt_epoll_engine.c" NXT_LIB_KQUEUE_SRCS="src/nxt_kqueue_engine.c" NXT_LIB_EVENTPORT_SRCS="src/nxt_eventport_engine.c" diff --git a/auto/summary b/auto/summary index 79e7ce34..c8a49d08 100644 --- a/auto/summary +++ b/auto/summary @@ -28,6 +28,7 @@ Unit configuration summary: Unix domain sockets support: $NXT_UNIX_DOMAIN TLS support: ............... $NXT_OPENSSL Regex support: ............. $NXT_REGEX + NJS support: ............... $NXT_NJS process isolation: ......... $NXT_ISOLATION diff --git a/configure b/configure index ea86e18e..c21973eb 100755 --- a/configure +++ b/configure @@ -171,5 +171,9 @@ NXT_LIB_AUX_LIBS="$NXT_OPENSSL_LIBS $NXT_GNUTLS_LIBS \\ $NXT_CYASSL_LIBS $NXT_POLARSSL_LIBS \\ $NXT_PCRE_LIB" +if [ $NXT_NJS != NO ]; then + . auto/njs +fi + . auto/make . auto/summary diff --git a/docs/changes.xml b/docs/changes.xml index 014f243d..0c52c628 100644 --- a/docs/changes.xml +++ b/docs/changes.xml @@ -43,6 +43,12 @@ prefer system crypto policy, instead of hardcoding a default. + + +njs support with the basic syntax of JS template literals. + + + compatibility with OpenSSL 3. diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index 09b3be55..e650b44d 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -34,7 +34,7 @@ typedef enum { typedef enum { NXT_CONF_VLDT_REQUIRED = 1 << 0, - NXT_CONF_VLDT_VAR = 1 << 1, + NXT_CONF_VLDT_TSTR = 1 << 1, } nxt_conf_vldt_flags_t; @@ -367,7 +367,7 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_listener_members[] = { .name = nxt_string("pass"), .type = NXT_CONF_VLDT_STRING, .validator = nxt_conf_vldt_pass, - .flags = NXT_CONF_VLDT_VAR, + .flags = NXT_CONF_VLDT_TSTR, }, { .name = nxt_string("application"), .type = NXT_CONF_VLDT_STRING, @@ -652,7 +652,7 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_pass_action_members[] = { .name = nxt_string("pass"), .type = NXT_CONF_VLDT_STRING, .validator = nxt_conf_vldt_pass, - .flags = NXT_CONF_VLDT_VAR, + .flags = NXT_CONF_VLDT_TSTR, }, NXT_CONF_VLDT_END @@ -667,7 +667,7 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_return_action_members[] = { }, { .name = nxt_string("location"), .type = NXT_CONF_VLDT_STRING, - .flags = NXT_CONF_VLDT_VAR, + .flags = NXT_CONF_VLDT_TSTR, }, NXT_CONF_VLDT_END @@ -697,7 +697,7 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_share_action_members[] = { .validator = nxt_conf_vldt_unsupported, .u.string = "chroot", #endif - .flags = NXT_CONF_VLDT_VAR, + .flags = NXT_CONF_VLDT_TSTR, }, { .name = nxt_string("follow_symlinks"), .type = NXT_CONF_VLDT_BOOLEAN, @@ -1226,8 +1226,9 @@ nxt_int_t nxt_conf_validate(nxt_conf_validation_t *vldt) { nxt_int_t ret; + u_char error[NXT_MAX_ERROR_STR]; - vldt->tstr_state = nxt_tstr_state_new(vldt->pool); + vldt->tstr_state = nxt_tstr_state_new(vldt->pool, 1); if (nxt_slow_path(vldt->tstr_state == NULL)) { return NXT_ERROR; } @@ -1237,7 +1238,17 @@ nxt_conf_validate(nxt_conf_validation_t *vldt) return ret; } - return nxt_conf_vldt_object(vldt, vldt->conf, nxt_conf_vldt_root_members); + ret = nxt_conf_vldt_object(vldt, vldt->conf, nxt_conf_vldt_root_members); + if (ret != NXT_OK) { + return ret; + } + + ret = nxt_tstr_state_done(vldt->tstr_state, error); + if (ret != NXT_OK) { + return nxt_conf_vldt_error(vldt, "%s", error); + } + + return NXT_OK; } @@ -1721,7 +1732,7 @@ nxt_conf_vldt_share_element(nxt_conf_validation_t *vldt, nxt_conf_get_string(value, &str); - if (nxt_is_var(&str)) { + if (nxt_is_tstr(&str)) { return nxt_conf_vldt_var(vldt, &share, &str); } @@ -2501,12 +2512,12 @@ nxt_conf_vldt_object(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, continue; } - if (vals->flags & NXT_CONF_VLDT_VAR + if (vals->flags & NXT_CONF_VLDT_TSTR && nxt_conf_type(member) == NXT_CONF_STRING) { nxt_conf_get_string(member, &var); - if (nxt_is_var(&var)) { + if (nxt_is_tstr(&var)) { ret = nxt_conf_vldt_var(vldt, &name, &var); if (ret != NXT_OK) { return ret; @@ -3147,7 +3158,7 @@ nxt_conf_vldt_access_log(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, "The \"path\" string must not be empty."); } - if (nxt_is_var(&conf.format)) { + if (nxt_is_tstr(&conf.format)) { return nxt_conf_vldt_var(vldt, &format_str, &conf.format); } diff --git a/src/nxt_http.h b/src/nxt_http.h index e29fe60a..a8725d9f 100644 --- a/src/nxt_http.h +++ b/src/nxt_http.h @@ -170,7 +170,7 @@ struct nxt_http_request_s { void *timer_data; nxt_tstr_query_t *tstr_query; - nxt_var_cache_t var_cache; + nxt_tstr_cache_t tstr_cache; void *req_rpc_data; diff --git a/src/nxt_http_request.c b/src/nxt_http_request.c index 84a67415..73ffd2f0 100644 --- a/src/nxt_http_request.c +++ b/src/nxt_http_request.c @@ -282,7 +282,7 @@ nxt_http_request_create(nxt_task_t *task) task->thread->engine->requests_cnt++; - r->var_cache.pool = mp; + r->tstr_cache.var.pool = mp; return r; diff --git a/src/nxt_http_return.c b/src/nxt_http_return.c index 63c7e06f..b50e4ad0 100644 --- a/src/nxt_http_return.c +++ b/src/nxt_http_return.c @@ -126,7 +126,7 @@ nxt_http_return(nxt_task_t *task, nxt_http_request_t *r, rtcf = r->conf->socket_conf->router_conf; ret = nxt_tstr_query_init(&r->tstr_query, rtcf->tstr_state, - &r->var_cache, r, r->mem_pool); + &r->tstr_cache, r, r->mem_pool); if (nxt_slow_path(ret != NXT_OK)) { goto fail; } diff --git a/src/nxt_http_route.c b/src/nxt_http_route.c index 77a59e9c..7081ff7e 100644 --- a/src/nxt_http_route.c +++ b/src/nxt_http_route.c @@ -1306,7 +1306,7 @@ nxt_http_pass_var(nxt_task_t *task, nxt_http_request_t *r, rtcf = r->conf->socket_conf->router_conf; - ret = nxt_tstr_query_init(&r->tstr_query, rtcf->tstr_state, &r->var_cache, + ret = nxt_tstr_query_init(&r->tstr_query, rtcf->tstr_state, &r->tstr_cache, r, r->mem_pool); if (nxt_slow_path(ret != NXT_OK)) { goto fail; diff --git a/src/nxt_http_static.c b/src/nxt_http_static.c index 7143ecd3..68174b9d 100644 --- a/src/nxt_http_static.c +++ b/src/nxt_http_static.c @@ -276,7 +276,7 @@ nxt_http_static_iterate(nxt_task_t *task, nxt_http_request_t *r, rtcf = r->conf->socket_conf->router_conf; ret = nxt_tstr_query_init(&r->tstr_query, rtcf->tstr_state, - &r->var_cache, r, r->mem_pool); + &r->tstr_cache, r, r->mem_pool); if (nxt_slow_path(ret != NXT_OK)) { nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR); return; diff --git a/src/nxt_js.c b/src/nxt_js.c new file mode 100644 index 00000000..52596fe6 --- /dev/null +++ b/src/nxt_js.c @@ -0,0 +1,230 @@ + +/* + * Copyright (C) NGINX, Inc. + */ + +#include + + +struct nxt_js_s { + uint32_t index; + njs_vm_t *vm; +}; + + +struct nxt_js_conf_s { + nxt_mp_t *pool; + njs_vm_t *vm; + nxt_array_t *funcs; +}; + + +nxt_js_conf_t * +nxt_js_conf_new(nxt_mp_t *mp) +{ + njs_vm_opt_t opts; + nxt_js_conf_t *jcf; + + jcf = nxt_mp_zget(mp, sizeof(nxt_js_conf_t)); + if (nxt_slow_path(jcf == NULL)) { + return NULL; + } + + jcf->pool = mp; + + njs_vm_opt_init(&opts); + + jcf->vm = njs_vm_create(&opts); + if (nxt_slow_path(jcf->vm == NULL)) { + return NULL; + } + + jcf->funcs = nxt_array_create(mp, 4, sizeof(nxt_str_t)); + if (nxt_slow_path(jcf->funcs == NULL)) { + return NULL; + } + + return jcf; +} + + +nxt_js_t * +nxt_js_add_tpl(nxt_js_conf_t *jcf, nxt_str_t *str, nxt_bool_t strz) +{ + size_t size; + u_char *p, *start; + nxt_js_t *js; + nxt_str_t *func; + + static nxt_str_t func_str = nxt_string("function() {" + " return "); + + /* + * Appending a terminating null character if strz is true. + */ + static nxt_str_t strz_str = nxt_string(" + '\\x00'"); + + size = func_str.length + str->length + 1; + + if (strz) { + size += strz_str.length; + } + + start = nxt_mp_nget(jcf->pool, size); + if (nxt_slow_path(start == NULL)) { + return NULL; + } + + p = start; + + p = nxt_cpymem(p, func_str.start, func_str.length); + p = nxt_cpymem(p, str->start, str->length); + + if (strz) { + p = nxt_cpymem(p, strz_str.start, strz_str.length); + } + + *p++ = '}'; + + js = nxt_mp_get(jcf->pool, sizeof(nxt_js_t)); + if (nxt_slow_path(js == NULL)) { + return NULL; + } + + js->vm = jcf->vm; + + func = nxt_array_add(jcf->funcs); + if (nxt_slow_path(func == NULL)) { + return NULL; + } + + func->start = start; + func->length = p - start; + + js->index = jcf->funcs->nelts - 1; + + return js; +} + + +nxt_int_t +nxt_js_compile(nxt_js_conf_t *jcf) +{ + size_t size; + u_char *p, *start; + njs_int_t ret; + nxt_str_t *func; + nxt_uint_t i; + + size = 2; + func = jcf->funcs->elts; + + for (i = 0; i < jcf->funcs->nelts; i++) { + size += func[i].length + 1; + } + + start = nxt_mp_nget(jcf->pool, size); + if (nxt_slow_path(start == NULL)) { + return NXT_ERROR; + } + + p = start; + *p++ = '['; + + func = jcf->funcs->elts; + + for (i = 0; i < jcf->funcs->nelts; i++) { + p = nxt_cpymem(p, func[i].start, func[i].length); + *p++ = ','; + } + + *p++ = ']'; + + ret = njs_vm_compile(jcf->vm, &start, p); + + return (ret == NJS_OK) ? NXT_OK : NXT_ERROR; +} + + +nxt_int_t +nxt_js_test(nxt_js_conf_t *jcf, nxt_str_t *str, u_char *error) +{ + u_char *start; + nxt_str_t err; + njs_int_t ret; + njs_str_t res; + + start = nxt_mp_nget(jcf->pool, str->length); + if (nxt_slow_path(start == NULL)) { + return NXT_ERROR; + } + + nxt_memcpy(start, str->start, str->length); + + ret = njs_vm_compile(jcf->vm, &start, start + str->length); + + if (nxt_slow_path(ret != NJS_OK)) { + (void) njs_vm_retval_string(jcf->vm, &res); + + err.start = res.start; + err.length = res.length; + + nxt_sprintf(error, error + NXT_MAX_ERROR_STR, "\"%V\"%Z", &err); + + return NXT_ERROR; + } + + return NXT_OK; +} + + +nxt_int_t +nxt_js_call(nxt_task_t *task, nxt_js_cache_t *cache, nxt_js_t *js, + nxt_str_t *str, void *ctx) +{ + njs_vm_t *vm; + njs_int_t rc, ret; + njs_str_t res; + njs_value_t *array, *value; + njs_function_t *func; + njs_opaque_value_t opaque_value; + + vm = cache->vm; + + if (vm == NULL) { + vm = njs_vm_clone(js->vm, ctx); + if (nxt_slow_path(vm == NULL)) { + return NXT_ERROR; + } + + ret = njs_vm_start(vm); + if (ret != NJS_OK) { + return NXT_ERROR; + } + + array = njs_vm_retval(vm); + + cache->vm = vm; + cache->array = *array; + } + + value = njs_vm_array_prop(vm, &cache->array, js->index, &opaque_value); + func = njs_value_function(value); + + ret = njs_vm_call(vm, func, NULL, 0); + + rc = njs_vm_retval_string(vm, &res); + if (rc != NJS_OK) { + return NXT_ERROR; + } + + if (ret != NJS_OK) { + nxt_alert(task, "js exception: %V", &res); + return NXT_ERROR; + } + + str->length = res.length; + str->start = res.start; + + return NXT_OK; +} diff --git a/src/nxt_js.h b/src/nxt_js.h new file mode 100644 index 00000000..321041ae --- /dev/null +++ b/src/nxt_js.h @@ -0,0 +1,34 @@ + +/* + * Copyright (C) NGINX, Inc. + */ + +#ifndef _NXT_JS_H_INCLUDED_ +#define _NXT_JS_H_INCLUDED_ + +#if (NXT_HAVE_NJS) + +#include + + +typedef struct nxt_js_s nxt_js_t; +typedef struct nxt_js_conf_s nxt_js_conf_t; + + +typedef struct { + njs_vm_t *vm; + njs_value_t array; +} nxt_js_cache_t; + + +nxt_js_conf_t *nxt_js_conf_new(nxt_mp_t *mp); +nxt_js_t *nxt_js_add_tpl(nxt_js_conf_t *jcf, nxt_str_t *str, nxt_bool_t strz); +nxt_int_t nxt_js_compile(nxt_js_conf_t *jcf); +nxt_int_t nxt_js_test(nxt_js_conf_t *jcf, nxt_str_t *str, u_char *error); +nxt_int_t nxt_js_call(nxt_task_t *task, nxt_js_cache_t *cache, nxt_js_t *js, + nxt_str_t *str, void *ctx); + + +#endif /* NXT_HAVE_NJS */ + +#endif /* _NXT_JS_H_INCLUDED_ */ diff --git a/src/nxt_router.c b/src/nxt_router.c index 3ad78fa4..26aa9fb1 100644 --- a/src/nxt_router.c +++ b/src/nxt_router.c @@ -1060,7 +1060,7 @@ nxt_router_temp_conf(nxt_task_t *task) rtcf->mem_pool = mp; - rtcf->tstr_state = nxt_tstr_state_new(mp); + rtcf->tstr_state = nxt_tstr_state_new(mp, 0); if (nxt_slow_path(rtcf->tstr_state == NULL)) { goto fail; } @@ -2042,6 +2042,11 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, } } + ret = nxt_tstr_state_done(rtcf->tstr_state, NULL); + if (nxt_slow_path(ret != NXT_OK)) { + goto fail; + } + nxt_queue_add(&deleting_sockets, &router->sockets); nxt_queue_init(&router->sockets); diff --git a/src/nxt_router_access_log.c b/src/nxt_router_access_log.c index 9da366f4..ccbddb96 100644 --- a/src/nxt_router_access_log.c +++ b/src/nxt_router_access_log.c @@ -161,7 +161,7 @@ nxt_router_access_log_writer(nxt_task_t *task, nxt_http_request_t *r, rtcf = r->conf->socket_conf->router_conf; ret = nxt_tstr_query_init(&r->tstr_query, rtcf->tstr_state, - &r->var_cache, r, r->mem_pool); + &r->tstr_cache, r, r->mem_pool); if (nxt_slow_path(ret != NXT_OK)) { return; } diff --git a/src/nxt_tstr.c b/src/nxt_tstr.c index dff61952..fd01797c 100644 --- a/src/nxt_tstr.c +++ b/src/nxt_tstr.c @@ -9,12 +9,22 @@ typedef enum { NXT_TSTR_CONST = 0, NXT_TSTR_VAR, +#if (NXT_HAVE_NJS) + NXT_TSTR_JS, +#endif } nxt_tstr_type_t; struct nxt_tstr_s { nxt_str_t str; - nxt_var_t *var; + + union { + nxt_var_t *var; +#if (NXT_HAVE_NJS) + nxt_js_t *js; +#endif + } u; + nxt_tstr_flags_t flags; nxt_tstr_type_t type; }; @@ -24,7 +34,7 @@ struct nxt_tstr_query_s { nxt_mp_t *pool; nxt_tstr_state_t *state; - nxt_var_cache_t *cache; + nxt_tstr_cache_t *cache; nxt_uint_t waiting; nxt_uint_t failed; /* 1 bit */ @@ -37,8 +47,12 @@ struct nxt_tstr_query_s { }; +#define nxt_tstr_is_js(str) \ + nxt_strchr_start(str, '`') + + nxt_tstr_state_t * -nxt_tstr_state_new(nxt_mp_t *mp) +nxt_tstr_state_new(nxt_mp_t *mp, nxt_bool_t test) { nxt_tstr_state_t *state; @@ -48,12 +62,20 @@ nxt_tstr_state_new(nxt_mp_t *mp) } state->pool = mp; + state->test = test; state->var_fields = nxt_array_create(mp, 4, sizeof(nxt_var_field_t)); if (nxt_slow_path(state->var_fields == NULL)) { return NULL; } +#if (NXT_HAVE_NJS) + state->jcf = nxt_js_conf_new(mp); + if (nxt_slow_path(state->jcf == NULL)) { + return NULL; + } +#endif + return state; } @@ -88,18 +110,38 @@ nxt_tstr_compile(nxt_tstr_state_t *state, nxt_str_t *str, tstr->flags = flags; - p = nxt_memchr(str->start, '$', str->length); + if (nxt_tstr_is_js(str)) { + +#if (NXT_HAVE_NJS) + + nxt_str_t tpl; - if (p != NULL) { - tstr->type = NXT_TSTR_VAR; + tstr->type = NXT_TSTR_JS; - tstr->var = nxt_var_compile(&tstr->str, state->pool, state->var_fields); - if (nxt_slow_path(tstr->var == NULL)) { + nxt_tstr_str(tstr, &tpl); + + tstr->u.js = nxt_js_add_tpl(state->jcf, &tpl, strz); + if (nxt_slow_path(tstr->u.js == NULL)) { return NULL; } +#endif + } else { - tstr->type = NXT_TSTR_CONST; + p = memchr(str->start, '$', str->length); + + if (p != NULL) { + tstr->type = NXT_TSTR_VAR; + + tstr->u.var = nxt_var_compile(&tstr->str, state->pool, + state->var_fields); + if (nxt_slow_path(tstr->u.var == NULL)) { + return NULL; + } + + } else { + tstr->type = NXT_TSTR_CONST; + } } return tstr; @@ -109,7 +151,46 @@ nxt_tstr_compile(nxt_tstr_state_t *state, nxt_str_t *str, nxt_int_t nxt_tstr_test(nxt_tstr_state_t *state, nxt_str_t *str, u_char *error) { - return nxt_var_test(str, state->var_fields, error); + u_char *p; + + if (nxt_tstr_is_js(str)) { +#if (NXT_HAVE_NJS) + return nxt_js_test(state->jcf, str, error); + +#else + nxt_sprintf(error, error + NXT_MAX_ERROR_STR, + "Unit is built without support of njs: " + "\"--njs\" ./configure option is missing."); + return NXT_ERROR; +#endif + + } else { + p = memchr(str->start, '$', str->length); + + if (p != NULL) { + return nxt_var_test(str, state->var_fields, error); + } + } + + return NXT_OK; +} + + +nxt_int_t +nxt_tstr_state_done(nxt_tstr_state_t *state, u_char *error) +{ +#if (NXT_HAVE_NJS) + if (!state->test) { + nxt_int_t ret; + + ret = nxt_js_compile(state->jcf); + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } + } +#endif + + return NXT_OK; } @@ -133,7 +214,7 @@ nxt_tstr_str(nxt_tstr_t *tstr, nxt_str_t *str) nxt_int_t nxt_tstr_query_init(nxt_tstr_query_t **query_p, nxt_tstr_state_t *state, - nxt_var_cache_t *cache, void *ctx, nxt_mp_t *mp) + nxt_tstr_cache_t *cache, void *ctx, nxt_mp_t *mp) { nxt_tstr_query_t *query; @@ -172,11 +253,24 @@ nxt_tstr_query(nxt_task_t *task, nxt_tstr_query_t *query, nxt_tstr_t *tstr, return; } - ret = nxt_var_interpreter(task, query->cache, tstr->var, val, query->ctx, - tstr->flags & NXT_TSTR_LOGGING); - if (nxt_slow_path(ret != NXT_OK)) { - query->failed = 1; - return; + if (tstr->type == NXT_TSTR_VAR) { + ret = nxt_var_interpreter(task, &query->cache->var, tstr->u.var, val, + query->ctx, tstr->flags & NXT_TSTR_LOGGING); + + if (nxt_slow_path(ret != NXT_OK)) { + query->failed = 1; + return; + } + + } else { +#if (NXT_HAVE_NJS) + ret = nxt_js_call(task, &query->cache->js, tstr->u.js, val, query->ctx); + + if (nxt_slow_path(ret != NXT_OK)) { + query->failed = 1; + return; + } +#endif } if (tstr->flags & NXT_TSTR_STRZ) { @@ -188,7 +282,7 @@ nxt_tstr_query(nxt_task_t *task, nxt_tstr_query_t *query, nxt_tstr_t *tstr, nxt_tstr_str(tstr, &str); - nxt_debug(task, "tstr: \"%V\" -> \"%V\"", &str, val); + nxt_debug(task, "tstr query: \"%V\", result: \"%V\"", &str, val); #endif } diff --git a/src/nxt_tstr.h b/src/nxt_tstr.h index 692b9d28..0cc24292 100644 --- a/src/nxt_tstr.h +++ b/src/nxt_tstr.h @@ -7,6 +7,8 @@ #define _NXT_TSTR_H_INCLUDED_ +#include + typedef struct nxt_tstr_s nxt_tstr_t; typedef struct nxt_tstr_query_s nxt_tstr_query_t; @@ -14,25 +16,38 @@ typedef struct nxt_tstr_query_s nxt_tstr_query_t; typedef struct { nxt_mp_t *pool; nxt_array_t *var_fields; +#if (NXT_HAVE_NJS) + nxt_js_conf_t *jcf; +#endif + uint8_t test; /* 1 bit */ } nxt_tstr_state_t; +typedef struct { + nxt_var_cache_t var; +#if (NXT_HAVE_NJS) + nxt_js_cache_t js; +#endif +} nxt_tstr_cache_t; + + typedef enum { NXT_TSTR_STRZ = 1 << 0, NXT_TSTR_LOGGING = 1 << 1, } nxt_tstr_flags_t; -nxt_tstr_state_t *nxt_tstr_state_new(nxt_mp_t *mp); +nxt_tstr_state_t *nxt_tstr_state_new(nxt_mp_t *mp, nxt_bool_t test); nxt_tstr_t *nxt_tstr_compile(nxt_tstr_state_t *state, nxt_str_t *str, nxt_tstr_flags_t flags); nxt_int_t nxt_tstr_test(nxt_tstr_state_t *state, nxt_str_t *str, u_char *error); +nxt_int_t nxt_tstr_state_done(nxt_tstr_state_t *state, u_char *error); nxt_bool_t nxt_tstr_is_const(nxt_tstr_t *tstr); void nxt_tstr_str(nxt_tstr_t *tstr, nxt_str_t *str); nxt_int_t nxt_tstr_query_init(nxt_tstr_query_t **query_p, - nxt_tstr_state_t *state, nxt_var_cache_t *cache, void *ctx, + nxt_tstr_state_t *state, nxt_tstr_cache_t *cache, void *ctx, nxt_mp_t *mp); void nxt_tstr_query(nxt_task_t *task, nxt_tstr_query_t *query, nxt_tstr_t *tstr, nxt_str_t *val); @@ -42,4 +57,23 @@ void nxt_tstr_query_handle(nxt_task_t *task, nxt_tstr_query_t *query, nxt_bool_t failed); +nxt_inline nxt_bool_t +nxt_is_tstr(nxt_str_t *str) +{ + u_char *p; + + p = memchr(str->start, '`', str->length); + if (p != NULL) { + return 1; + } + + p = memchr(str->start, '$', str->length); + if (p != NULL) { + return 1; + } + + return 0; +} + + #endif /* _NXT_TSTR_H_INCLUDED_ */ diff --git a/src/nxt_var.h b/src/nxt_var.h index 2c6f13bd..4f30a002 100644 --- a/src/nxt_var.h +++ b/src/nxt_var.h @@ -39,13 +39,6 @@ typedef struct { } nxt_var_cache_t; -nxt_inline nxt_bool_t -nxt_is_var(nxt_str_t *str) -{ - return (memchr(str->start, '$', str->length) != NULL); -} - - nxt_int_t nxt_var_register(nxt_var_decl_t *decl, size_t n); nxt_int_t nxt_var_index_init(void); -- cgit From e3bbf5b3b5be384a39bbd1c42d44379b17d94185 Mon Sep 17 00:00:00 2001 From: Zhidao HONG Date: Tue, 22 Nov 2022 10:13:18 +0800 Subject: NJS: added http request prototype. --- auto/sources | 2 +- src/nxt_http_js.c | 273 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/nxt_js.c | 75 ++++++++++++++- src/nxt_js.h | 4 + src/nxt_router.c | 4 + src/nxt_runtime.h | 3 + src/nxt_var.h | 2 + 7 files changed, 359 insertions(+), 4 deletions(-) create mode 100644 src/nxt_http_js.c diff --git a/auto/sources b/auto/sources index 9f9a27f7..cebced3a 100644 --- a/auto/sources +++ b/auto/sources @@ -136,7 +136,7 @@ NXT_LIB_PCRE_SRCS="src/nxt_pcre.c" NXT_LIB_PCRE2_SRCS="src/nxt_pcre2.c" if [ "$NXT_NJS" != "NO" ]; then - NXT_LIB_SRCS="$NXT_LIB_SRCS src/nxt_js.c" + NXT_LIB_SRCS="$NXT_LIB_SRCS src/nxt_js.c src/nxt_http_js.c" fi NXT_LIB_EPOLL_SRCS="src/nxt_epoll_engine.c" diff --git a/src/nxt_http_js.c b/src/nxt_http_js.c new file mode 100644 index 00000000..5a08a309 --- /dev/null +++ b/src/nxt_http_js.c @@ -0,0 +1,273 @@ + +/* + * Copyright (C) NGINX, Inc. + */ + +#include +#include +#include + + +static njs_int_t nxt_http_js_ext_uri(njs_vm_t *vm, njs_object_prop_t *prop, + njs_value_t *value, njs_value_t *setval, njs_value_t *retval); +static njs_int_t nxt_http_js_ext_host(njs_vm_t *vm, njs_object_prop_t *prop, + njs_value_t *value, njs_value_t *setval, njs_value_t *retval); +static njs_int_t nxt_http_js_ext_remote_addr(njs_vm_t *vm, + njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, + njs_value_t *retval); +static njs_int_t nxt_http_js_ext_get_arg(njs_vm_t *vm, + njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, + njs_value_t *retval); +static njs_int_t nxt_http_js_ext_get_header(njs_vm_t *vm, + njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, + njs_value_t *retval); +static njs_int_t nxt_http_js_ext_get_cookie(njs_vm_t *vm, + njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, + njs_value_t *retval); + + +static njs_external_t nxt_http_js_proto[] = { + { + .flags = NJS_EXTERN_PROPERTY, + .name.string = njs_str("uri"), + .enumerable = 1, + .u.property = { + .handler = nxt_http_js_ext_uri, + } + }, + + { + .flags = NJS_EXTERN_PROPERTY, + .name.string = njs_str("host"), + .enumerable = 1, + .u.property = { + .handler = nxt_http_js_ext_host, + } + }, + + { + .flags = NJS_EXTERN_PROPERTY, + .name.string = njs_str("remoteAddr"), + .enumerable = 1, + .u.property = { + .handler = nxt_http_js_ext_remote_addr, + } + }, + + { + .flags = NJS_EXTERN_OBJECT, + .name.string = njs_str("args"), + .enumerable = 1, + .u.object = { + .enumerable = 1, + .prop_handler = nxt_http_js_ext_get_arg, + } + }, + + { + .flags = NJS_EXTERN_OBJECT, + .name.string = njs_str("headers"), + .enumerable = 1, + .u.object = { + .enumerable = 1, + .prop_handler = nxt_http_js_ext_get_header, + } + }, + + { + .flags = NJS_EXTERN_OBJECT, + .name.string = njs_str("cookies"), + .enumerable = 1, + .u.object = { + .enumerable = 1, + .prop_handler = nxt_http_js_ext_get_cookie, + } + }, +}; + + +void +nxt_http_register_js_proto(nxt_js_conf_t *jcf) +{ + nxt_js_set_proto(jcf, nxt_http_js_proto, njs_nitems(nxt_http_js_proto)); +} + + +static njs_int_t +nxt_http_js_ext_uri(njs_vm_t *vm, njs_object_prop_t *prop, + njs_value_t *value, njs_value_t *setval, njs_value_t *retval) +{ + nxt_http_request_t *r; + + r = njs_vm_external(vm, nxt_js_proto_id, value); + if (r == NULL) { + njs_value_undefined_set(retval); + return NJS_DECLINED; + } + + return njs_vm_value_string_set(vm, retval, r->path->start, r->path->length); +} + + +static njs_int_t +nxt_http_js_ext_host(njs_vm_t *vm, njs_object_prop_t *prop, + njs_value_t *value, njs_value_t *setval, njs_value_t *retval) +{ + nxt_http_request_t *r; + + r = njs_vm_external(vm, nxt_js_proto_id, value); + if (r == NULL) { + njs_value_undefined_set(retval); + return NJS_DECLINED; + } + + return njs_vm_value_string_set(vm, retval, r->host.start, r->host.length); +} + + +static njs_int_t +nxt_http_js_ext_remote_addr(njs_vm_t *vm, njs_object_prop_t *prop, + njs_value_t *value, njs_value_t *setval, njs_value_t *retval) +{ + nxt_http_request_t *r; + + r = njs_vm_external(vm, nxt_js_proto_id, value); + if (r == NULL) { + njs_value_undefined_set(retval); + return NJS_DECLINED; + } + + return njs_vm_value_string_set(vm, retval, + nxt_sockaddr_address(r->remote), + r->remote->address_length); +} + + +static njs_int_t +nxt_http_js_ext_get_arg(njs_vm_t *vm, njs_object_prop_t *prop, + njs_value_t *value, njs_value_t *setval, njs_value_t *retval) +{ + njs_int_t rc; + njs_str_t key; + nxt_array_t *args; + nxt_http_request_t *r; + nxt_http_name_value_t *nv, *start, *end; + + r = njs_vm_external(vm, nxt_js_proto_id, value); + if (r == NULL) { + njs_value_undefined_set(retval); + return NJS_DECLINED; + } + + rc = njs_vm_prop_name(vm, prop, &key); + if (rc != NJS_OK) { + njs_value_undefined_set(retval); + return NJS_DECLINED; + } + + args = nxt_http_arguments_parse(r); + if (nxt_slow_path(args == NULL)) { + return NJS_ERROR; + } + + start = args->elts; + end = start + args->nelts; + + for (nv = start; nv < end; nv++) { + + if (key.length == nv->name_length + && memcmp(key.start, nv->name, nv->name_length) == 0) + { + return njs_vm_value_string_set(vm, retval, nv->value, + nv->value_length); + } + } + + njs_value_undefined_set(retval); + + return NJS_DECLINED; +} + + +static njs_int_t +nxt_http_js_ext_get_header(njs_vm_t *vm, njs_object_prop_t *prop, + njs_value_t *value, njs_value_t *setval, njs_value_t *retval) +{ + njs_int_t rc; + njs_str_t key; + nxt_http_field_t *f; + nxt_http_request_t *r; + + r = njs_vm_external(vm, nxt_js_proto_id, value); + if (r == NULL) { + njs_value_undefined_set(retval); + return NJS_DECLINED; + } + + rc = njs_vm_prop_name(vm, prop, &key); + if (rc != NJS_OK) { + njs_value_undefined_set(retval); + return NJS_DECLINED; + } + + nxt_list_each(f, r->fields) { + + if (key.length == f->name_length + && memcmp(key.start, f->name, f->name_length) == 0) + { + return njs_vm_value_string_set(vm, retval, f->value, + f->value_length); + } + + } nxt_list_loop; + + njs_value_undefined_set(retval); + + return NJS_DECLINED; +} + + +static njs_int_t +nxt_http_js_ext_get_cookie(njs_vm_t *vm, njs_object_prop_t *prop, + njs_value_t *value, njs_value_t *setval, njs_value_t *retval) +{ + njs_int_t rc; + njs_str_t key; + nxt_array_t *cookies; + nxt_http_request_t *r; + nxt_http_name_value_t *nv, *start, *end; + + r = njs_vm_external(vm, nxt_js_proto_id, value); + if (r == NULL) { + njs_value_undefined_set(retval); + return NJS_DECLINED; + } + + rc = njs_vm_prop_name(vm, prop, &key); + if (rc != NJS_OK) { + njs_value_undefined_set(retval); + return NJS_DECLINED; + } + + cookies = nxt_http_cookies_parse(r); + if (nxt_slow_path(cookies == NULL)) { + return NJS_ERROR; + } + + start = cookies->elts; + end = start + cookies->nelts; + + for (nv = start; nv < end; nv++) { + + if (key.length == nv->name_length + && memcmp(key.start, nv->name, nv->name_length) == 0) + { + return njs_vm_value_string_set(vm, retval, nv->value, + nv->value_length); + } + } + + njs_value_undefined_set(retval); + + return NJS_DECLINED; +} diff --git a/src/nxt_js.c b/src/nxt_js.c index 52596fe6..aa3c4af5 100644 --- a/src/nxt_js.c +++ b/src/nxt_js.c @@ -15,10 +15,15 @@ struct nxt_js_s { struct nxt_js_conf_s { nxt_mp_t *pool; njs_vm_t *vm; + njs_uint_t protos; + njs_external_t *proto; nxt_array_t *funcs; }; +njs_int_t nxt_js_proto_id; + + nxt_js_conf_t * nxt_js_conf_new(nxt_mp_t *mp) { @@ -48,6 +53,14 @@ nxt_js_conf_new(nxt_mp_t *mp) } +void +nxt_js_set_proto(nxt_js_conf_t *jcf, njs_external_t *proto, njs_uint_t n) +{ + jcf->protos = n; + jcf->proto = proto; +} + + nxt_js_t * nxt_js_add_tpl(nxt_js_conf_t *jcf, nxt_str_t *str, nxt_bool_t strz) { @@ -56,7 +69,8 @@ nxt_js_add_tpl(nxt_js_conf_t *jcf, nxt_str_t *str, nxt_bool_t strz) nxt_js_t *js; nxt_str_t *func; - static nxt_str_t func_str = nxt_string("function() {" + static nxt_str_t func_str = nxt_string("function(uri, host, remoteAddr, " + "args, headers, cookies) {" " return "); /* @@ -140,6 +154,12 @@ nxt_js_compile(nxt_js_conf_t *jcf) *p++ = ']'; + nxt_js_proto_id = njs_vm_external_prototype(jcf->vm, jcf->proto, + jcf->protos); + if (nxt_slow_path(nxt_js_proto_id < 0)) { + return NXT_ERROR; + } + ret = njs_vm_compile(jcf->vm, &start, p); return (ret == NJS_OK) ? NXT_OK : NXT_ERROR; @@ -187,7 +207,14 @@ nxt_js_call(nxt_task_t *task, nxt_js_cache_t *cache, nxt_js_t *js, njs_str_t res; njs_value_t *array, *value; njs_function_t *func; - njs_opaque_value_t opaque_value; + njs_opaque_value_t opaque_value, arguments[6]; + + static const njs_str_t uri_str = njs_str("uri"); + static const njs_str_t host_str = njs_str("host"); + static const njs_str_t remote_addr_str = njs_str("remoteAddr"); + static const njs_str_t args_str = njs_str("args"); + static const njs_str_t headers_str = njs_str("headers"); + static const njs_str_t cookies_str = njs_str("cookies"); vm = cache->vm; @@ -211,7 +238,49 @@ nxt_js_call(nxt_task_t *task, nxt_js_cache_t *cache, nxt_js_t *js, value = njs_vm_array_prop(vm, &cache->array, js->index, &opaque_value); func = njs_value_function(value); - ret = njs_vm_call(vm, func, NULL, 0); + ret = njs_vm_external_create(vm, njs_value_arg(&opaque_value), + nxt_js_proto_id, ctx, 0); + if (nxt_slow_path(ret != NJS_OK)) { + return NXT_ERROR; + } + + value = njs_vm_object_prop(vm, njs_value_arg(&opaque_value), &uri_str, + &arguments[0]); + if (nxt_slow_path(value == NULL)) { + return NXT_ERROR; + } + + value = njs_vm_object_prop(vm, njs_value_arg(&opaque_value), &host_str, + &arguments[1]); + if (nxt_slow_path(value == NULL)) { + return NXT_ERROR; + } + + value = njs_vm_object_prop(vm, njs_value_arg(&opaque_value), + &remote_addr_str, &arguments[2]); + if (nxt_slow_path(value == NULL)) { + return NXT_ERROR; + } + + value = njs_vm_object_prop(vm, njs_value_arg(&opaque_value), &args_str, + &arguments[3]); + if (nxt_slow_path(value == NULL)) { + return NXT_ERROR; + } + + value = njs_vm_object_prop(vm, njs_value_arg(&opaque_value), &headers_str, + &arguments[4]); + if (nxt_slow_path(value == NULL)) { + return NXT_ERROR; + } + + value = njs_vm_object_prop(vm, njs_value_arg(&opaque_value), &cookies_str, + &arguments[5]); + if (nxt_slow_path(value == NULL)) { + return NXT_ERROR; + } + + ret = njs_vm_call(vm, func, njs_value_arg(&arguments), 6); rc = njs_vm_retval_string(vm, &res); if (rc != NJS_OK) { diff --git a/src/nxt_js.h b/src/nxt_js.h index 321041ae..dea43fe3 100644 --- a/src/nxt_js.h +++ b/src/nxt_js.h @@ -22,6 +22,7 @@ typedef struct { nxt_js_conf_t *nxt_js_conf_new(nxt_mp_t *mp); +void nxt_js_set_proto(nxt_js_conf_t *jcf, njs_external_t *proto, nxt_uint_t n); nxt_js_t *nxt_js_add_tpl(nxt_js_conf_t *jcf, nxt_str_t *str, nxt_bool_t strz); nxt_int_t nxt_js_compile(nxt_js_conf_t *jcf); nxt_int_t nxt_js_test(nxt_js_conf_t *jcf, nxt_str_t *str, u_char *error); @@ -29,6 +30,9 @@ nxt_int_t nxt_js_call(nxt_task_t *task, nxt_js_cache_t *cache, nxt_js_t *js, nxt_str_t *str, void *ctx); +extern njs_int_t nxt_js_proto_id; + + #endif /* NXT_HAVE_NJS */ #endif /* _NXT_JS_H_INCLUDED_ */ diff --git a/src/nxt_router.c b/src/nxt_router.c index 26aa9fb1..edc015c5 100644 --- a/src/nxt_router.c +++ b/src/nxt_router.c @@ -1065,6 +1065,10 @@ nxt_router_temp_conf(nxt_task_t *task) goto fail; } +#if (NXT_HAVE_NJS) + nxt_http_register_js_proto(rtcf->tstr_state->jcf); +#endif + tmp = nxt_mp_create(1024, 128, 256, 32); if (nxt_slow_path(tmp == NULL)) { goto fail; diff --git a/src/nxt_runtime.h b/src/nxt_runtime.h index d7fe2f38..687914f0 100644 --- a/src/nxt_runtime.h +++ b/src/nxt_runtime.h @@ -138,6 +138,9 @@ void nxt_cdecl nxt_log_time_handler(nxt_uint_t level, nxt_log_t *log, void nxt_stream_connection_init(nxt_task_t *task, void *obj, void *data); nxt_int_t nxt_http_register_variables(void); +#if (NXT_HAVE_NJS) +void nxt_http_register_js_proto(nxt_js_conf_t *jcf); +#endif #define nxt_runtime_process_each(rt, process) \ diff --git a/src/nxt_var.h b/src/nxt_var.h index 4f30a002..ab25800d 100644 --- a/src/nxt_var.h +++ b/src/nxt_var.h @@ -49,6 +49,8 @@ nxt_int_t nxt_var_test(nxt_str_t *str, nxt_array_t *fields, u_char *error); nxt_int_t nxt_var_interpreter(nxt_task_t *task, nxt_var_cache_t *cache, nxt_var_t *var, nxt_str_t *str, void *ctx, nxt_bool_t logging); +nxt_str_t *nxt_var_get(nxt_task_t *task, nxt_var_cache_t *cache, + nxt_str_t *name, void *ctx); #endif /* _NXT_VAR_H_INCLUDED_ */ -- cgit From 190691ade82f5126271b374dd5b8d0cb57f9473a Mon Sep 17 00:00:00 2001 From: Andrei Zeliankou Date: Tue, 29 Nov 2022 01:02:08 +0000 Subject: Tests: NJS. --- test/conftest.py | 2 ++ test/test_njs.py | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++ test/unit/check/njs.py | 6 ++++ test/unit/http.py | 4 ++- 4 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 test/test_njs.py create mode 100644 test/unit/check/njs.py diff --git a/test/conftest.py b/test/conftest.py index bf2951db..759f11bd 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -17,6 +17,7 @@ import pytest from unit.check.chroot import check_chroot from unit.check.go import check_go from unit.check.isolation import check_isolation +from unit.check.njs import check_njs from unit.check.node import check_node from unit.check.regex import check_regex from unit.check.tls import check_openssl @@ -206,6 +207,7 @@ def pytest_sessionstart(session): # discover modules from check option.available['modules']['go'] = check_go() + option.available['modules']['njs'] = check_njs(output_version) option.available['modules']['node'] = check_node(option.current_dir) option.available['modules']['openssl'] = check_openssl(output_version) option.available['modules']['regex'] = check_regex(output_version) diff --git a/test/test_njs.py b/test/test_njs.py new file mode 100644 index 00000000..2cbded5b --- /dev/null +++ b/test/test_njs.py @@ -0,0 +1,90 @@ +import os + +from unit.applications.proto import TestApplicationProto +from unit.option import option + + +class TestNJS(TestApplicationProto): + prerequisites = {'modules': {'njs': 'any'}} + + def setup_method(self): + os.makedirs(option.temp_dir + '/assets') + open(option.temp_dir + '/assets/index.html', 'a') + open(option.temp_dir + '/assets/localhost', 'a') + open(option.temp_dir + '/assets/`string`', 'a') + open(option.temp_dir + '/assets/`backtick', 'a') + open(option.temp_dir + '/assets/l1\nl2', 'a') + open(option.temp_dir + '/assets/127.0.0.1', 'a') + + assert 'success' in self.conf( + { + "listeners": {"*:7080": {"pass": "routes"}}, + "routes": [ + {"action": {"share": option.temp_dir + "/assets$uri"}} + ], + } + ) + + def set_share(self, share): + assert 'success' in self.conf(share, 'routes/0/action/share') + + def test_njs_template_string(self, temp_dir): + self.set_share('"`' + temp_dir + '/assets/index.html`"') + assert self.get()['status'] == 200, 'string' + + self.set_share('"' + temp_dir + '/assets/`string`"') + assert self.get()['status'] == 200, 'string 2' + + self.set_share('"`' + temp_dir + '/assets/\\\\`backtick`"') + assert self.get()['status'] == 200, 'escape' + + self.set_share('"`' + temp_dir + '/assets/l1\\nl2`"') + assert self.get()['status'] == 200, 'multiline' + + def test_njs_template_expression(self, temp_dir): + def check_expression(expression): + self.set_share(expression) + assert self.get()['status'] == 200 + + check_expression('"`' + temp_dir + '/assets${uri}`"') + check_expression('"`' + temp_dir + '/assets${uri}${host}`"') + check_expression('"`' + temp_dir + '/assets${uri + host}`"') + check_expression('"`' + temp_dir + '/assets${uri + `${host}`}`"') + + def test_njs_variables(self, temp_dir): + self.set_share('"`' + temp_dir + '/assets/${host}`"') + assert self.get()['status'] == 200, 'host' + + self.set_share('"`' + temp_dir + '/assets/${remoteAddr}`"') + assert self.get()['status'] == 200, 'remoteAddr' + + self.set_share('"`' + temp_dir + '/assets/${headers.Host}`"') + assert self.get()['status'] == 200, 'headers' + + self.set_share('"`' + temp_dir + '/assets/${cookies.foo}`"') + assert ( + self.get( + headers={'Cookie': 'foo=localhost', 'Connection': 'close'} + )['status'] + == 200 + ), 'cookies' + + self.set_share('"`' + temp_dir + '/assets/${args.foo}`"') + assert self.get(url='/?foo=localhost')['status'] == 200, 'args' + + def test_njs_invalid(self, temp_dir, skip_alert): + skip_alert(r'js exception:') + + def check_invalid(template): + assert 'error' in self.conf(template, 'routes/0/action/share') + + check_invalid('"`a"') + check_invalid('"`a``"') + check_invalid('"`a`/"') + + def check_invalid_resolve(template): + assert 'success' in self.conf(template, 'routes/0/action/share') + assert self.get()['status'] == 500 + + check_invalid_resolve('"`${a}`"') + check_invalid_resolve('"`${uri.a.a}`"') diff --git a/test/unit/check/njs.py b/test/unit/check/njs.py new file mode 100644 index 00000000..433473a1 --- /dev/null +++ b/test/unit/check/njs.py @@ -0,0 +1,6 @@ +import re + + +def check_njs(output_version): + if re.search('--njs', output_version): + return True diff --git a/test/unit/http.py b/test/unit/http.py index 144f300c..c48a720f 100644 --- a/test/unit/http.py +++ b/test/unit/http.py @@ -102,7 +102,9 @@ class TestHTTP: if 'read_buffer_size' in kwargs: recvall_kwargs['buff_size'] = kwargs['read_buffer_size'] - resp = self.recvall(sock, **recvall_kwargs).decode(encoding) + resp = self.recvall(sock, **recvall_kwargs).decode( + encoding, errors='ignore' + ) else: return sock -- cgit From dfededabdc47caa548c7d7bd62133e0d4df2f0fa Mon Sep 17 00:00:00 2001 From: Liam Crilly Date: Sat, 3 Dec 2022 18:11:14 +0000 Subject: Added tools/README.md. --- tools/README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 tools/README.md diff --git a/tools/README.md b/tools/README.md new file mode 100644 index 00000000..7dbf0781 --- /dev/null +++ b/tools/README.md @@ -0,0 +1,16 @@ +# Unit Tools + +This directory contains useful tools for installing, configuring, and +managing NGINX Unit. They may not be part of official packages and +should be considered experimental. + +--- + +## setup-unit + +### A script that simplifies installing and configuring an NGINX Unit server for first-time users + +* `setup-unit repo-config` configures your package manager with the NGINX +Unit repository for later installation. +* `setup-unit welcome` creates an initial configuration to serve a welcome +web page with NGINX Unit. -- cgit From 491d0f700f5690eba0f1fcf2124f3a37ef73eb1a Mon Sep 17 00:00:00 2001 From: Andrew Clayton Date: Thu, 17 Nov 2022 21:56:58 +0000 Subject: Python: Added support for Python 3.11. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Python 3.8 added a new Python initialisation configuration API[0]. Python 3.11 marked the old API as deprecated resulting in the following compiler warnings which we treat as errors, failing the build src/python/nxt_python.c: In function ‘nxt_python_start’: src/python/nxt_python.c:130:13: error: ‘Py_SetProgramName’ is deprecated [-Werror=deprecated-declarations] 130 | Py_SetProgramName(nxt_py_home); | ^~~~~~~~~~~~~~~~~ In file included from /opt/python-3.11/include/python3.11/Python.h:94, from src/python/nxt_python.c:7: /opt/python-3.11/include/python3.11/pylifecycle.h:37:38: note: declared here 37 | Py_DEPRECATED(3.11) PyAPI_FUNC(void) Py_SetProgramName(const wchar_t *); | ^~~~~~~~~~~~~~~~~ src/python/nxt_python.c:134:13: error: ‘Py_SetPythonHome’ is deprecated [-Werror=deprecated-declarations] 134 | Py_SetPythonHome(nxt_py_home); | ^~~~~~~~~~~~~~~~ /opt/python-3.11/include/python3.11/pylifecycle.h:40:38: note: declared here 40 | Py_DEPRECATED(3.11) PyAPI_FUNC(void) Py_SetPythonHome(const wchar_t *); | ^~~~~~~~~~~~~~~~ cc1: all warnings being treated as errors We actually have a few config scenarios: Python < 3, Python >= 3.0 < 3.8 and for Python 3 we have two configs where we select one based on virtual environment setup. Factor out the Python 3 config initialisation into its own function. We actually create two functions, one for Python 3.8+ and one for older Python 3. We pick the right function to use at build time. The new API also has error checking (where the old API doesn't) which we handle. [0]: https://peps.python.org/pep-0587/ Closes: [ Andrew: Expanded upon patch from @sandeep-gh ] Signed-off-by: Andrew Clayton --- docs/changes.xml | 6 +++++ src/python/nxt_python.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 73 insertions(+), 2 deletions(-) diff --git a/docs/changes.xml b/docs/changes.xml index 0c52c628..2dd1b472 100644 --- a/docs/changes.xml +++ b/docs/changes.xml @@ -43,6 +43,12 @@ prefer system crypto policy, instead of hardcoding a default. + + +compatibility with Python 3.11. + + + njs support with the basic syntax of JS template literals. diff --git a/src/python/nxt_python.c b/src/python/nxt_python.c index 37204051..8983815c 100644 --- a/src/python/nxt_python.c +++ b/src/python/nxt_python.c @@ -22,6 +22,10 @@ typedef struct { } nxt_py_thread_info_t; +#if PY_MAJOR_VERSION == 3 +static nxt_int_t nxt_python3_init_config(nxt_int_t pep405); +#endif + static nxt_int_t nxt_python_start(nxt_task_t *task, nxt_process_data_t *data); static nxt_int_t nxt_python_set_target(nxt_task_t *task, @@ -64,6 +68,63 @@ static nxt_py_thread_info_t *nxt_py_threads; static nxt_python_proto_t nxt_py_proto; +#if PY_VERSION_HEX >= NXT_PYTHON_VER(3, 8) + +static nxt_int_t +nxt_python3_init_config(nxt_int_t pep405) +{ + PyStatus status; + PyConfig config; + + PyConfig_InitIsolatedConfig(&config); + + if (pep405) { + status = PyConfig_SetString(&config, &config.program_name, + nxt_py_home); + if (PyStatus_Exception(status)) { + goto pyinit_exception; + } + + } else { + status =PyConfig_SetString(&config, &config.home, nxt_py_home); + if (PyStatus_Exception(status)) { + goto pyinit_exception; + } + } + + status = Py_InitializeFromConfig(&config); + if (PyStatus_Exception(status)) { + goto pyinit_exception; + } + PyConfig_Clear(&config); + + return NXT_OK; + +pyinit_exception: + + PyConfig_Clear(&config); + + return NXT_ERROR; +} + +#elif PY_MAJOR_VERSION == 3 + +static nxt_int_t +nxt_python3_init_config(nxt_int_t pep405) +{ + if (pep405) { + Py_SetProgramName(nxt_py_home); + + } else { + Py_SetPythonHome(nxt_py_home); + } + + return NXT_OK; +} + +#endif + + static nxt_int_t nxt_python_start(nxt_task_t *task, nxt_process_data_t *data) { @@ -127,11 +188,15 @@ nxt_python_start(nxt_task_t *task, nxt_process_data_t *data) if (pep405) { mbstowcs(nxt_py_home, c->home, len); mbstowcs(nxt_py_home + len, bin_python, sizeof(bin_python)); - Py_SetProgramName(nxt_py_home); } else { mbstowcs(nxt_py_home, c->home, len + 1); - Py_SetPythonHome(nxt_py_home); + } + + ret = nxt_python3_init_config(pep405); + if (nxt_slow_path(ret == NXT_ERROR)) { + nxt_alert(task, "Failed to initialise config"); + return NXT_ERROR; } #else -- cgit From d862f581db968519fb7adb38c8872d020f4f21e6 Mon Sep 17 00:00:00 2001 From: Andrei Zeliankou Date: Tue, 6 Dec 2022 14:30:13 +0000 Subject: Node.js: added "shortCircuit" option for ES modules hook. Starting from Node.js v18.6.0 return value from all hooks must have "shortCircuit: true" option specified. For more information see: https://github.com/nodejs/node/commit/10bcad5c6e --- docs/changes.xml | 6 ++++++ src/nodejs/unit-http/loader.mjs | 6 ++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/changes.xml b/docs/changes.xml index 2dd1b472..9aaec9e5 100644 --- a/docs/changes.xml +++ b/docs/changes.xml @@ -67,6 +67,12 @@ compatibility with PHP 8.2. + + +compatibility with Node.js 19.0. + + + support rack v3 in ruby applications. diff --git a/src/nodejs/unit-http/loader.mjs b/src/nodejs/unit-http/loader.mjs index 546548f5..83985b0f 100644 --- a/src/nodejs/unit-http/loader.mjs +++ b/src/nodejs/unit-http/loader.mjs @@ -4,13 +4,15 @@ export async function resolve(specifier, context, defaultResolver) { case "websocket": return { url: new URL("./websocket.js", import.meta.url).href, - format: "commonjs" + format: "commonjs", + shortCircuit: true, } case "http": return { url: new URL("./http.js", import.meta.url).href, - format: "commonjs" + format: "commonjs", + shortCircuit: true, } } -- cgit From 09ac678943e253dfea65f84cd84963d863580efe Mon Sep 17 00:00:00 2001 From: Konstantin Pavlov Date: Tue, 29 Nov 2022 18:10:38 +0400 Subject: Used pkg-config to detect njs where available. --- auto/njs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/auto/njs b/auto/njs index 72304793..c0c43f19 100644 --- a/auto/njs +++ b/auto/njs @@ -5,10 +5,18 @@ nxt_found=no NXT_HAVE_NJS=NO -NXT_NJS_CFLAGS= -NXT_NJS_AUX_CFLAGS= -NXT_NJS_LIBS="-lnjs" -NXT_NJS_AUX_LIBS="$NXT_LIBM $NXT_LIB_AUX_LIBS" +if /bin/sh -c "(pkg-config njs --exists)" >> $NXT_AUTOCONF_ERR 2>&1; +then + NXT_NJS_AUX_CFLAGS= + NXT_NJS_AUX_LIBS= + NXT_NJS_CFLAGS=`pkg-config njs --cflags` + NXT_NJS_LIBS=`pkg-config njs --libs` +else + NXT_NJS_AUX_CFLAGS= + NXT_NJS_AUX_LIBS="$NXT_LIBM $NXT_LIB_AUX_LIBS" + NXT_NJS_CFLAGS= + NXT_NJS_LIBS="-lnjs" +fi nxt_feature="NJS" nxt_feature_name=NXT_HAVE_NJS -- cgit From 0feab91c5ba1bfd3e1e71cadb4cb0687bae846cd Mon Sep 17 00:00:00 2001 From: Konstantin Pavlov Date: Wed, 16 Nov 2022 17:06:42 +0400 Subject: Packages: added Fedora 37 support. --- pkg/rpm/Makefile | 15 +++++- pkg/rpm/Makefile.python311 | 55 ++++++++++++++++++++++ .../rpmbuild/SOURCES/unit.example-python311-config | 16 +++++++ 3 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 pkg/rpm/Makefile.python311 create mode 100644 pkg/rpm/rpmbuild/SOURCES/unit.example-python311-config diff --git a/pkg/rpm/Makefile b/pkg/rpm/Makefile index ef824c65..a1ac2af9 100644 --- a/pkg/rpm/Makefile +++ b/pkg/rpm/Makefile @@ -18,8 +18,10 @@ else ifeq ($(shell rpm --eval "%{?rhel}"), 9) OSVER = centos9 else ifeq ($(shell rpm --eval "%{?amzn}"), 2) OSVER = amazonlinux2 -else ifeq ($(shell test `rpm --eval '0%{?fedora} -ge 35'`; echo $$?),0) +else ifeq ($(shell test `rpm --eval '0%{?fedora} -ge 35 -a 0%{?fedora} -le 36'`; echo $$?),0) OSVER = fedora +else ifeq ($(shell test `rpm --eval '0%{?fedora} -ge 37'`; echo $$?),0) +OSVER = fedora37 endif BUILD_DEPENDS_unit = gcc rpm-build rpmlint @@ -93,6 +95,17 @@ include Makefile.jsc8 include Makefile.jsc11 endif +ifeq ($(OSVER), fedora37) +include Makefile.php +include Makefile.python311 +include Makefile.go +include Makefile.perl +include Makefile.ruby +include Makefile.jsc-common +include Makefile.jsc8 +include Makefile.jsc11 +endif + CONFIGURE_ARGS=\ --prefix=/usr \ --state=%{_sharedstatedir}/unit \ diff --git a/pkg/rpm/Makefile.python311 b/pkg/rpm/Makefile.python311 new file mode 100644 index 00000000..a8bee943 --- /dev/null +++ b/pkg/rpm/Makefile.python311 @@ -0,0 +1,55 @@ +MODULES+= python311 +MODULE_SUFFIX_python311= python3.11 + +MODULE_SUMMARY_python311= Python 3.11 module for NGINX Unit + +MODULE_VERSION_python311= $(VERSION) +MODULE_RELEASE_python311= 1 + +MODULE_CONFARGS_python311= python --config=python3.11-config +MODULE_MAKEARGS_python311= python3.11 +MODULE_INSTARGS_python311= python3.11-install + +MODULE_SOURCES_python311= unit.example-python-app \ + unit.example-python311-config + +ifneq (,$(findstring $(OSVER),fedora37)) +BUILD_DEPENDS_python311= python3-devel +endif + +BUILD_DEPENDS+= $(BUILD_DEPENDS_python311) + +define MODULE_PREINSTALL_python311 +%{__mkdir} -p %{buildroot}%{_datadir}/doc/unit-python311/examples/python-app +%{__install} -m 644 -p %{SOURCE100} \ + %{buildroot}%{_datadir}/doc/unit-python311/examples/python-app/wsgi.py +%{__install} -m 644 -p %{SOURCE101} \ + %{buildroot}%{_datadir}/doc/unit-python311/examples/unit.config +endef +export MODULE_PREINSTALL_python311 + +define MODULE_FILES_python311 +%{_libdir}/unit/modules/* +%{_libdir}/unit/debug-modules/* +endef +export MODULE_FILES_python311 + +define MODULE_POST_python311 +cat < Date: Wed, 16 Nov 2022 17:05:57 +0400 Subject: Packages: added Ubuntu 22.10 "kinetic" support. --- docs/changes.xml | 16 +++++- pkg/deb/Makefile | 15 ++++++ pkg/deb/Makefile.jsc19 | 71 +++++++++++++++++++++++++ pkg/deb/debian.module/unit.example-jsc19-config | 15 ++++++ 4 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 pkg/deb/Makefile.jsc19 create mode 100644 pkg/deb/debian.module/unit.example-jsc19-config diff --git a/docs/changes.xml b/docs/changes.xml index 9aaec9e5..e2444a8e 100644 --- a/docs/changes.xml +++ b/docs/changes.xml @@ -13,7 +13,8 @@ unit-perl unit-ruby unit-jsc-common unit-jsc8 unit-jsc10 unit-jsc11 unit-jsc13 - unit-jsc14 unit-jsc15 unit-jsc16 unit-jsc17 unit-jsc18" + unit-jsc14 unit-jsc15 unit-jsc16 unit-jsc17 unit-jsc18 + unit-jsc19" ver="1.29.0" rev="1" date="" time="" packager="Nginx Packaging <nginx-packaging@f5.com>"> @@ -27,6 +28,19 @@ NGINX Unit updated to 1.29.0. + + + + +Initial release of Java 19 module for NGINX Unit. + + + + + + diff --git a/pkg/deb/Makefile b/pkg/deb/Makefile index 6595cb45..e9cf53b3 100644 --- a/pkg/deb/Makefile +++ b/pkg/deb/Makefile @@ -19,6 +19,21 @@ BUILD_DEPENDS = $(BUILD_DEPENDS_unit) MODULES= +# Ubuntu 22.10 +ifeq ($(CODENAME),kinetic) +include Makefile.php +include Makefile.python27 +include Makefile.python310 +include Makefile.go +include Makefile.perl +include Makefile.ruby +include Makefile.jsc-common +include Makefile.jsc11 +include Makefile.jsc17 +include Makefile.jsc18 +include Makefile.jsc19 +endif + # Ubuntu 22.04 ifeq ($(CODENAME),jammy) include Makefile.php diff --git a/pkg/deb/Makefile.jsc19 b/pkg/deb/Makefile.jsc19 new file mode 100644 index 00000000..2a6ef7db --- /dev/null +++ b/pkg/deb/Makefile.jsc19 @@ -0,0 +1,71 @@ +MODULES+= jsc19 +MODULE_SUFFIX_jsc19= jsc19 + +MODULE_SUMMARY_jsc19= Java 19 module for NGINX Unit + +MODULE_VERSION_jsc19= $(VERSION) +MODULE_RELEASE_jsc19= 1 + +MODULE_CONFARGS_jsc19= java --module=java19 --home=/usr/lib/jvm/java-19-openjdk-$$\(DEB_HOST_ARCH\) --jars=/usr/share/unit-jsc-common/ +MODULE_MAKEARGS_jsc19= java19 +MODULE_INSTARGS_jsc19= java19-install + +MODULE_SOURCES_jsc19= unit.example-jsc-app \ + unit.example-jsc19-config + +BUILD_DEPENDS_jsc19= openjdk-19-jdk-headless openjdk-19-jre-headless +BUILD_DEPENDS+= $(BUILD_DEPENDS_jsc19) + +MODULE_BUILD_DEPENDS_jsc19=,openjdk-19-jdk-headless +MODULE_DEPENDS_jsc19=,openjdk-19-jre-headless,unit-jsc-common (= $(MODULE_VERSION_jsc_common)-$(MODULE_RELEASE_jsc_common)~$(CODENAME)) + +define MODULE_PREINSTALL_jsc19 + mkdir -p debian/unit-jsc19/usr/share/doc/unit-jsc19/examples/jsc-app + install -m 644 -p debian/unit.example-jsc-app debian/unit-jsc19/usr/share/doc/unit-jsc19/examples/jsc-app/index.jsp + install -m 644 -p debian/unit.example-jsc19-config debian/unit-jsc19/usr/share/doc/unit-jsc19/examples/unit.config + install -m 644 -p src/java/README.JSR-340 debian/unit-jsc19/usr/share/doc/unit-jsc19/ +endef +export MODULE_PREINSTALL_jsc19 + +define MODULE_POSTINSTALL_jsc19 + cd $$\(BUILDDIR_unit\) \&\& \ + DESTDIR=$$\(INSTALLDIR\) make java-shared-uninstall +endef +export MODULE_POSTINSTALL_jsc19 + +define MODULE_POST_jsc19 +cat < Date: Tue, 6 Dec 2022 20:57:16 +0000 Subject: Fix compilation with GCC and -O0. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Andrei reported an issue with building unit when using '-O0' with GCC producing the following compiler errors cc -c -pipe -fPIC -fvisibility=hidden -O -W -Wall -Wextra -Wno-unused-parameter -Wwrite-strings -Wmissing-prototypes -Werror -g -O0 -I src -I build \ \ \ -o build/src/nxt_unit.o \ -MMD -MF build/src/nxt_unit.dep -MT build/src/nxt_unit.o \ src/nxt_unit.c src/nxt_unit.c: In function ‘nxt_unit_log’: src/nxt_unit.c:6601:9: error: ‘msg’ may be used uninitialized [-Werror=maybe-uninitialized] 6601 | p = nxt_unit_snprint_prefix(p, end, pid, level); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ src/nxt_unit.c:6682:1: note: by argument 2 of type ‘const char *’ to ‘nxt_unit_snprint_prefix’ declared here 6682 | nxt_unit_snprint_prefix(char *p, const char *end, pid_t pid, int level) | ^~~~~~~~~~~~~~~~~~~~~~~ src/nxt_unit.c:6582:22: note: ‘msg’ declared here 6582 | char msg[NXT_MAX_ERROR_STR], *p, *end; | ^~~ src/nxt_unit.c: In function ‘nxt_unit_req_log’: src/nxt_unit.c:6645:9: error: ‘msg’ may be used uninitialized [-Werror=maybe-uninitialized] 6645 | p = nxt_unit_snprint_prefix(p, end, pid, level); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ src/nxt_unit.c:6682:1: note: by argument 2 of type ‘const char *’ to ‘nxt_unit_snprint_prefix’ declared here 6682 | nxt_unit_snprint_prefix(char *p, const char *end, pid_t pid, int level) | ^~~~~~~~~~~~~~~~~~~~~~~ src/nxt_unit.c:6625:35: note: ‘msg’ declared here 6625 | char msg[NXT_MAX_ERROR_STR], *p, *end; | ^~~ cc1: all warnings being treated as errors The above was reproduced with $ ./configure --cc-opt=-O0 && ./configure python && make -j4 This warning doesn't happen on clang (15.0.4) or GCC (8.3) and seems to have been introduced in GCC 11. The above is from GCC (12.2.1, Fedora 37). The trigger of this GCC issue is actually part of a commit I introduced a few months back to constify some function parameters and it seems the consensus for how to resolve this problem is to simply remove the const qualifier from the *end parameter to nxt_unit_snprint_prefix(). Reported-by: Andrei Zeliankou Link: Link: Link: Fixes: 4418f99 ("Constified numerous function parameters.") Signed-off-by: Andrew Clayton --- src/nxt_unit.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/nxt_unit.c b/src/nxt_unit.c index 3932b2ab..e1b1897a 100644 --- a/src/nxt_unit.c +++ b/src/nxt_unit.c @@ -196,7 +196,7 @@ static int nxt_unit_request_hash_add(nxt_unit_ctx_t *ctx, static nxt_unit_request_info_t *nxt_unit_request_hash_find( nxt_unit_ctx_t *ctx, uint32_t stream, int remove); -static char * nxt_unit_snprint_prefix(char *p, const char *end, pid_t pid, +static char * nxt_unit_snprint_prefix(char *p, char *end, pid_t pid, int level); static void *nxt_unit_lvlhsh_alloc(void *data, size_t size); static void nxt_unit_lvlhsh_free(void *data, void *p); @@ -6679,7 +6679,7 @@ static const char * nxt_unit_log_levels[] = { static char * -nxt_unit_snprint_prefix(char *p, const char *end, pid_t pid, int level) +nxt_unit_snprint_prefix(char *p, char *end, pid_t pid, int level) { struct tm tm; struct timespec ts; -- cgit From 55b9a5307d705da91d3ef317639356c748853a7c Mon Sep 17 00:00:00 2001 From: Andrei Zeliankou Date: Fri, 9 Dec 2022 14:17:49 +0000 Subject: Tests: fixed tests to run as privileged user. --- test/test_python_isolation.py | 13 +++++++------ test/unit/utils.py | 10 ++++++++-- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/test/test_python_isolation.py b/test/test_python_isolation.py index 8cef6812..db398444 100644 --- a/test/test_python_isolation.py +++ b/test/test_python_isolation.py @@ -63,24 +63,25 @@ class TestPythonIsolation(TestApplicationPython): pytest.skip('requires root') isolation = {'rootfs': temp_dir, 'automount': {'language_deps': False}} - self.load('empty', isolation=isolation) - assert findmnt().find(temp_dir) == -1 + python_path = temp_dir + '/usr' + + assert findmnt().find(python_path) == -1 assert self.get()['status'] != 200, 'disabled language_deps' - assert findmnt().find(temp_dir) == -1 + assert findmnt().find(python_path) == -1 isolation['automount']['language_deps'] = True self.load('empty', isolation=isolation) - assert findmnt().find(temp_dir) == -1 + assert findmnt().find(python_path) == -1 assert self.get()['status'] == 200, 'enabled language_deps' - assert waitformount(temp_dir), 'language_deps mount' + assert waitformount(python_path), 'language_deps mount' self.conf({"listeners": {}, "applications": {}}) - assert waitforunmount(temp_dir), 'language_deps unmount' + assert waitforunmount(python_path), 'language_deps unmount' def test_python_isolation_procfs(self, is_su, temp_dir): if not is_su: diff --git a/test/unit/utils.py b/test/unit/utils.py index 43aaa81b..f9e9d08a 100644 --- a/test/unit/utils.py +++ b/test/unit/utils.py @@ -12,9 +12,15 @@ def public_dir(path): for root, dirs, files in os.walk(path): for d in dirs: - os.chmod(os.path.join(root, d), 0o777) + try: + os.chmod(os.path.join(root, d), 0o777) + except FileNotFoundError: + pass for f in files: - os.chmod(os.path.join(root, f), 0o777) + try: + os.chmod(os.path.join(root, f), 0o777) + except FileNotFoundError: + pass def waitforfiles(*files, timeout=50): -- cgit From 9466daf9bdafa3e00f521a47f4ce218353bf7f86 Mon Sep 17 00:00:00 2001 From: Andrew Clayton Date: Thu, 1 Dec 2022 01:39:57 +0000 Subject: Added simple wrappers for fopen(3) and fclose(3). Add simple wrapper functions for fopen(3) and fclose(3) that are somewhat akin to the nxt_file_open() and nxt_file_close() wrappers that log errors. Suggested-by: Alejandro Colomar Reviewed-by: Alejandro Colomar Signed-off-by: Andrew Clayton --- src/nxt_file.c | 37 +++++++++++++++++++++++++++++++++++++ src/nxt_file.h | 4 ++++ 2 files changed, 41 insertions(+) diff --git a/src/nxt_file.c b/src/nxt_file.c index 5d38d57e..a3fcda76 100644 --- a/src/nxt_file.c +++ b/src/nxt_file.c @@ -500,6 +500,43 @@ nxt_fd_close(nxt_fd_t fd) } +FILE * +nxt_file_fopen(nxt_task_t *task, const char *pathname, const char *mode) +{ + int err; + FILE *fp; + +#if (NXT_DEBUG) + nxt_thread_time_update(task->thread); +#endif + + fp = fopen(pathname, mode); + err = (fp == NULL) ? nxt_errno : 0; + + nxt_debug(task, "fopen(\"%s\", \"%s\"): fp:%p err:%d", pathname, mode, fp, + err); + + if (nxt_fast_path(fp != NULL)) { + return fp; + } + + nxt_alert(task, "fopen(\"%s\") failed %E", pathname, err); + + return NULL; +} + + +void +nxt_file_fclose(nxt_task_t *task, FILE *fp) +{ + nxt_debug(task, "fclose(%p)", fp); + + if (nxt_slow_path(fclose(fp) == -1)) { + nxt_alert(task, "fclose() failed %E", nxt_errno); + } +} + + /* * nxt_file_redirect() redirects the file to the fd descriptor. * Then the fd descriptor is closed. diff --git a/src/nxt_file.h b/src/nxt_file.h index 07c7a22b..945717b3 100644 --- a/src/nxt_file.h +++ b/src/nxt_file.h @@ -186,6 +186,10 @@ NXT_EXPORT ssize_t nxt_fd_write(nxt_fd_t fd, u_char *buf, size_t size); NXT_EXPORT ssize_t nxt_fd_read(nxt_fd_t fd, u_char *buf, size_t size); NXT_EXPORT void nxt_fd_close(nxt_fd_t fd); +NXT_EXPORT FILE *nxt_file_fopen(nxt_task_t *task, const char *pathname, + const char *mode); +NXT_EXPORT void nxt_file_fclose(nxt_task_t *task, FILE *fp); + NXT_EXPORT nxt_int_t nxt_file_redirect(nxt_file_t *file, nxt_fd_t fd); NXT_EXPORT nxt_int_t nxt_file_stderr(nxt_file_t *file); NXT_EXPORT nxt_int_t nxt_stderr_start(void); -- cgit From 7d177faf3b8a483fd7ef958e884ec5625e058ca0 Mon Sep 17 00:00:00 2001 From: Andrew Clayton Date: Mon, 24 Oct 2022 17:33:23 +0100 Subject: Isolation: added core cgroup infrastructure. Firstly, this is not to be confused with CLONE_NEWCGROUP which unit already supports and is related to namespaces. To re-cap, namespaces allow processes to have different views of various parts of the system such as filesystem mounts, networking, hostname etc. Whereas cgroup[0] is a Linux kernel facility for collecting a bunch of processes together to perform some task on the group as a whole, for example to implement resource limits. There are two parts to cgroup, the core part of organising processes into a hierarchy and the controllers which are responsible for enforcing resource limits etc. There are currently two versions of the cgroup sub-system, the original cgroup and a version 2[1] introduced in 3.16 (August 2014) and marked stable in 4.5 (March 2016). This commit supports the cgroup V2 API and implements the ability to place applications into their own cgroup on a per-application basis. You can put them each into their own cgroup or you can group some together. The ability to set resource limits can easily be added in future. The initial use case of this would be to aid in observability of unit applications which becomes much easier if you can just monitor them on a per cgroup basis. One thing to note about cgroup, is that unlike namespaces which are controlled via system calls such as clone(2) and unshare(2), cgroups are setup and controlled through the cgroupfs pseudo-filesystem. cgroup is Linux only and this support will only be enabled if configure finds the cgroup2 filesystem mount, e.g cgroup2 on /sys/fs/cgroup type cgroup2 (rw,nosuid,nodev,noexec,relatime,seclabel,nsdelegate,memory_recursiveprot) The cgroups are removed on shutdown or as required on reconfiguration. This commit just adds the basic infrastructure for using cgroups within unit. Subsequent commits will wire up this support. It supports creating cgroups relative to the main cgroup root and also below the cgroup of the main unit process. [0]: [1]: Cc: Alejandro Colomar Signed-off-by: Andrew Clayton --- src/nxt_cgroup.c | 174 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/nxt_cgroup.h | 14 +++++ 2 files changed, 188 insertions(+) create mode 100644 src/nxt_cgroup.c create mode 100644 src/nxt_cgroup.h diff --git a/src/nxt_cgroup.c b/src/nxt_cgroup.c new file mode 100644 index 00000000..2c404acc --- /dev/null +++ b/src/nxt_cgroup.c @@ -0,0 +1,174 @@ +/* + * Copyright (C) Andrew Clayton + * Copyright (C) F5, Inc. + */ + +#include + +#include + + +static int nxt_mk_cgpath_relative(nxt_task_t *task, const char *dir, + char *cgpath); +static nxt_int_t nxt_mk_cgpath(nxt_task_t *task, const char *dir, + char *cgpath); + + +nxt_int_t +nxt_cgroup_proc_add(nxt_task_t *task, nxt_process_t *process) +{ + int len; + char cgprocs[NXT_MAX_PATH_LEN]; + FILE *fp; + nxt_int_t ret; + + if (task->thread->runtime->type != NXT_PROCESS_MAIN + || nxt_process_type(process) != NXT_PROCESS_PROTOTYPE + || process->isolation.cgroup.path == NULL) + { + return NXT_OK; + } + + ret = nxt_mk_cgpath(task, process->isolation.cgroup.path, cgprocs); + if (nxt_slow_path(ret == NXT_ERROR)) { + return NXT_ERROR; + } + + ret = nxt_fs_mkdir_all((const u_char *) cgprocs, 0777); + if (nxt_slow_path(ret == NXT_ERROR)) { + return NXT_ERROR; + } + + len = strlen(cgprocs); + + len = snprintf(cgprocs + len, NXT_MAX_PATH_LEN - len, "/cgroup.procs"); + if (nxt_slow_path(len >= NXT_MAX_PATH_LEN - len)) { + nxt_errno = ENAMETOOLONG; + return NXT_ERROR; + } + + fp = nxt_file_fopen(task, cgprocs, "we"); + if (nxt_slow_path(fp == NULL)) { + return NXT_ERROR; + } + + setvbuf(fp, NULL, _IONBF, 0); + len = fprintf(fp, "%d\n", process->pid); + nxt_file_fclose(task, fp); + + if (nxt_slow_path(len < 0)) { + return NXT_ERROR; + } + + return NXT_OK; +} + + +void +nxt_cgroup_cleanup(nxt_task_t *task, const nxt_process_t *process) +{ + char *ptr; + char cgroot[NXT_MAX_PATH_LEN], cgpath[NXT_MAX_PATH_LEN]; + nxt_int_t ret; + + ret = nxt_mk_cgpath(task, "", cgroot); + if (nxt_slow_path(ret == NXT_ERROR)) { + return; + } + + ret = nxt_mk_cgpath(task, process->isolation.cgroup.path, cgpath); + if (nxt_slow_path(ret == NXT_ERROR)) { + return; + } + + while (*cgpath != '\0' && strcmp(cgroot, cgpath) != 0) { + rmdir(cgpath); + ptr = strrchr(cgpath, '/'); + *ptr = '\0'; + } +} + + +static int +nxt_mk_cgpath_relative(nxt_task_t *task, const char *dir, char *cgpath) +{ + int i, len; + char *buf, *ptr; + FILE *fp; + size_t size; + ssize_t nread; + nxt_bool_t found; + + fp = nxt_file_fopen(task, "/proc/self/cgroup", "re"); + if (nxt_slow_path(fp == NULL)) { + return -1; + } + + len = -1; + buf = NULL; + found = 0; + while ((nread = getline(&buf, &size, fp)) != -1) { + if (strncmp(buf, "0::", 3) == 0) { + found = 1; + break; + } + } + + nxt_file_fclose(task, fp); + + if (!found) { + nxt_errno = ENODATA; + goto out_free_buf; + } + + buf[nread - 1] = '\0'; /* lose the trailing '\n' */ + ptr = buf; + for (i = 0; i < 2; i++) { + ptr = strchr(ptr, ':'); + if (ptr == NULL) { + nxt_errno = ENODATA; + goto out_free_buf; + } + + ptr++; + } + + len = snprintf(cgpath, NXT_MAX_PATH_LEN, NXT_CGROUP_ROOT "%s/%s", + ptr, dir); + +out_free_buf: + + nxt_free(buf); + + return len; +} + + +static nxt_int_t +nxt_mk_cgpath(nxt_task_t *task, const char *dir, char *cgpath) +{ + int len; + + /* + * If the path from the config is relative, we need to make + * the cgroup path include the main unit processes cgroup. I.e + * + * NXT_CGROUP_ROOT/
/ + */ + if (dir[0] != '/') { + len = nxt_mk_cgpath_relative(task, dir, cgpath); + } else { + len = snprintf(cgpath, NXT_MAX_PATH_LEN, NXT_CGROUP_ROOT "%s", dir); + } + + if (len == -1) { + return NXT_ERROR; + } + + if (len >= NXT_MAX_PATH_LEN) { + nxt_errno = ENAMETOOLONG; + return NXT_ERROR; + } + + return NXT_OK; +} diff --git a/src/nxt_cgroup.h b/src/nxt_cgroup.h new file mode 100644 index 00000000..0b9055d2 --- /dev/null +++ b/src/nxt_cgroup.h @@ -0,0 +1,14 @@ +/* + * Copyright (C) Andrew Clayton + * Copyright (C) F5, Inc. + */ + +#ifndef _NXT_CGROUP_H_INCLUDED_ +#define _NXT_CGROUP_H_INCLUDED_ + + +nxt_int_t nxt_cgroup_proc_add(nxt_task_t *task, nxt_process_t *process); +void nxt_cgroup_cleanup(nxt_task_t *task, const nxt_process_t *process); + + +#endif /* _NXT_CGROUP_H_INCLUDED_ */ -- cgit From 867a839f103bf7859b76eb98cfc28e7f0155dd1b Mon Sep 17 00:00:00 2001 From: Andrew Clayton Date: Mon, 24 Oct 2022 17:35:04 +0100 Subject: Isolation: wired up per-application cgroup support internally. This commit hooks into the cgroup infrastructure added in the previous commit to create per-application cgroups. It does this by adding each "prototype process" into its own cgroup, then each child process inherits its parents cgroup. If we fail to create a cgroup we simply fail the process. This behaviour may get enhanced in the future. This won't actually do anything yet. Subsequent commits will hook this up to the build and config systems. Reviewed-by: Alejandro Colomar Signed-off-by: Andrew Clayton --- src/nxt_isolation.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/nxt_main_process.c | 4 ++++ src/nxt_process.c | 12 ++++++++++++ src/nxt_process.h | 13 +++++++++++++ 4 files changed, 79 insertions(+) diff --git a/src/nxt_isolation.c b/src/nxt_isolation.c index 796da4c6..b6b13c59 100644 --- a/src/nxt_isolation.c +++ b/src/nxt_isolation.c @@ -6,6 +6,7 @@ #include #include #include +#include #if (NXT_HAVE_MNTENT_H) #include @@ -15,6 +16,11 @@ static nxt_int_t nxt_isolation_set(nxt_task_t *task, nxt_conf_value_t *isolation, nxt_process_t *process); +#if (NXT_HAVE_CGROUP) +static nxt_int_t nxt_isolation_set_cgroup(nxt_task_t *task, + nxt_conf_value_t *isolation, nxt_process_t *process); +#endif + #if (NXT_HAVE_CLONE) static nxt_int_t nxt_isolation_set_namespaces(nxt_task_t *task, nxt_conf_value_t *isolation, nxt_process_t *process); @@ -155,6 +161,14 @@ static nxt_int_t nxt_isolation_set(nxt_task_t *task, nxt_conf_value_t *isolation, nxt_process_t *process) { +#if (NXT_HAVE_CGROUP) + if (nxt_slow_path(nxt_isolation_set_cgroup(task, isolation, process) + != NXT_OK)) + { + return NXT_ERROR; + } +#endif + #if (NXT_HAVE_CLONE) if (nxt_slow_path(nxt_isolation_set_namespaces(task, isolation, process) != NXT_OK)) @@ -197,6 +211,42 @@ nxt_isolation_set(nxt_task_t *task, nxt_conf_value_t *isolation, } +#if (NXT_HAVE_CGROUP) + +static nxt_int_t +nxt_isolation_set_cgroup(nxt_task_t *task, nxt_conf_value_t *isolation, + nxt_process_t *process) +{ + nxt_str_t str; + nxt_conf_value_t *obj; + + static nxt_str_t cgname = nxt_string("cgroup"); + static nxt_str_t path = nxt_string("path"); + + obj = nxt_conf_get_object_member(isolation, &cgname, NULL); + if (obj == NULL) { + return NXT_OK; + } + + obj = nxt_conf_get_object_member(obj, &path, NULL); + if (obj == NULL) { + return NXT_ERROR; + } + + nxt_conf_get_string(obj, &str); + process->isolation.cgroup.path = nxt_mp_alloc(process->mem_pool, + str.length + 1); + nxt_memcpy(process->isolation.cgroup.path, str.start, str.length); + process->isolation.cgroup.path[str.length] = '\0'; + + process->isolation.cgroup_cleanup = nxt_cgroup_cleanup; + + return NXT_OK; +} + +#endif + + #if (NXT_HAVE_CLONE) static nxt_int_t diff --git a/src/nxt_main_process.c b/src/nxt_main_process.c index f21482d5..de41e8d7 100644 --- a/src/nxt_main_process.c +++ b/src/nxt_main_process.c @@ -1007,6 +1007,10 @@ nxt_main_process_cleanup(nxt_task_t *task, nxt_process_t *process) if (process->isolation.cleanup != NULL) { process->isolation.cleanup(task, process); } + + if (process->isolation.cgroup_cleanup != NULL) { + process->isolation.cgroup_cleanup(task, process); + } } diff --git a/src/nxt_process.c b/src/nxt_process.c index 738a03bf..d8836ad2 100644 --- a/src/nxt_process.c +++ b/src/nxt_process.c @@ -5,6 +5,7 @@ */ #include +#include #if (NXT_HAVE_CLONE) #include @@ -378,6 +379,17 @@ nxt_process_create(nxt_task_t *task, nxt_process_t *process) nxt_runtime_process_add(task, process); } +#if (NXT_HAVE_CGROUP) + ret = nxt_cgroup_proc_add(task, process); + if (nxt_slow_path(ret != NXT_OK)) { + nxt_alert(task, "cgroup: failed to add process %s to %s %E", + process->name, process->isolation.cgroup.path, nxt_errno); + nxt_cgroup_cleanup(task, process); + kill(pid, SIGTERM); + return -1; + } +#endif + return pid; } diff --git a/src/nxt_process.h b/src/nxt_process.h index 15fd4e7f..0db68d45 100644 --- a/src/nxt_process.h +++ b/src/nxt_process.h @@ -61,8 +61,11 @@ typedef enum { typedef struct nxt_port_mmap_s nxt_port_mmap_t; typedef struct nxt_process_s nxt_process_t; +typedef struct nxt_cgroup_s nxt_cgroup_t; typedef void (*nxt_isolation_cleanup_t)(nxt_task_t *task, nxt_process_t *process); +typedef void (*nxt_cgroup_cleanup_t)(nxt_task_t *task, + const nxt_process_t *process); typedef struct { @@ -80,6 +83,11 @@ typedef struct { } nxt_process_automount_t; +struct nxt_cgroup_s { + char *path; +}; + + typedef struct { u_char *rootfs; nxt_process_automount_t automount; @@ -87,6 +95,11 @@ typedef struct { nxt_isolation_cleanup_t cleanup; + nxt_cgroup_cleanup_t cgroup_cleanup; +#if (NXT_HAVE_CGROUP) + nxt_cgroup_t cgroup; +#endif + #if (NXT_HAVE_CLONE) nxt_clone_t clone; #endif -- cgit From c9e433a13d0e3a9d580891f83fd5fe7f640492b7 Mon Sep 17 00:00:00 2001 From: Andrew Clayton Date: Mon, 24 Oct 2022 14:13:23 +0100 Subject: Isolation: wired up cgroup to build system. This commit enables the building of the cgroup code. This is only built when the cgroupv2 filesystem is found. If cgroupv2 support is found then cgroupv2: .................. YES will be printed by ./configure Reviewed-by: Alejandro Colomar Signed-off-by: Andrew Clayton --- auto/cgroup | 22 ++++++++++++++++++++++ auto/sources | 5 +++++ auto/summary | 1 + configure | 1 + 4 files changed, 29 insertions(+) create mode 100644 auto/cgroup diff --git a/auto/cgroup b/auto/cgroup new file mode 100644 index 00000000..2262b2ef --- /dev/null +++ b/auto/cgroup @@ -0,0 +1,22 @@ +# Copyright (C) Andrew Clayton +# Copyright (C) F5, Inc. + +NXT_HAVE_CGROUP=NO + +if [ -f "/proc/mounts" ]; then + CGROUP=$(grep cgroup2 /proc/mounts | head -n 1 | cut -d " " -f 2) + + if [ "$CGROUP" ]; then + NXT_HAVE_CGROUP=YES + + cat << END >> $NXT_AUTO_CONFIG_H + +#ifndef NXT_HAVE_CGROUP +#define NXT_HAVE_CGROUP 1 +#define NXT_CGROUP_ROOT "$CGROUP" +#endif + +END + + fi +fi diff --git a/auto/sources b/auto/sources index cebced3a..29f3c7b5 100644 --- a/auto/sources +++ b/auto/sources @@ -304,6 +304,11 @@ if [ "$NXT_HAVE_CLONE" = "YES" ]; then fi +if [ "$NXT_HAVE_CGROUP" = "YES" ]; then + NXT_LIB_SRCS="$NXT_LIB_SRCS src/nxt_cgroup.c" +fi + + if [ "$NXT_TEST_BUILD" = "YES" ]; then NXT_LIB_SRCS="$NXT_LIB_SRCS $NXT_TEST_BUILD_SRCS" fi diff --git a/auto/summary b/auto/summary index c8a49d08..51db0eae 100644 --- a/auto/summary +++ b/auto/summary @@ -31,6 +31,7 @@ Unit configuration summary: NJS support: ............... $NXT_NJS process isolation: ......... $NXT_ISOLATION + cgroupv2: .................. $NXT_HAVE_CGROUP debug logging: ............. $NXT_DEBUG diff --git a/configure b/configure index c21973eb..1d897f1d 100755 --- a/configure +++ b/configure @@ -129,6 +129,7 @@ if [ $NXT_REGEX = YES ]; then . auto/pcre fi +. auto/cgroup . auto/isolation . auto/capability -- cgit From f67a01b88fd7c7057767e18a3dd06c24e94c8aa8 Mon Sep 17 00:00:00 2001 From: Andrew Clayton Date: Mon, 24 Oct 2022 17:14:06 +0100 Subject: Isolation: wired up cgroup support to the config system. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This hooks the cgroup support up to the config system so it can actually be used. To make use of this in unit a new "cgroup" section has been added to the isolation configuration. e.g "applications": { "python": { "type": "python", "processes": 5, "path": "/opt/unit/unit-cgroup-test/", "module": "app", "isolation": { "cgroup": { "path": "app/python" } } } } Now there are two ways to specify the path, relative, like the above (without a leading '/') and absolute (with a leading '/'). In the above case the "python" application is placed into its own cgroup under CGROUP_ROOT/
/app/python. Whereas if you specified say "path": "/unit/app/python" Then the python application would be placed under CGROUP_ROOT/unit/app/python The first option allows you to easily take advantage of any resource limits that have already been configured for unit. With the second method (absolute pathname) if you know of an already existing cgroup where you'd like to place it, you can, e.g "path": "/system.slice/unit/python" Where system.slice has already been created by systemd and may already have some overall system limits applied which would also apply to unit. Limits apply down the hierarchy and lower groups can't exceed the previous group limits. So what does this actually look like? Lets take the unit-calculator application[0] and have each of its applications placed into their own cgroup. If we give each application a new section like "isolation": { "cgroup": { "path": "/unit/unit-calculator/add" } } changing the path for each one, we can visualise the result with the systemd-cgls command, e.g │ └─session-5.scope (#4561) │ ├─ 6667 sshd: andrew [priv] │ ├─ 6684 sshd: andrew@pts/0 │ ├─ 6685 -bash │ ├─ 12632 unit: main v1.28.0 [/opt/unit/sbin/unitd --control 127.0.0.1:808> │ ├─ 12634 unit: controller │ ├─ 12635 unit: router │ ├─ 13550 systemd-cgls │ └─ 13551 less ├─unit (#4759) │ └─unit-calculator (#5037) │ ├─subtract (#5069) │ │ ├─ 12650 unit: "subtract" prototype │ │ └─ 12651 unit: "subtract" application │ ├─multiply (#5085) │ │ ├─ 12653 unit: "multiply" prototype │ │ └─ 12654 unit: "multiply" application │ ├─divide (#5101) │ │ ├─ 12671 unit: "divide" prototype │ │ └─ 12672 node divide.js │ ├─sqroot (#5117) │ │ ├─ 12679 unit: "sqroot" prototype │ │ └─ 12680 /home/andrew/src/unit-calculator/sqroot/sqroot │ └─add (#5053) │ ├─ 12648 unit: "add" prototype │ └─ 12649 unit: "add" application We used an absolute path so the cgroups will be created relative to the main cgroupfs mount, e.g /sys/fs/cgroup We can see that the main unit processes are in the same cgroup as the shell from where they were started, by default child process are placed into the same cgroup as the parent. Then we can see that each application has been placed into its own cgroup under /sys/fs/cgroup Taking another example of a simple 5 process python application, with "isolation": { "cgroup": { "path": "app/python" } } Here we have specified a relative path and thus the python application will be placed below the existing cgroup that contains the main unit process. E.g │ │ │ ├─app-glib-cinnamon\x2dcustom\x2dlauncher\x2d3-43951.scope (#90951) │ │ │ │ ├─ 988 unit: main v1.28.0 [/opt/unit/sbin/unitd --no-daemon] │ │ │ │ ├─ 990 unit: controller │ │ │ │ ├─ 991 unit: router │ │ │ │ ├─ 43951 xterm -bg rgb:20/20/20 -fg white -fa DejaVu Sans Mono │ │ │ │ ├─ 43956 bash │ │ │ │ ├─ 58828 sudo -i │ │ │ │ ├─ 58831 -bash │ │ │ │ └─app (#107351) │ │ │ │ └─python (#107367) │ │ │ │ ├─ 992 unit: "python" prototype │ │ │ │ ├─ 993 unit: "python" application │ │ │ │ ├─ 994 unit: "python" application │ │ │ │ ├─ 995 unit: "python" application │ │ │ │ ├─ 996 unit: "python" application │ │ │ │ └─ 997 unit: "python" application [0]: Reviewed-by: Alejandro Colomar Signed-off-by: Andrew Clayton --- docs/changes.xml | 6 +++++ src/nxt_conf_validation.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/docs/changes.xml b/docs/changes.xml index e2444a8e..3a24f9ca 100644 --- a/docs/changes.xml +++ b/docs/changes.xml @@ -57,6 +57,12 @@ prefer system crypto policy, instead of hardcoding a default. + + +support per-application cgroups on Linux. + + + compatibility with Python 3.11. diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index e650b44d..0f22c540 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -219,6 +219,11 @@ static nxt_int_t nxt_conf_vldt_clone_gidmap(nxt_conf_validation_t *vldt, nxt_conf_value_t *value); #endif +#if (NXT_HAVE_CGROUP) +static nxt_int_t nxt_conf_vldt_cgroup_path(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value, void *data); +#endif + static nxt_conf_vldt_object_t nxt_conf_vldt_setting_members[]; static nxt_conf_vldt_object_t nxt_conf_vldt_http_members[]; @@ -240,6 +245,9 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_app_limits_members[]; static nxt_conf_vldt_object_t nxt_conf_vldt_app_processes_members[]; static nxt_conf_vldt_object_t nxt_conf_vldt_app_isolation_members[]; static nxt_conf_vldt_object_t nxt_conf_vldt_app_namespaces_members[]; +#if (NXT_HAVE_CGROUP) +static nxt_conf_vldt_object_t nxt_conf_vldt_app_cgroup_members[]; +#endif #if (NXT_HAVE_ISOLATION_ROOTFS) static nxt_conf_vldt_object_t nxt_conf_vldt_app_automount_members[]; #endif @@ -1094,6 +1102,15 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_app_isolation_members[] = { }, #endif +#if (NXT_HAVE_CGROUP) + { + .name = nxt_string("cgroup"), + .type = NXT_CONF_VLDT_OBJECT, + .validator = nxt_conf_vldt_object, + .u.members = nxt_conf_vldt_app_cgroup_members, + }, +#endif + NXT_CONF_VLDT_END }; @@ -1166,6 +1183,22 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_app_automount_members[] = { #endif +#if (NXT_HAVE_CGROUP) + +static nxt_conf_vldt_object_t nxt_conf_vldt_app_cgroup_members[] = { + { + .name = nxt_string("path"), + .type = NXT_CONF_VLDT_STRING, + .flags = NXT_CONF_VLDT_REQUIRED, + .validator = nxt_conf_vldt_cgroup_path, + }, + + NXT_CONF_VLDT_END +}; + +#endif + + #if (NXT_HAVE_CLONE_NEWUSER) static nxt_conf_vldt_object_t nxt_conf_vldt_app_procmap_members[] = { @@ -2798,6 +2831,35 @@ nxt_conf_vldt_target(nxt_conf_validation_t *vldt, nxt_str_t *name, } +#if (NXT_HAVE_CGROUP) + +static nxt_int_t +nxt_conf_vldt_cgroup_path(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, + void *data) +{ + char path[NXT_MAX_PATH_LEN]; + nxt_str_t cgpath; + + nxt_conf_get_string(value, &cgpath); + if (cgpath.length >= NXT_MAX_PATH_LEN - strlen(NXT_CGROUP_ROOT) - 1) { + return nxt_conf_vldt_error(vldt, "The cgroup path \"%V\" is too long.", + &cgpath); + } + + sprintf(path, "/%*s/", (int) cgpath.length, cgpath.start); + + if (cgpath.length == 0 || strstr(path, "/../") != NULL) { + return nxt_conf_vldt_error(vldt, + "The cgroup path \"%V\" is invalid.", + &cgpath); + } + + return NXT_OK; +} + +#endif + + static nxt_int_t nxt_conf_vldt_clone_namespaces(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data) -- cgit From 648e91a623d3822e8ab4780b452da211ea3ba257 Mon Sep 17 00:00:00 2001 From: Andrei Zeliankou Date: Mon, 12 Dec 2022 16:24:54 +0000 Subject: Tests: pretty output. Hide expected alerts by default. Silence succesfull "go build" information. --- test/conftest.py | 9 +++++---- test/unit/applications/lang/go.py | 6 ++++-- test/unit/check/go.py | 4 +--- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/test/conftest.py b/test/conftest.py index 759f11bd..c2680744 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -485,14 +485,15 @@ def _check_alerts(*, log=None): log = f.read() found = False - alerts = re.findall(r'.+\[alert\].+', log) if alerts: - print('\nAll alerts/sanitizer errors found in log:') - [print(alert) for alert in alerts] found = True + if option.detailed: + print('\nAll alerts/sanitizer errors found in log:') + [print(alert) for alert in alerts] + if option.skip_alerts: for skip in option.skip_alerts: alerts = [al for al in alerts if re.search(skip, al) is None] @@ -504,7 +505,7 @@ def _check_alerts(*, log=None): assert not sanitizer_errors, 'sanitizer error(s)' - if found: + if found and option.detailed: print('skipped.') diff --git a/test/unit/applications/lang/go.py b/test/unit/applications/lang/go.py index 3db955f3..14e76362 100644 --- a/test/unit/applications/lang/go.py +++ b/test/unit/applications/lang/go.py @@ -67,7 +67,9 @@ replace unit.nginx.org/go => {replace_path} print("\n$ GOPATH=" + env['GOPATH'] + " " + " ".join(args)) try: - process = subprocess.run(args, env=env, cwd=temp_dir) + output = subprocess.check_output( + args, env=env, cwd=temp_dir, stderr=subprocess.STDOUT + ) except KeyboardInterrupt: raise @@ -75,7 +77,7 @@ replace unit.nginx.org/go => {replace_path} except subprocess.CalledProcessError: return None - return process + return output def load(self, script, name='app', **kwargs): static_build = False diff --git a/test/unit/check/go.py b/test/unit/check/go.py index 3d9d13e7..09ae641d 100644 --- a/test/unit/check/go.py +++ b/test/unit/check/go.py @@ -2,7 +2,5 @@ from unit.applications.lang.go import TestApplicationGo def check_go(): - process = TestApplicationGo.prepare_env('empty') - - if process != None and process.returncode == 0: + if TestApplicationGo.prepare_env('empty') is not None: return True -- cgit From 12e2cbae8a0bf190c8e7d98de6c08aff57d2ae4f Mon Sep 17 00:00:00 2001 From: Andrei Zeliankou Date: Mon, 12 Dec 2022 16:27:02 +0000 Subject: Tests: stop execution if can't unmount any filesystem. --- test/conftest.py | 6 ++++++ test/unit/utils.py | 11 +++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/test/conftest.py b/test/conftest.py index c2680744..4a1aa7cc 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -26,8 +26,10 @@ from unit.http import TestHTTP from unit.log import Log from unit.option import option from unit.status import Status +from unit.utils import check_findmnt from unit.utils import public_dir from unit.utils import waitforfiles +from unit.utils import waitforunmount def pytest_addoption(parser): @@ -87,6 +89,7 @@ _fds_info = { }, } http = TestHTTP() +is_findmnt = check_findmnt() def pytest_configure(config): @@ -315,6 +318,9 @@ def run(request): if not option.restart: _clear_conf(unit['temp_dir'] + '/control.unit.sock', log=log) + if is_findmnt and not waitforunmount(unit['temp_dir'], timeout=600): + exit('Could not unmount some filesystems in tmp dir.') + for item in os.listdir(unit['temp_dir']): if item not in [ 'control.unit.sock', diff --git a/test/unit/utils.py b/test/unit/utils.py index f9e9d08a..d6590b97 100644 --- a/test/unit/utils.py +++ b/test/unit/utils.py @@ -72,12 +72,19 @@ def waitforsocket(port): pytest.fail('Can\'t connect to the 127.0.0.1:' + str(port)) -def findmnt(): +def check_findmnt(): try: - out = subprocess.check_output( + return subprocess.check_output( ['findmnt', '--raw'], stderr=subprocess.STDOUT ).decode() except FileNotFoundError: + return False + + +def findmnt(): + out = check_findmnt() + + if not out: pytest.skip('requires findmnt') return out -- cgit From dad7ef9a12f2cf16f2be011f167fc19b011ffe03 Mon Sep 17 00:00:00 2001 From: Andrew Clayton Date: Wed, 4 May 2022 16:13:19 +0100 Subject: Configuration: made large_header_buffer_size a valid setting. @JanMikes and @tagur87 on GitHub both reported issues with long URLs that were exceeding the 8192 byte large_header_buffer_size setting, which resulted in a HTTP 431 error (Request Header Fields Too Large). This can be resolved in the code by updating the following line in src/nxt_router.c::nxt_router_conf_create() skcf->large_header_buffer_size = 8192; However, requiring users to modify unit and install custom versions is less than ideal. We could increase the value, but to what? This commit takes the option of allowing the user to set this option in their config by making large_header_buffer_size a valid configuration setting. large_header_buffer_size is already set by the configuration system in nxt_router.c it just isn't set as a valid config option in nxt_conf_validation.c With this change users can set this option in their config if required by the following "settings": { "http": { "large_header_buffer_size": 16384 } }, It retains its default value of 8192 bytes if this is not set. With this commit, without the above setting or too low a value, with a long URL you get a 431 error. With the above setting set to a large enough value, the request is successful. NOTE: This setting really determines the maximum size of any single header _value_. Also, unit will try and place multiple values into a buffer _if_ they fully fit. NOTE: This is being released as undocumented and subject to change as it exposes internal workings of unit. Closes: Signed-off-by: Andrew Clayton --- src/nxt_conf_validation.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index 0f22c540..218254bf 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -314,6 +314,9 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_http_members[] = { }, { .name = nxt_string("idle_timeout"), .type = NXT_CONF_VLDT_INTEGER, + }, { + .name = nxt_string("large_header_buffer_size"), + .type = NXT_CONF_VLDT_INTEGER, }, { .name = nxt_string("body_buffer_size"), .type = NXT_CONF_VLDT_INTEGER, -- cgit From f88371ff1d5173da44b8bf152e1b261e444a6eac Mon Sep 17 00:00:00 2001 From: Andrew Clayton Date: Tue, 6 Dec 2022 12:56:03 +0000 Subject: Configuration: made large_header_buffers a valid setting. This is an extension to the previous commit, which made large_header_buffer_size a valid configuration setting. This commit makes a related value, large_header_buffers, a valid configuration setting. While large_header_buffer_size effectively limits the maximum size of any single header (although unit will try to pack multiple headers into a buffer if they wholly fit). large_header_buffers limits how many of these 'large' buffers are available. It makes sense to also allow this to be user set. large_header_buffers is already set by the configuration system in nxt_router.c it just isn't set as a valid config option in nxt_conf_validation.c With this change users can set this option in their config if required by the following "settings": { "http": { "large_header_buffers": 8 } }, It retains its default value of 4 if this is not set. NOTE: This is being released as undocumented and subject to change as it exposes internal workings of unit. Signed-off-by: Andrew Clayton --- src/nxt_conf_validation.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index 218254bf..c6e63c25 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -317,6 +317,9 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_http_members[] = { }, { .name = nxt_string("large_header_buffer_size"), .type = NXT_CONF_VLDT_INTEGER, + }, { + .name = nxt_string("large_header_buffers"), + .type = NXT_CONF_VLDT_INTEGER, }, { .name = nxt_string("body_buffer_size"), .type = NXT_CONF_VLDT_INTEGER, -- cgit From 6313cffd26bf88009a871361f3da56444def0288 Mon Sep 17 00:00:00 2001 From: Andrei Zeliankou Date: Tue, 13 Dec 2022 14:51:11 +0000 Subject: Tests: added tests for the large header buffer settings. Added tests for the "large_header_buffer_size" and "large_header_buffers" configuration options. --- test/test_settings.py | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/test/test_settings.py b/test/test_settings.py index 1bc9a432..ad8929f8 100644 --- a/test/test_settings.py +++ b/test/test_settings.py @@ -10,6 +10,66 @@ from unit.utils import sysctl class TestSettings(TestApplicationPython): prerequisites = {'modules': {'python': 'any'}} + def test_settings_large_header_buffer_size(self): + self.load('empty') + + def set_buffer_size(size): + assert 'success' in self.conf( + {'http': {'large_header_buffer_size': size}}, + 'settings', + ) + + def header_value(size, expect=200): + headers = {'Host': 'a' * (size - 1), 'Connection': 'close'} + assert self.get(headers=headers)['status'] == expect + + set_buffer_size(4096) + header_value(4096) + header_value(4097, 431) + + set_buffer_size(16384) + header_value(16384) + header_value(16385, 431) + + def test_settings_large_header_buffers(self): + self.load('empty') + + def set_buffers(buffers): + assert 'success' in self.conf( + {'http': {'large_header_buffers': buffers}}, + 'settings', + ) + + def big_headers(headers_num, expect=200): + headers = {'Host': 'localhost', 'Connection': 'close'} + + for i in range(headers_num): + headers['Custom-header-' + str(i)] = 'a' * 8000 + + assert self.get(headers=headers)['status'] == expect + + set_buffers(1) + big_headers(1) + big_headers(2, 431) + + set_buffers(2) + big_headers(2) + big_headers(3, 431) + + set_buffers(8) + big_headers(8) + big_headers(9, 431) + + @pytest.mark.skip('not yet') + def test_settings_large_header_buffer_invalid(self): + def check_error(conf): + assert 'error' in self.conf({'http': conf}, 'settings') + + check_error({'large_header_buffer_size': -1}) + check_error({'large_header_buffer_size': 0}) + check_error({'large_header_buffers': -1}) + check_error({'large_header_buffers': 0}) + def test_settings_header_read_timeout(self): self.load('empty') -- cgit From 9f6d7c7ccd61ee4230d6be550a5123bb0f39b7ba Mon Sep 17 00:00:00 2001 From: Konstantin Pavlov Date: Wed, 7 Dec 2022 18:31:48 -0800 Subject: Docker: bumped language versions. --- pkg/docker/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/docker/Makefile b/pkg/docker/Makefile index 03723d1d..b08e885f 100644 --- a/pkg/docker/Makefile +++ b/pkg/docker/Makefile @@ -34,7 +34,7 @@ CONFIGURE_jsc ?= java --jars=/usr/share/unit-jsc-common/ INSTALL_jsc ?= java-shared-install java-install COPY_jsc = COPY --from=BUILDER /usr/share/unit-jsc-common/ /usr/share/unit-jsc-common/ -VERSION_node ?= 16 +VERSION_node ?= 18 CONTAINER_node ?= node:$(VERSION_node) CONFIGURE_node ?= nodejs --node-gyp=/usr/local/lib/node_modules/npm/bin/node-gyp-bin/node-gyp INSTALL_node ?= node node-install libunit-install @@ -56,7 +56,7 @@ CONFIGURE_php ?= php INSTALL_php ?= php-install COPY_php = RUN ldconfig -VERSION_python ?= 3.10 +VERSION_python ?= 3.11 CONTAINER_python ?= python:$(VERSION_python) CONFIGURE_python ?= python --config=/usr/local/bin/python3-config INSTALL_python ?= python3-install -- cgit From f09d0e22847b2d2dab79f782c55067c596e38fb1 Mon Sep 17 00:00:00 2001 From: Konstantin Pavlov Date: Tue, 13 Dec 2022 11:50:42 -0800 Subject: Regenerated Dockerfiles. --- pkg/docker/Dockerfile.node16 | 79 ---------------------------------------- pkg/docker/Dockerfile.node18 | 79 ++++++++++++++++++++++++++++++++++++++++ pkg/docker/Dockerfile.python3.10 | 77 --------------------------------------- pkg/docker/Dockerfile.python3.11 | 77 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 156 insertions(+), 156 deletions(-) delete mode 100644 pkg/docker/Dockerfile.node16 create mode 100644 pkg/docker/Dockerfile.node18 delete mode 100644 pkg/docker/Dockerfile.python3.10 create mode 100644 pkg/docker/Dockerfile.python3.11 diff --git a/pkg/docker/Dockerfile.node16 b/pkg/docker/Dockerfile.node16 deleted file mode 100644 index d341e43f..00000000 --- a/pkg/docker/Dockerfile.node16 +++ /dev/null @@ -1,79 +0,0 @@ -FROM node:16 as BUILDER - -LABEL maintainer="NGINX Docker Maintainers " - -RUN set -ex \ - && apt-get update \ - && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates mercurial build-essential libssl-dev libpcre2-dev \ - && mkdir -p /usr/lib/unit/modules /usr/lib/unit/debug-modules \ - && hg clone https://hg.nginx.org/unit \ - && cd unit \ - && hg up 1.28.0 \ - && NCPU="$(getconf _NPROCESSORS_ONLN)" \ - && DEB_HOST_MULTIARCH="$(dpkg-architecture -q DEB_HOST_MULTIARCH)" \ - && CC_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_CFLAGS_MAINT_APPEND="-Wp,-D_FORTIFY_SOURCE=2 -fPIC" dpkg-buildflags --get CFLAGS)" \ - && LD_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_LDFLAGS_MAINT_APPEND="-Wl,--as-needed -pie" dpkg-buildflags --get LDFLAGS)" \ - && CONFIGURE_ARGS="--prefix=/usr \ - --state=/var/lib/unit \ - --control=unix:/var/run/control.unit.sock \ - --pid=/var/run/unit.pid \ - --log=/var/log/unit.log \ - --tmp=/var/tmp \ - --user=unit \ - --group=unit \ - --openssl \ - --libdir=/usr/lib/$DEB_HOST_MULTIARCH" \ - && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --ld-opt="$LD_OPT" --modules=/usr/lib/unit/debug-modules --debug \ - && make -j $NCPU unitd \ - && install -pm755 build/unitd /usr/sbin/unitd-debug \ - && make clean \ - && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --ld-opt="$LD_OPT" --modules=/usr/lib/unit/modules \ - && make -j $NCPU unitd \ - && install -pm755 build/unitd /usr/sbin/unitd \ - && make clean \ - && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --modules=/usr/lib/unit/debug-modules --debug \ - && ./configure nodejs --node-gyp=/usr/local/lib/node_modules/npm/bin/node-gyp-bin/node-gyp \ - && make -j $NCPU node node-install libunit-install \ - && make clean \ - && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --modules=/usr/lib/unit/modules \ - && ./configure nodejs --node-gyp=/usr/local/lib/node_modules/npm/bin/node-gyp-bin/node-gyp \ - && make -j $NCPU node node-install libunit-install \ - && ldd /usr/sbin/unitd | awk '/=>/{print $(NF-1)}' | while read n; do dpkg-query -S $n; done | sed 's/^\([^:]\+\):.*$/\1/' | sort | uniq > /requirements.apt - -FROM node:16 -COPY docker-entrypoint.sh /usr/local/bin/ -COPY --from=BUILDER /usr/sbin/unitd /usr/sbin/unitd -COPY --from=BUILDER /usr/sbin/unitd-debug /usr/sbin/unitd-debug -COPY --from=BUILDER /usr/lib/unit/ /usr/lib/unit/ -COPY --from=BUILDER /requirements.apt /requirements.apt -COPY --from=BUILDER /usr/lib/*-linux-gnu/libunit.a /tmp/ -COPY --from=BUILDER /usr/include/nxt_* /usr/include/ -COPY --from=BUILDER /usr/local/lib/node_modules/unit-http/ /usr/local/lib/node_modules/unit-http/ -RUN set -x \ - && if [ -f "/tmp/libunit.a" ]; then \ - mv /tmp/libunit.a /usr/lib/$(dpkg-architecture -q DEB_HOST_MULTIARCH)/libunit.a; \ - rm -f /tmp/libunit.a; \ - fi \ - && mkdir -p /var/lib/unit/ \ - && mkdir /docker-entrypoint.d/ \ - && addgroup --system unit \ - && adduser \ - --system \ - --disabled-login \ - --ingroup unit \ - --no-create-home \ - --home /nonexistent \ - --gecos "unit user" \ - --shell /bin/false \ - unit \ - && apt update \ - && apt --no-install-recommends --no-install-suggests -y install curl $(cat /requirements.apt) \ - && apt-get clean && rm -rf /var/lib/apt/lists/* \ - && rm -f /requirements.apt \ - && ln -sf /dev/stdout /var/log/unit.log - -STOPSIGNAL SIGTERM - -ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"] - -CMD ["unitd", "--no-daemon", "--control", "unix:/var/run/control.unit.sock"] diff --git a/pkg/docker/Dockerfile.node18 b/pkg/docker/Dockerfile.node18 new file mode 100644 index 00000000..9a474564 --- /dev/null +++ b/pkg/docker/Dockerfile.node18 @@ -0,0 +1,79 @@ +FROM node:18 as BUILDER + +LABEL maintainer="NGINX Docker Maintainers " + +RUN set -ex \ + && apt-get update \ + && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates mercurial build-essential libssl-dev libpcre2-dev \ + && mkdir -p /usr/lib/unit/modules /usr/lib/unit/debug-modules \ + && hg clone https://hg.nginx.org/unit \ + && cd unit \ + && hg up 1.28.0 \ + && NCPU="$(getconf _NPROCESSORS_ONLN)" \ + && DEB_HOST_MULTIARCH="$(dpkg-architecture -q DEB_HOST_MULTIARCH)" \ + && CC_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_CFLAGS_MAINT_APPEND="-Wp,-D_FORTIFY_SOURCE=2 -fPIC" dpkg-buildflags --get CFLAGS)" \ + && LD_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_LDFLAGS_MAINT_APPEND="-Wl,--as-needed -pie" dpkg-buildflags --get LDFLAGS)" \ + && CONFIGURE_ARGS="--prefix=/usr \ + --state=/var/lib/unit \ + --control=unix:/var/run/control.unit.sock \ + --pid=/var/run/unit.pid \ + --log=/var/log/unit.log \ + --tmp=/var/tmp \ + --user=unit \ + --group=unit \ + --openssl \ + --libdir=/usr/lib/$DEB_HOST_MULTIARCH" \ + && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --ld-opt="$LD_OPT" --modules=/usr/lib/unit/debug-modules --debug \ + && make -j $NCPU unitd \ + && install -pm755 build/unitd /usr/sbin/unitd-debug \ + && make clean \ + && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --ld-opt="$LD_OPT" --modules=/usr/lib/unit/modules \ + && make -j $NCPU unitd \ + && install -pm755 build/unitd /usr/sbin/unitd \ + && make clean \ + && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --modules=/usr/lib/unit/debug-modules --debug \ + && ./configure nodejs --node-gyp=/usr/local/lib/node_modules/npm/bin/node-gyp-bin/node-gyp \ + && make -j $NCPU node node-install libunit-install \ + && make clean \ + && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --modules=/usr/lib/unit/modules \ + && ./configure nodejs --node-gyp=/usr/local/lib/node_modules/npm/bin/node-gyp-bin/node-gyp \ + && make -j $NCPU node node-install libunit-install \ + && ldd /usr/sbin/unitd | awk '/=>/{print $(NF-1)}' | while read n; do dpkg-query -S $n; done | sed 's/^\([^:]\+\):.*$/\1/' | sort | uniq > /requirements.apt + +FROM node:18 +COPY docker-entrypoint.sh /usr/local/bin/ +COPY --from=BUILDER /usr/sbin/unitd /usr/sbin/unitd +COPY --from=BUILDER /usr/sbin/unitd-debug /usr/sbin/unitd-debug +COPY --from=BUILDER /usr/lib/unit/ /usr/lib/unit/ +COPY --from=BUILDER /requirements.apt /requirements.apt +COPY --from=BUILDER /usr/lib/*-linux-gnu/libunit.a /tmp/ +COPY --from=BUILDER /usr/include/nxt_* /usr/include/ +COPY --from=BUILDER /usr/local/lib/node_modules/unit-http/ /usr/local/lib/node_modules/unit-http/ +RUN set -x \ + && if [ -f "/tmp/libunit.a" ]; then \ + mv /tmp/libunit.a /usr/lib/$(dpkg-architecture -q DEB_HOST_MULTIARCH)/libunit.a; \ + rm -f /tmp/libunit.a; \ + fi \ + && mkdir -p /var/lib/unit/ \ + && mkdir /docker-entrypoint.d/ \ + && addgroup --system unit \ + && adduser \ + --system \ + --disabled-login \ + --ingroup unit \ + --no-create-home \ + --home /nonexistent \ + --gecos "unit user" \ + --shell /bin/false \ + unit \ + && apt update \ + && apt --no-install-recommends --no-install-suggests -y install curl $(cat /requirements.apt) \ + && apt-get clean && rm -rf /var/lib/apt/lists/* \ + && rm -f /requirements.apt \ + && ln -sf /dev/stdout /var/log/unit.log + +STOPSIGNAL SIGTERM + +ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"] + +CMD ["unitd", "--no-daemon", "--control", "unix:/var/run/control.unit.sock"] diff --git a/pkg/docker/Dockerfile.python3.10 b/pkg/docker/Dockerfile.python3.10 deleted file mode 100644 index 6502f8a8..00000000 --- a/pkg/docker/Dockerfile.python3.10 +++ /dev/null @@ -1,77 +0,0 @@ -FROM python:3.10 as BUILDER - -LABEL maintainer="NGINX Docker Maintainers " - -RUN set -ex \ - && apt-get update \ - && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates mercurial build-essential libssl-dev libpcre2-dev \ - && mkdir -p /usr/lib/unit/modules /usr/lib/unit/debug-modules \ - && hg clone https://hg.nginx.org/unit \ - && cd unit \ - && hg up 1.28.0 \ - && NCPU="$(getconf _NPROCESSORS_ONLN)" \ - && DEB_HOST_MULTIARCH="$(dpkg-architecture -q DEB_HOST_MULTIARCH)" \ - && CC_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_CFLAGS_MAINT_APPEND="-Wp,-D_FORTIFY_SOURCE=2 -fPIC" dpkg-buildflags --get CFLAGS)" \ - && LD_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_LDFLAGS_MAINT_APPEND="-Wl,--as-needed -pie" dpkg-buildflags --get LDFLAGS)" \ - && CONFIGURE_ARGS="--prefix=/usr \ - --state=/var/lib/unit \ - --control=unix:/var/run/control.unit.sock \ - --pid=/var/run/unit.pid \ - --log=/var/log/unit.log \ - --tmp=/var/tmp \ - --user=unit \ - --group=unit \ - --openssl \ - --libdir=/usr/lib/$DEB_HOST_MULTIARCH" \ - && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --ld-opt="$LD_OPT" --modules=/usr/lib/unit/debug-modules --debug \ - && make -j $NCPU unitd \ - && install -pm755 build/unitd /usr/sbin/unitd-debug \ - && make clean \ - && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --ld-opt="$LD_OPT" --modules=/usr/lib/unit/modules \ - && make -j $NCPU unitd \ - && install -pm755 build/unitd /usr/sbin/unitd \ - && make clean \ - && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --modules=/usr/lib/unit/debug-modules --debug \ - && ./configure python --config=/usr/local/bin/python3-config \ - && make -j $NCPU python3-install \ - && make clean \ - && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --modules=/usr/lib/unit/modules \ - && ./configure python --config=/usr/local/bin/python3-config \ - && make -j $NCPU python3-install \ - && ldd /usr/sbin/unitd | awk '/=>/{print $(NF-1)}' | while read n; do dpkg-query -S $n; done | sed 's/^\([^:]\+\):.*$/\1/' | sort | uniq > /requirements.apt - -FROM python:3.10 -COPY docker-entrypoint.sh /usr/local/bin/ -COPY --from=BUILDER /usr/sbin/unitd /usr/sbin/unitd -COPY --from=BUILDER /usr/sbin/unitd-debug /usr/sbin/unitd-debug -COPY --from=BUILDER /usr/lib/unit/ /usr/lib/unit/ -COPY --from=BUILDER /requirements.apt /requirements.apt - -RUN set -x \ - && if [ -f "/tmp/libunit.a" ]; then \ - mv /tmp/libunit.a /usr/lib/$(dpkg-architecture -q DEB_HOST_MULTIARCH)/libunit.a; \ - rm -f /tmp/libunit.a; \ - fi \ - && mkdir -p /var/lib/unit/ \ - && mkdir /docker-entrypoint.d/ \ - && addgroup --system unit \ - && adduser \ - --system \ - --disabled-login \ - --ingroup unit \ - --no-create-home \ - --home /nonexistent \ - --gecos "unit user" \ - --shell /bin/false \ - unit \ - && apt update \ - && apt --no-install-recommends --no-install-suggests -y install curl $(cat /requirements.apt) \ - && apt-get clean && rm -rf /var/lib/apt/lists/* \ - && rm -f /requirements.apt \ - && ln -sf /dev/stdout /var/log/unit.log - -STOPSIGNAL SIGTERM - -ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"] - -CMD ["unitd", "--no-daemon", "--control", "unix:/var/run/control.unit.sock"] diff --git a/pkg/docker/Dockerfile.python3.11 b/pkg/docker/Dockerfile.python3.11 new file mode 100644 index 00000000..73f8614c --- /dev/null +++ b/pkg/docker/Dockerfile.python3.11 @@ -0,0 +1,77 @@ +FROM python:3.11 as BUILDER + +LABEL maintainer="NGINX Docker Maintainers " + +RUN set -ex \ + && apt-get update \ + && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates mercurial build-essential libssl-dev libpcre2-dev \ + && mkdir -p /usr/lib/unit/modules /usr/lib/unit/debug-modules \ + && hg clone https://hg.nginx.org/unit \ + && cd unit \ + && hg up 1.28.0 \ + && NCPU="$(getconf _NPROCESSORS_ONLN)" \ + && DEB_HOST_MULTIARCH="$(dpkg-architecture -q DEB_HOST_MULTIARCH)" \ + && CC_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_CFLAGS_MAINT_APPEND="-Wp,-D_FORTIFY_SOURCE=2 -fPIC" dpkg-buildflags --get CFLAGS)" \ + && LD_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_LDFLAGS_MAINT_APPEND="-Wl,--as-needed -pie" dpkg-buildflags --get LDFLAGS)" \ + && CONFIGURE_ARGS="--prefix=/usr \ + --state=/var/lib/unit \ + --control=unix:/var/run/control.unit.sock \ + --pid=/var/run/unit.pid \ + --log=/var/log/unit.log \ + --tmp=/var/tmp \ + --user=unit \ + --group=unit \ + --openssl \ + --libdir=/usr/lib/$DEB_HOST_MULTIARCH" \ + && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --ld-opt="$LD_OPT" --modules=/usr/lib/unit/debug-modules --debug \ + && make -j $NCPU unitd \ + && install -pm755 build/unitd /usr/sbin/unitd-debug \ + && make clean \ + && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --ld-opt="$LD_OPT" --modules=/usr/lib/unit/modules \ + && make -j $NCPU unitd \ + && install -pm755 build/unitd /usr/sbin/unitd \ + && make clean \ + && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --modules=/usr/lib/unit/debug-modules --debug \ + && ./configure python --config=/usr/local/bin/python3-config \ + && make -j $NCPU python3-install \ + && make clean \ + && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --modules=/usr/lib/unit/modules \ + && ./configure python --config=/usr/local/bin/python3-config \ + && make -j $NCPU python3-install \ + && ldd /usr/sbin/unitd | awk '/=>/{print $(NF-1)}' | while read n; do dpkg-query -S $n; done | sed 's/^\([^:]\+\):.*$/\1/' | sort | uniq > /requirements.apt + +FROM python:3.11 +COPY docker-entrypoint.sh /usr/local/bin/ +COPY --from=BUILDER /usr/sbin/unitd /usr/sbin/unitd +COPY --from=BUILDER /usr/sbin/unitd-debug /usr/sbin/unitd-debug +COPY --from=BUILDER /usr/lib/unit/ /usr/lib/unit/ +COPY --from=BUILDER /requirements.apt /requirements.apt + +RUN set -x \ + && if [ -f "/tmp/libunit.a" ]; then \ + mv /tmp/libunit.a /usr/lib/$(dpkg-architecture -q DEB_HOST_MULTIARCH)/libunit.a; \ + rm -f /tmp/libunit.a; \ + fi \ + && mkdir -p /var/lib/unit/ \ + && mkdir /docker-entrypoint.d/ \ + && addgroup --system unit \ + && adduser \ + --system \ + --disabled-login \ + --ingroup unit \ + --no-create-home \ + --home /nonexistent \ + --gecos "unit user" \ + --shell /bin/false \ + unit \ + && apt update \ + && apt --no-install-recommends --no-install-suggests -y install curl $(cat /requirements.apt) \ + && apt-get clean && rm -rf /var/lib/apt/lists/* \ + && rm -f /requirements.apt \ + && ln -sf /dev/stdout /var/log/unit.log + +STOPSIGNAL SIGTERM + +ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"] + +CMD ["unitd", "--no-daemon", "--control", "unix:/var/run/control.unit.sock"] -- cgit From 63cc4a31bdea2601c8adc08194759657f1476b16 Mon Sep 17 00:00:00 2001 From: Konstantin Pavlov Date: Tue, 13 Dec 2022 13:36:39 -0800 Subject: Docker: limited the waiting time for control socket removal. Fixes https://github.com/nginx/unit/issues/728 Refs https://github.com/nginx/unit/issues/718 --- pkg/docker/docker-entrypoint.sh | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/pkg/docker/docker-entrypoint.sh b/pkg/docker/docker-entrypoint.sh index 7f65d94a..3d134ea2 100755 --- a/pkg/docker/docker-entrypoint.sh +++ b/pkg/docker/docker-entrypoint.sh @@ -2,6 +2,9 @@ set -e +WAITLOOPS=5 +SLEEPSEC=1 + curl_put() { RET=$(/usr/bin/curl -s -w '%{http_code}' -X PUT --data-binary @$1 --unix-socket /var/run/control.unit.sock http://localhost/$2) @@ -57,7 +60,18 @@ if [ "$1" = "unitd" ] || [ "$1" = "unitd-debug" ]; then echo "$0: Stopping Unit daemon after initial configuration..." kill -TERM $(/bin/cat /var/run/unit.pid) - while [ -S /var/run/control.unit.sock ]; do echo "$0: Waiting for control socket to be removed..."; /bin/sleep 0.1; done + for i in $(/usr/bin/seq $WAITLOOPS); do + if [ -S /var/run/control.unit.sock ]; then + echo "$0 Waiting for control socket to be removed..." + /bin/sleep $SLEEPSEC + else + break + fi + done + if [ -S /var/run/control.unit.sock ]; then + kill -KILL $(/bin/cat /var/run/unit.pid) + rm -f /var/run/control.unit.sock + fi echo echo "$0: Unit initial configuration complete; ready for start up..." -- cgit From c9c001ee16091c76773d3e9e655d777696dd755a Mon Sep 17 00:00:00 2001 From: "Sergey A. Osokin" Date: Wed, 14 Dec 2022 01:43:24 +0000 Subject: Java: upgrading third-party components. --- auto/modules/java | 6 +++--- auto/modules/java_jar.sha512 | 26 +++++++++++++------------- test/test_java_application.py | 17 ++++++++++------- test/unit/applications/lang/java.py | 2 +- 4 files changed, 27 insertions(+), 24 deletions(-) diff --git a/auto/modules/java b/auto/modules/java index 691060b3..bdf17022 100644 --- a/auto/modules/java +++ b/auto/modules/java @@ -238,7 +238,7 @@ cat << END > $NXT_JAVA_JARS static const char *nxt_java_system_jars[] = { END -NXT_TOMCAT_VERSION=9.0.52 +NXT_TOMCAT_VERSION=9.0.70 NXT_JAR_VERSION=$NXT_TOMCAT_VERSION @@ -284,7 +284,7 @@ static const char *nxt_java_unit_jars[] = { "$NXT_UNIT_JAR", END -NXT_JAR_VERSION=9.4.43.v20210629 +NXT_JAR_VERSION=9.4.49.v20220914 NXT_JAR_NAMESPACE=org/eclipse/jetty/ NXT_JAR_NAME=jetty-util @@ -297,7 +297,7 @@ NXT_JAR_NAME=jetty-http . auto/modules/java_get_jar NXT_JAR_NAME=classgraph -NXT_JAR_VERSION=4.8.112 +NXT_JAR_VERSION=4.8.151 NXT_JAR_NAMESPACE=io/github/classgraph/ . auto/modules/java_get_jar diff --git a/auto/modules/java_jar.sha512 b/auto/modules/java_jar.sha512 index 5289081c..d3e9016c 100644 --- a/auto/modules/java_jar.sha512 +++ b/auto/modules/java_jar.sha512 @@ -1,14 +1,14 @@ -e7ad5ee436f6befaddcdd1046022a2e30444a435d9419a33f8316f66e794cf710809dbcf7e408a087f434cd9cb724682965b5e4098e34803569241eb44288322 classgraph-4.8.112.jar +4b47eabc83f3f672a7e91af6ae97bbdbc6f01ed7149540cb06b0f530f45a95d025cc7807a6640982d23d2da50bd973ad788a4c8fdfa025da7cf93c560abbe61e classgraph-4.8.151.jar ab441acf5551a7dc81c353eaccb3b3df9e89a48987294d19e39acdb83a5b640fcdff7414cee29f5b96eaa8826647f1d5323e185018fe33a64c402d69c73c9158 ecj-3.26.0.jar -a3ce1a5a41c9791ece4cbbf049ec4add1ec41330743d6757daea520f8b329299f5dd274f9e5741ba41fe49510f203efd19540d2404390eca53421132f5f46d4b jetty-http-9.4.43.v20210629.jar -61a14e97baac9962bd68ece24f8b967eec8e32edfebfa27c6a13996a86650d82f8977bf1aa582fc9706a1b028cb3cec0057c97628235dfc130061939845229e6 jetty-server-9.4.43.v20210629.jar -304fcdba2bdbf37e8f2ea69a3f5fbdffdfefd98d80fa78883b1dca1129a4907cef63eb2fa7c83eef359022a3b6a2f3ff742d8d23075c83d049ac01f1402e97f8 jetty-util-9.4.43.v20210629.jar -f14ac948559c0b6e20f6d84e5177fea46ea1321a7a401f280ee9323f3a07e70e141d2b12c8c466c446efb55a58743af931b0584f56494f17311cab511bcd214a tomcat-api-9.0.52.jar -a5ca293732267854a296ccc79b25051acf76fa8dea6118d43aa2e95b6d1951dfaffb941430b5080d7ab62d82d2c82c9385baf99e3c4a2bb6bf4a04372149169d tomcat-el-api-9.0.52.jar -8660e11dd0f994de43b19ba251a789dc3194a6b82d674085fed510200c789b402b27ab97bcecfec0720f563bb0dd18c2631cd8bb5c35e604c1460d7357492123 tomcat-jasper-9.0.52.jar -5b0b3e0edb47e3c4269736542d66d6fc099a74965fdcd5d3a6382db3f75bec7329e81f0719aaafccd318a058ec8fbba113a6ae9234ca94a00c8c39e5c8885568 tomcat-jasper-el-9.0.52.jar -a4368d1073d79a4f8a1cb8967a5e39af87723a17b2d94466554e8e4d3e8bb2dec3ee37db9f606e0c775dd4028604d4e87921f0dda764c8ef138aa50acf03d549 tomcat-jsp-api-9.0.52.jar -8687e108489996226a83e8310c73a2a176fac9ce365a7bd3fc23955fe968c3858a24c047cb5c7fbd1f3a890c893dcdf55e88180eefe61b98c1a3bf4e316fb07e tomcat-juli-9.0.52.jar -f9929f433e2b2f93897a87d117af2519e44020b44e3a475dfc81662b08d08e010b14a3dd6df2d4800196cdba7cbb8db2b879341c5a0ef1d11e5abe63d872bc34 tomcat-servlet-api-9.0.52.jar -c21ccf969378f2cad0ead32451c2527ea944207b5a894b642ee554042fe87eb0ce647aacbf8a51d12b4ecf2bf13e9380da78d8f7792486909daba72e8d0f83f2 tomcat-util-9.0.52.jar -14b4eb31c124d22c7ea7f05808cd6a46076f9d72648afd76e2d11924874117266771a455686d704225d2eff94656f024293140a3259b108857fa6b8b218ddd63 tomcat-util-scan-9.0.52.jar +82c6985f0d7c76459bf0638fdc24f3692a11804a95845b1a45203dfcc1205ab7bf67934f6babf7eb2f2b87d637a8fcbd87eae297e4403511bf73f359b1957e09 jetty-http-9.4.49.v20220914.jar +2f199729ad9b46fda968b4bfafd657971fc9d90371852f8ad7afdae6d5752d2b84648734eabb6ffbf084800253d1da97d4bb9ad60f799ee6ae38a80c2d881fc4 jetty-server-9.4.49.v20220914.jar +e207d93ef5bc98ad2b1a43393231bdacfb3ab642b6197a8b72d819f8ad30357c4daa0a76a0459340563fcdee0fdfc111e719a2db5be778d6b1e10f1ccbe77fc9 jetty-util-9.4.49.v20220914.jar +a2cd93ccaa58191475df9aa40a11c8b3f14f77e78b6b2dc9e5fbebf07297e318d60c5cc5aca37e61bd748456b01491a0e6702b9e4d3ec3ef43d9b1a93f9b733e tomcat-api-9.0.70.jar +4b2b33f6bdcb3fbff6de7da6f7558e4a21335c5c08dbc2adba1be90ddcaa4be1ba053d9021a4891edef975759a562b46a58da6c5acc2209ae8b942e4058b7022 tomcat-el-api-9.0.70.jar +7ee837f218220022bf2543e4b3191c0a948c7f8bbd4f2e7202cc29196e5f4a8264aee027bc3521b79775b1ab0b3f8a4bef8982be9c0b2c5f95b77f36d5e5930f tomcat-jasper-9.0.70.jar +f92cdddd3aae8d1b0b861afc67344fc6544c413d78e2e810f804632e68a3667b2b1929ac4995b582af03774ad024632e820143cd53273e06a796484ce2f0a73e tomcat-jasper-el-9.0.70.jar +5ec6985740e7a5873f56430b1f0fd6e55a625fac8f5618d846072117f5ed8ccc69665fd6ebde40381099cf42ab9525f5da3cd16dd0b50a267734bfdf7f2e168d tomcat-jsp-api-9.0.70.jar +33cf08f10bad572c9e7085b3ba8e91b38a293f8838a39483b01d07d9c1b9d0e67492343e0523da24af47782ec4a5d639db49679d951ccbe1da9d1309346cc693 tomcat-juli-9.0.70.jar +0c8ee46dc49828720cd431e4e6bcb2a9d7409b3bae3d3427640b159985a27de22181151c8fa15a1f44f607730977c4ae2512c63a19c070b92e38438ad0ba8138 tomcat-servlet-api-9.0.70.jar +e882c47acdb9e5612a0810503cb8900570b68aec5dd33dd6439884b15723a67cbf982c9cf546e7cd6d67b731df3d64ec5347500ab8a987d7cb1e11a74f819325 tomcat-util-9.0.70.jar +0a562e8a40e406966ae2be5587dcad0ceae3143b03ef9b9f7dd77c6a2db522c31ed82b9c38b4464f9f80c1d8ca418ce6a09f9fecb3e0209a962da01e2f9bd626 tomcat-util-scan-9.0.70.jar diff --git a/test/test_java_application.py b/test/test_java_application.py index b4dbff59..b825d925 100644 --- a/test/test_java_application.py +++ b/test/test_java_application.py @@ -71,6 +71,11 @@ class TestJavaApplication(TestApplicationJava): def test_java_application_get_variables(self): self.load('get_params') + def check_header(header, expect): + values = header.split(' ')[:-1] + assert len(values) == len(expect) + assert set(values) == set(expect) + headers = self.get(url='/?var1=val1&var2=&var4=val4&var4=foo')[ 'headers' ] @@ -79,13 +84,11 @@ class TestJavaApplication(TestApplicationJava): assert headers['X-Var-2'] == 'true', 'GET variables 2' assert headers['X-Var-3'] == 'false', 'GET variables 3' - assert ( - headers['X-Param-Names'] == 'var4 var2 var1 ' - ), 'getParameterNames' - assert headers['X-Param-Values'] == 'val4 foo ', 'getParameterValues' - assert ( - headers['X-Param-Map'] == 'var2= var1=val1 var4=val4,foo ' - ), 'getParameterMap' + check_header(headers['X-Param-Names'], ['var4', 'var2', 'var1']) + check_header(headers['X-Param-Values'], ['val4', 'foo']) + check_header( + headers['X-Param-Map'], ['var2=', 'var1=val1', 'var4=val4,foo'] + ) def test_java_application_post_variables(self): self.load('post_params') diff --git a/test/unit/applications/lang/java.py b/test/unit/applications/lang/java.py index 50998978..c8936274 100644 --- a/test/unit/applications/lang/java.py +++ b/test/unit/applications/lang/java.py @@ -52,7 +52,7 @@ class TestApplicationJava(TestApplicationProto): os.makedirs(classes_path) classpath = ( - option.current_dir + '/build/tomcat-servlet-api-9.0.52.jar' + option.current_dir + '/build/tomcat-servlet-api-9.0.70.jar' ) ws_jars = glob.glob( -- cgit From 7a81d9d61d0c4e3cc7e6b74b6be367cbf26ea2ab Mon Sep 17 00:00:00 2001 From: OutOfFocus4 Date: Tue, 22 Nov 2022 15:03:59 -0500 Subject: Removed dead code. Signed-off-by: Alejandro Colomar --- src/nxt_application.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/nxt_application.h b/src/nxt_application.h index 239f357b..4d624448 100644 --- a/src/nxt_application.h +++ b/src/nxt_application.h @@ -49,7 +49,6 @@ typedef struct { typedef struct { char *home; nxt_conf_value_t *path; - char *callable; nxt_str_t protocol; uint32_t threads; uint32_t thread_stack_size; -- cgit From 6dae517ebd20baa2066541e703d6aa594326dd69 Mon Sep 17 00:00:00 2001 From: OutOfFocus4 Date: Sun, 14 Nov 2021 10:47:07 -0500 Subject: Python: Added "prefix" to configuration. This patch gives users the option to set a `"prefix"` attribute for Python applications, either at the top level or for specific `"target"`s. If the attribute is present, the value of `"prefix"` must be a string beginning with `"/"`. If the value of the `"prefix"` attribute is longer than 1 character and ends in `"/"`, the trailing `"/"` is stripped. The purpose of the `"prefix"` attribute is to set the `SCRIPT_NAME` context value for WSGI applications and the `root_path` context value for ASGI applications, allowing applications to properly route requests regardless of the path that the server uses to expose the application. The context value is only set if the request's URL path begins with the value of the `"prefix"` attribute. In all other cases, the `SCRIPT_NAME` or `root_path` values are not set. In addition, for WSGI applications, the value of `"prefix"` will be stripped from the beginning of the request's URL path before it is sent to the application. Reviewed-by: Andrei Zeliankou Reviewed-by: Artem Konev Signed-off-by: Alejandro Colomar --- docs/changes.xml | 7 +++ src/nxt_conf_validation.c | 37 +++++++++++++++ src/python/nxt_python.c | 60 +++++++++++++++++++++++- src/python/nxt_python.h | 2 + src/python/nxt_python_asgi.c | 28 +++++++++--- src/python/nxt_python_asgi_str.c | 2 +- src/python/nxt_python_wsgi.c | 49 ++++++++++++++------ test/python/prefix/asgi.py | 15 ++++++ test/python/prefix/wsgi.py | 10 ++++ test/python/targets/asgi.py | 17 +++++++ test/python/targets/wsgi.py | 9 ++++ test/test_asgi_application.py | 37 +++++++++++++++ test/test_asgi_targets.py | 45 ++++++++++++++++++ test/test_configuration.py | 86 +++++++++++++++++++++++++++++++++++ test/test_python_application.py | 38 ++++++++++++++++ test/test_python_targets.py | 52 +++++++++++++++++++++ test/unit/applications/lang/python.py | 1 + 17 files changed, 472 insertions(+), 23 deletions(-) create mode 100644 test/python/prefix/asgi.py create mode 100644 test/python/prefix/wsgi.py diff --git a/docs/changes.xml b/docs/changes.xml index 3a24f9ca..80febc54 100644 --- a/docs/changes.xml +++ b/docs/changes.xml @@ -111,6 +111,13 @@ report the regex status in configure summary. + + +new "prefix" attribute in Python configurations to set WSGI SCRIPT_NAME +and ASGI root-path variables. + + + fix HTTP cookie parsing when the value contains an equals sign. diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index c6e63c25..bf8aa760 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -128,6 +128,8 @@ static nxt_int_t nxt_conf_vldt_python_path_element(nxt_conf_validation_t *vldt, nxt_conf_value_t *value); static nxt_int_t nxt_conf_vldt_python_protocol(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data); +static nxt_int_t nxt_conf_vldt_python_prefix(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value, void *data); static nxt_int_t nxt_conf_vldt_threads(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data); static nxt_int_t nxt_conf_vldt_thread_stack_size(nxt_conf_validation_t *vldt, @@ -795,6 +797,11 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_python_members[] = { .type = NXT_CONF_VLDT_STRING, .validator = nxt_conf_vldt_targets_exclusive, .u.string = "callable", + }, { + .name = nxt_string("prefix"), + .type = NXT_CONF_VLDT_STRING, + .validator = nxt_conf_vldt_targets_exclusive, + .u.string = "prefix", }, { .name = nxt_string("targets"), .type = NXT_CONF_VLDT_OBJECT, @@ -814,6 +821,10 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_python_target_members[] = { }, { .name = nxt_string("callable"), .type = NXT_CONF_VLDT_STRING, + }, { + .name = nxt_string("prefix"), + .type = NXT_CONF_VLDT_STRING, + .validator = nxt_conf_vldt_python_prefix, }, NXT_CONF_VLDT_END @@ -828,6 +839,10 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_python_notargets_members[] = { }, { .name = nxt_string("callable"), .type = NXT_CONF_VLDT_STRING, + }, { + .name = nxt_string("prefix"), + .type = NXT_CONF_VLDT_STRING, + .validator = nxt_conf_vldt_python_prefix, }, NXT_CONF_VLDT_NEXT(nxt_conf_vldt_python_common_members) @@ -1870,6 +1885,28 @@ nxt_conf_vldt_python_protocol(nxt_conf_validation_t *vldt, } +static nxt_int_t +nxt_conf_vldt_python_prefix(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value, void *data) +{ + nxt_str_t prefix; + + if (nxt_conf_type(value) != NXT_CONF_STRING) { + return nxt_conf_vldt_error(vldt, "The \"prefix\" must be a string " + "beginning with \"/\"."); + } + + nxt_conf_get_string(value, &prefix); + + if (!nxt_strchr_start(&prefix, '/')) { + return nxt_conf_vldt_error(vldt, "The \"prefix\" must be a string " + "beginning with \"/\"."); + } + + return NXT_OK; +} + + static nxt_int_t nxt_conf_vldt_threads(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data) diff --git a/src/python/nxt_python.c b/src/python/nxt_python.c index 8983815c..bdb04579 100644 --- a/src/python/nxt_python.c +++ b/src/python/nxt_python.c @@ -30,6 +30,8 @@ static nxt_int_t nxt_python_start(nxt_task_t *task, nxt_process_data_t *data); static nxt_int_t nxt_python_set_target(nxt_task_t *task, nxt_python_target_t *target, nxt_conf_value_t *conf); +nxt_inline nxt_int_t nxt_python_set_prefix(nxt_task_t *task, + nxt_python_target_t *target, nxt_conf_value_t *value); static nxt_int_t nxt_python_set_path(nxt_task_t *task, nxt_conf_value_t *value); static int nxt_python_init_threads(nxt_python_app_conf_t *c); static int nxt_python_ready_handler(nxt_unit_ctx_t *ctx); @@ -389,6 +391,7 @@ nxt_python_set_target(nxt_task_t *task, nxt_python_target_t *target, static nxt_str_t module_str = nxt_string("module"); static nxt_str_t callable_str = nxt_string("callable"); + static nxt_str_t prefix_str = nxt_string("prefix"); module = obj = NULL; @@ -436,6 +439,11 @@ nxt_python_set_target(nxt_task_t *task, nxt_python_target_t *target, goto fail; } + value = nxt_conf_get_object_member(conf, &prefix_str, NULL); + if (nxt_slow_path(nxt_python_set_prefix(task, target, value) != NXT_OK)) { + goto fail; + } + target->application = obj; obj = NULL; @@ -453,6 +461,48 @@ fail: } +nxt_inline nxt_int_t +nxt_python_set_prefix(nxt_task_t *task, nxt_python_target_t *target, + nxt_conf_value_t *value) +{ + u_char *prefix; + nxt_str_t str; + + if (value == NULL) { + return NXT_OK; + } + + nxt_conf_get_string(value, &str); + + if (str.length == 0) { + return NXT_OK; + } + + if (str.start[str.length - 1] == '/') { + str.length--; + } + target->prefix.length = str.length; + prefix = nxt_malloc(str.length); + if (nxt_slow_path(prefix == NULL)) { + nxt_alert(task, "Failed to allocate target prefix string"); + return NXT_ERROR; + } + + target->py_prefix = PyString_FromStringAndSize((char *)str.start, + str.length); + if (nxt_slow_path(target->py_prefix == NULL)) { + nxt_free(prefix); + nxt_alert(task, "Python failed to allocate target prefix " + "string"); + return NXT_ERROR; + } + nxt_memcpy(prefix, str.start, str.length); + target->prefix.start = prefix; + + return NXT_OK; +} + + static nxt_int_t nxt_python_set_path(nxt_task_t *task, nxt_conf_value_t *value) { @@ -730,7 +780,8 @@ nxt_python_done_strings(nxt_python_string_t *pstr) static void nxt_python_atexit(void) { - nxt_int_t i; + nxt_int_t i; + nxt_python_target_t *target; if (nxt_py_proto.done != NULL) { nxt_py_proto.done(); @@ -740,7 +791,12 @@ nxt_python_atexit(void) if (nxt_py_targets != NULL) { for (i = 0; i < nxt_py_targets->count; i++) { - Py_XDECREF(nxt_py_targets->target[i].application); + target = &nxt_py_targets->target[i]; + + Py_XDECREF(target->application); + Py_XDECREF(target->py_prefix); + + nxt_free(target->prefix.start); } nxt_unit_free(NULL, nxt_py_targets); diff --git a/src/python/nxt_python.h b/src/python/nxt_python.h index eddb1cfc..37e6265e 100644 --- a/src/python/nxt_python.h +++ b/src/python/nxt_python.h @@ -40,6 +40,8 @@ typedef struct { PyObject *application; + PyObject *py_prefix; + nxt_str_t prefix; nxt_bool_t asgi_legacy; } nxt_python_target_t; diff --git a/src/python/nxt_python_asgi.c b/src/python/nxt_python_asgi.c index 256bcdcc..587a17cf 100644 --- a/src/python/nxt_python_asgi.c +++ b/src/python/nxt_python_asgi.c @@ -27,7 +27,8 @@ static void nxt_py_asgi_remove_reader(nxt_unit_ctx_t *ctx, static void nxt_py_asgi_request_handler(nxt_unit_request_info_t *req); static void nxt_py_asgi_close_handler(nxt_unit_request_info_t *req); -static PyObject *nxt_py_asgi_create_http_scope(nxt_unit_request_info_t *req); +static PyObject *nxt_py_asgi_create_http_scope(nxt_unit_request_info_t *req, + nxt_python_target_t *app_target); static PyObject *nxt_py_asgi_create_address(nxt_unit_sptr_t *sptr, uint8_t len, uint16_t port); static PyObject *nxt_py_asgi_create_ip_address(nxt_unit_sptr_t *sptr, @@ -455,16 +456,16 @@ nxt_py_asgi_request_handler(nxt_unit_request_info_t *req) goto release_send; } - scope = nxt_py_asgi_create_http_scope(req); + req->data = asgi; + target = &nxt_py_targets->target[req->request->app_target]; + + scope = nxt_py_asgi_create_http_scope(req, target); if (nxt_slow_path(scope == NULL)) { nxt_unit_request_done(req, NXT_UNIT_ERROR); goto release_done; } - req->data = asgi; - target = &nxt_py_targets->target[req->request->app_target]; - if (!target->asgi_legacy) { nxt_unit_req_debug(req, "Python call ASGI 3.0 application"); @@ -573,12 +574,14 @@ nxt_py_asgi_close_handler(nxt_unit_request_info_t *req) static PyObject * -nxt_py_asgi_create_http_scope(nxt_unit_request_info_t *req) +nxt_py_asgi_create_http_scope(nxt_unit_request_info_t *req, + nxt_python_target_t *app_target) { char *p, *target, *query; - uint32_t target_length, i; + uint32_t target_length, i, path_length; PyObject *scope, *v, *type, *scheme; PyObject *headers, *header; + nxt_str_t prefix; nxt_unit_field_t *f; nxt_unit_request_t *r; @@ -612,6 +615,17 @@ nxt_py_asgi_create_http_scope(nxt_unit_request_info_t *req) return NULL; } + prefix = app_target->prefix; + path_length = r->path_length; + p = nxt_unit_sptr_get(&r->path); + if (prefix.length > 0 + && ((path_length > prefix.length && p[prefix.length] == '/') + || path_length == prefix.length) + && memcmp(prefix.start, p, prefix.length) == 0) + { + SET_ITEM(scope, root_path, app_target->py_prefix); + } + p = nxt_unit_sptr_get(&r->version); SET_ITEM(scope, http_version, p[7] == '1' ? nxt_py_1_1_str : nxt_py_1_0_str) diff --git a/src/python/nxt_python_asgi_str.c b/src/python/nxt_python_asgi_str.c index 34422973..7171d52b 100644 --- a/src/python/nxt_python_asgi_str.c +++ b/src/python/nxt_python_asgi_str.c @@ -99,7 +99,7 @@ static nxt_python_string_t nxt_py_asgi_strings[] = { { nxt_string("query_string"), &nxt_py_query_string_str }, { nxt_string("raw_path"), &nxt_py_raw_path_str }, { nxt_string("result"), &nxt_py_result_str }, - { nxt_string("root_path"), &nxt_py_root_path_str }, // not used + { nxt_string("root_path"), &nxt_py_root_path_str }, { nxt_string("scheme"), &nxt_py_scheme_str }, { nxt_string("server"), &nxt_py_server_str }, { nxt_string("set_exception"), &nxt_py_set_exception_str }, diff --git a/src/python/nxt_python_wsgi.c b/src/python/nxt_python_wsgi.c index 34afd9a9..dfb31509 100644 --- a/src/python/nxt_python_wsgi.c +++ b/src/python/nxt_python_wsgi.c @@ -60,7 +60,8 @@ static void nxt_python_request_handler(nxt_unit_request_info_t *req); static PyObject *nxt_python_create_environ(nxt_python_app_conf_t *c); static PyObject *nxt_python_copy_environ(nxt_unit_request_info_t *req); -static PyObject *nxt_python_get_environ(nxt_python_ctx_t *pctx); +static PyObject *nxt_python_get_environ(nxt_python_ctx_t *pctx, + nxt_python_target_t *app_target); static int nxt_python_add_sptr(nxt_python_ctx_t *pctx, PyObject *name, nxt_unit_sptr_t *sptr, uint32_t size); static int nxt_python_add_char(nxt_python_ctx_t *pctx, PyObject *name, @@ -141,6 +142,7 @@ static PyObject *nxt_py_query_string_str; static PyObject *nxt_py_remote_addr_str; static PyObject *nxt_py_request_method_str; static PyObject *nxt_py_request_uri_str; +static PyObject *nxt_py_script_name_str; static PyObject *nxt_py_server_addr_str; static PyObject *nxt_py_server_name_str; static PyObject *nxt_py_server_port_str; @@ -160,6 +162,7 @@ static nxt_python_string_t nxt_python_strings[] = { { nxt_string("REMOTE_ADDR"), &nxt_py_remote_addr_str }, { nxt_string("REQUEST_METHOD"), &nxt_py_request_method_str }, { nxt_string("REQUEST_URI"), &nxt_py_request_uri_str }, + { nxt_string("SCRIPT_NAME"), &nxt_py_script_name_str }, { nxt_string("SERVER_ADDR"), &nxt_py_server_addr_str }, { nxt_string("SERVER_NAME"), &nxt_py_server_name_str }, { nxt_string("SERVER_PORT"), &nxt_py_server_port_str }, @@ -304,11 +307,12 @@ nxt_python_wsgi_done(void) static void nxt_python_request_handler(nxt_unit_request_info_t *req) { - int rc; - PyObject *environ, *args, *response, *iterator, *item; - PyObject *close, *result, *application; - nxt_bool_t prepare_environ; - nxt_python_ctx_t *pctx; + int rc; + PyObject *environ, *args, *response, *iterator, *item; + PyObject *close, *result; + nxt_bool_t prepare_environ; + nxt_python_ctx_t *pctx; + nxt_python_target_t *target; pctx = req->ctx->data; @@ -331,7 +335,9 @@ nxt_python_request_handler(nxt_unit_request_info_t *req) prepare_environ = 1; - environ = nxt_python_get_environ(pctx); + target = &nxt_py_targets->target[req->request->app_target]; + + environ = nxt_python_get_environ(pctx, target); if (nxt_slow_path(environ == NULL)) { rc = NXT_UNIT_ERROR; goto done; @@ -352,8 +358,7 @@ nxt_python_request_handler(nxt_unit_request_info_t *req) Py_INCREF(pctx->start_resp); PyTuple_SET_ITEM(args, 1, pctx->start_resp); - application = nxt_py_targets->target[req->request->app_target].application; - response = PyObject_CallObject(application, args); + response = PyObject_CallObject(target->application, args); Py_DECREF(args); @@ -584,11 +589,14 @@ nxt_python_copy_environ(nxt_unit_request_info_t *req) static PyObject * -nxt_python_get_environ(nxt_python_ctx_t *pctx) +nxt_python_get_environ(nxt_python_ctx_t *pctx, + nxt_python_target_t *app_target) { int rc; - uint32_t i, j, vl; + char *path; + uint32_t i, j, vl, path_length; PyObject *environ; + nxt_str_t prefix; nxt_unit_field_t *f, *f2; nxt_unit_request_t *r; @@ -608,8 +616,23 @@ nxt_python_get_environ(nxt_python_ctx_t *pctx) r->target_length)); RC(nxt_python_add_sptr(pctx, nxt_py_query_string_str, &r->query, r->query_length)); - RC(nxt_python_add_sptr(pctx, nxt_py_path_info_str, &r->path, - r->path_length)); + + prefix = app_target->prefix; + path_length = r->path_length; + path = nxt_unit_sptr_get(&r->path); + if (prefix.length > 0 + && ((path_length > prefix.length && path[prefix.length] == '/') + || path_length == prefix.length) + && memcmp(prefix.start, path, prefix.length) == 0) + { + RC(nxt_python_add_py_string(pctx, nxt_py_script_name_str, + app_target->py_prefix)); + + path += prefix.length; + path_length -= prefix.length; + } + + RC(nxt_python_add_char(pctx, nxt_py_path_info_str, path, path_length)); RC(nxt_python_add_sptr(pctx, nxt_py_remote_addr_str, &r->remote, r->remote_length)); diff --git a/test/python/prefix/asgi.py b/test/python/prefix/asgi.py new file mode 100644 index 00000000..234f084f --- /dev/null +++ b/test/python/prefix/asgi.py @@ -0,0 +1,15 @@ +async def application(scope, receive, send): + assert scope['type'] == 'http' + + await send( + { + 'type': 'http.response.start', + 'status': 200, + 'headers': [ + (b'content-length', b'0'), + (b'prefix', scope.get('root_path', 'NULL').encode()), + ], + } + ) + + await send({'type': 'http.response.body', 'body': b''}) diff --git a/test/python/prefix/wsgi.py b/test/python/prefix/wsgi.py new file mode 100644 index 00000000..83b58c9a --- /dev/null +++ b/test/python/prefix/wsgi.py @@ -0,0 +1,10 @@ +def application(environ, start_response): + start_response( + '200', + [ + ('Content-Length', '0'), + ('Script-Name', environ.get('SCRIPT_NAME', 'NULL')), + ('Path-Info', environ['PATH_INFO']), + ], + ) + return [] diff --git a/test/python/targets/asgi.py b/test/python/targets/asgi.py index b51f3964..749ec5b1 100644 --- a/test/python/targets/asgi.py +++ b/test/python/targets/asgi.py @@ -22,6 +22,23 @@ async def application_200(scope, receive, send): ) +async def application_prefix(scope, receive, send): + assert scope['type'] == 'http' + + await send( + { + 'type': 'http.response.start', + 'status': 200, + 'headers': [ + (b'content-length', b'0'), + (b'prefix', scope.get('root_path', 'NULL').encode()), + ], + } + ) + + await send({'type': 'http.response.body', 'body': b''}) + + def legacy_application_200(scope): assert scope['type'] == 'http' diff --git a/test/python/targets/wsgi.py b/test/python/targets/wsgi.py index fa17ab87..3f3d4b27 100644 --- a/test/python/targets/wsgi.py +++ b/test/python/targets/wsgi.py @@ -6,3 +6,12 @@ def wsgi_target_a(env, start_response): def wsgi_target_b(env, start_response): start_response('200', [('Content-Length', '1')]) return [b'2'] + + +def wsgi_target_prefix(env, start_response): + data = u'%s %s' % ( + env.get('SCRIPT_NAME', 'No Script Name'), + env['PATH_INFO'], + ) + start_response('200', [('Content-Length', '%d' % len(data))]) + return [data.encode('utf-8')] diff --git a/test/test_asgi_application.py b/test/test_asgi_application.py index 7f5f1578..121a2fbc 100644 --- a/test/test_asgi_application.py +++ b/test/test_asgi_application.py @@ -79,6 +79,43 @@ custom-header: BLAH resp['headers']['query-string'] == 'var1=val1&var2=val2' ), 'query-string header' + def test_asgi_application_prefix(self): + self.load('prefix', prefix='/api/rest') + + def set_prefix(prefix): + self.conf('"' + prefix + '"', 'applications/prefix/prefix') + + def check_prefix(url, prefix): + resp = self.get(url=url) + assert resp['status'] == 200 + assert resp['headers']['prefix'] == prefix + + check_prefix('/ap', 'NULL') + check_prefix('/api', 'NULL') + check_prefix('/api/', 'NULL') + check_prefix('/api/res', 'NULL') + check_prefix('/api/restful', 'NULL') + check_prefix('/api/rest', '/api/rest') + check_prefix('/api/rest/', '/api/rest') + check_prefix('/api/rest/get', '/api/rest') + check_prefix('/api/rest/get/blah', '/api/rest') + + set_prefix('/api/rest/') + check_prefix('/api/rest', '/api/rest') + check_prefix('/api/restful', 'NULL') + check_prefix('/api/rest/', '/api/rest') + check_prefix('/api/rest/blah', '/api/rest') + + set_prefix('/app') + check_prefix('/ap', 'NULL') + check_prefix('/app', '/app') + check_prefix('/app/', '/app') + check_prefix('/application/', 'NULL') + + set_prefix('/') + check_prefix('/', 'NULL') + check_prefix('/app', 'NULL') + def test_asgi_application_query_string_space(self): self.load('query_string') diff --git a/test/test_asgi_targets.py b/test/test_asgi_targets.py index c1e345ef..84d7b3b0 100644 --- a/test/test_asgi_targets.py +++ b/test/test_asgi_targets.py @@ -90,3 +90,48 @@ class TestASGITargets(TestApplicationPython): ) assert self.get(url='/1')['status'] != 200 + + def test_asgi_targets_prefix(self): + self.conf_targets( + { + "1": { + "module": "asgi", + "callable": "application_prefix", + "prefix": "/1/", + }, + "2": { + "module": "asgi", + "callable": "application_prefix", + "prefix": "/api", + }, + } + ) + self.conf( + [ + { + "match": {"uri": "/1*"}, + "action": {"pass": "applications/targets/1"}, + }, + { + "match": {"uri": "*"}, + "action": {"pass": "applications/targets/2"}, + }, + ], + "routes", + ) + + def check_prefix(url, prefix): + resp = self.get(url=url) + assert resp['status'] == 200 + assert resp['headers']['prefix'] == prefix + + check_prefix('/1', '/1') + check_prefix('/11', 'NULL') + check_prefix('/1/', '/1') + check_prefix('/', 'NULL') + check_prefix('/ap', 'NULL') + check_prefix('/api', '/api') + check_prefix('/api/', '/api') + check_prefix('/api/test/', '/api') + check_prefix('/apis', 'NULL') + check_prefix('/apis/', 'NULL') diff --git a/test/test_configuration.py b/test/test_configuration.py index 7c612db0..9c27222c 100644 --- a/test/test_configuration.py +++ b/test/test_configuration.py @@ -318,6 +318,92 @@ class TestConfiguration(TestControl): assert 'success' in self.conf(conf) + def test_json_application_python_prefix(self): + conf = { + "applications": { + "sub-app": { + "type": "python", + "processes": {"spare": 0}, + "path": "/app", + "module": "wsgi", + "prefix": "/app", + } + }, + "listeners": {"*:7080": {"pass": "routes"}}, + "routes": [ + { + "match": {"uri": "/app/*"}, + "action": {"pass": "applications/sub-app"}, + } + ], + } + + assert 'success' in self.conf(conf) + + def test_json_application_prefix_target(self): + conf = { + "applications": { + "sub-app": { + "type": "python", + "processes": {"spare": 0}, + "path": "/app", + "targets": { + "foo": {"module": "foo.wsgi", "prefix": "/app"}, + "bar": { + "module": "bar.wsgi", + "callable": "bar", + "prefix": "/api", + }, + }, + } + }, + "listeners": {"*:7080": {"pass": "routes"}}, + "routes": [ + { + "match": {"uri": "/app/*"}, + "action": {"pass": "applications/sub-app/foo"}, + }, + { + "match": {"uri": "/api/*"}, + "action": {"pass": "applications/sub-app/bar"}, + }, + ], + } + + assert 'success' in self.conf(conf) + + def test_json_application_invalid_python_prefix(self): + conf = { + "applications": { + "sub-app": { + "type": "python", + "processes": {"spare": 0}, + "path": "/app", + "module": "wsgi", + "prefix": "app", + } + }, + "listeners": {"*:7080": {"pass": "applications/sub-app"}}, + } + + assert 'error' in self.conf(conf) + + def test_json_application_empty_python_prefix(self): + conf = { + "applications": { + "sub-app": { + "type": "python", + "processes": {"spare": 0}, + "path": "/app", + "module": "wsgi", + "prefix": "", + } + }, + "listeners": {"*:7080": {"pass": "applications/sub-app"}}, + } + + assert 'error' in self.conf(conf) + def test_json_application_many2(self): conf = { "applications": { diff --git a/test/test_python_application.py b/test/test_python_application.py index 946d2118..c9483b6a 100644 --- a/test/test_python_application.py +++ b/test/test_python_application.py @@ -94,6 +94,44 @@ custom-header: BLAH resp['headers']['Query-String'] == ' var1= val1 & var2=val2' ), 'Query-String space 4' + def test_python_application_prefix(self): + self.load('prefix', prefix='/api/rest') + + def set_prefix(prefix): + self.conf('"' + prefix + '"', 'applications/prefix/prefix') + + def check_prefix(url, script_name, path_info): + resp = self.get(url=url) + assert resp['status'] == 200 + assert resp['headers']['Script-Name'] == script_name + assert resp['headers']['Path-Info'] == path_info + + check_prefix('/ap', 'NULL', '/ap') + check_prefix('/api', 'NULL', '/api') + check_prefix('/api/', 'NULL', '/api/') + check_prefix('/api/res', 'NULL', '/api/res') + check_prefix('/api/restful', 'NULL', '/api/restful') + check_prefix('/api/rest', '/api/rest', '') + check_prefix('/api/rest/', '/api/rest', '/') + check_prefix('/api/rest/get', '/api/rest', '/get') + check_prefix('/api/rest/get/blah', '/api/rest', '/get/blah') + + set_prefix('/api/rest/') + check_prefix('/api/rest', '/api/rest', '') + check_prefix('/api/restful', 'NULL', '/api/restful') + check_prefix('/api/rest/', '/api/rest', '/') + check_prefix('/api/rest/blah', '/api/rest', '/blah') + + set_prefix('/app') + check_prefix('/ap', 'NULL', '/ap') + check_prefix('/app', '/app', '') + check_prefix('/app/', '/app', '/') + check_prefix('/application/', 'NULL', '/application/') + + set_prefix('/') + check_prefix('/', 'NULL', '/') + check_prefix('/app', 'NULL', '/app') + def test_python_application_query_string_empty(self): self.load('query_string') diff --git a/test/test_python_targets.py b/test/test_python_targets.py index 8e9ecb87..ae271b5f 100644 --- a/test/test_python_targets.py +++ b/test/test_python_targets.py @@ -47,3 +47,55 @@ class TestPythonTargets(TestApplicationPython): resp = self.get(url='/2') assert resp['status'] == 200 assert resp['body'] == '2' + + def test_python_targets_prefix(self): + assert 'success' in self.conf( + { + "listeners": {"*:7080": {"pass": "routes"}}, + "routes": [ + { + "match": {"uri": ["/app*"]}, + "action": {"pass": "applications/targets/app"}, + }, + { + "match": {"uri": "*"}, + "action": {"pass": "applications/targets/catchall"}, + }, + ], + "applications": { + "targets": { + "type": "python", + "working_directory": option.test_dir + + "/python/targets/", + "path": option.test_dir + '/python/targets/', + "protocol": "wsgi", + "targets": { + "app": { + "module": "wsgi", + "callable": "wsgi_target_prefix", + "prefix": "/app/", + }, + "catchall": { + "module": "wsgi", + "callable": "wsgi_target_prefix", + "prefix": "/api", + }, + }, + } + }, + } + ) + + def check_prefix(url, body): + resp = self.get(url=url) + assert resp['status'] == 200 + assert resp['body'] == body + + check_prefix('/app', '/app ') + check_prefix('/app/', '/app /') + check_prefix('/app/rest/user/', '/app /rest/user/') + check_prefix('/catchall', 'No Script Name /catchall') + check_prefix('/api', '/api ') + check_prefix('/api/', '/api /') + check_prefix('/apis', 'No Script Name /apis') + check_prefix('/api/users/', '/api /users/') diff --git a/test/unit/applications/lang/python.py b/test/unit/applications/lang/python.py index 1e38f3fa..3768cf07 100644 --- a/test/unit/applications/lang/python.py +++ b/test/unit/applications/lang/python.py @@ -50,6 +50,7 @@ class TestApplicationPython(TestApplicationProto): 'protocol', 'targets', 'threads', + 'prefix', ): if attr in kwargs: app[attr] = kwargs.pop(attr) -- cgit From 101b262f1feedb7119c8e2cee82657958a31e460 Mon Sep 17 00:00:00 2001 From: Liam Crilly Date: Wed, 14 Dec 2022 16:20:08 +0000 Subject: Tools: Added unitc. --- tools/README.md | 75 ++++++++++++++++++ tools/unitc | 235 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 310 insertions(+) create mode 100755 tools/unitc diff --git a/tools/README.md b/tools/README.md index 7dbf0781..f534aa1f 100644 --- a/tools/README.md +++ b/tools/README.md @@ -4,6 +4,9 @@ This directory contains useful tools for installing, configuring, and managing NGINX Unit. They may not be part of official packages and should be considered experimental. +* [`setup-unit`](#setup-unit) +* [`unitc`](#unitc) + --- ## setup-unit @@ -14,3 +17,75 @@ should be considered experimental. Unit repository for later installation. * `setup-unit welcome` creates an initial configuration to serve a welcome web page with NGINX Unit. + +--- + +## unitc + +### A curl wrapper for managing NGINX Unit configuration + +```USAGE: unitc [options] URI``` + + * **URI** specifies the target in Unit's control API, e.g. `/config` . + * Configuration data is read from stdin. + * [jq](https://stedolan.github.io/jq/) is used to prettify JSON output, if + available. + +| Options | | +|---------|-| +| filename … | Read configuration data consequently from the specified files instead of stdin. +| _HTTP method_ | It is usually not required to specify a HTTP method. `GET` is used to read the configuration. `PUT` is used when making configuration changes unless a specific method is provided. +| `INSERT` | A _virtual_ HTTP method that prepends data when the URI specifies an existing array. The [jq](https://stedolan.github.io/jq/) tool is required for this option. +| `-q` \| `--quiet` | No output to stdout. + +Options are case insensitive and can appear in any order. For example, a +redundant part of the configuration can be identified by its URI, and +followed by `delete` in a subsequent command. + +### Local Configuration +For local instances of Unit, the control socket is automatically detected. +The error log is monitored; when changes occur, new log entries are shown. + +| Options | | +|---------|-| +| `-l` \| `--nolog` | Do not monitor the error log after configuration changes. + +#### Examples +```shell +unitc /config +unitc /control/applications/my_app/restart +unitc /config < unitconf.json +echo '{"*:8080": {"pass": "routes"}}' | unitc /config/listeners +unitc /config/applications/my_app DELETE +unitc /certificates/bundle cert.pem key.pem +``` + +### Remote Configuration +For remote instances of NGINX Unit, the control socket on the remote host can +be set with the `$UNIT_CTRL` environment variable. The remote control socket +can be accessed over TCP or SSH, depending on the type of control socket: + + * `ssh://[user@]remote_host[:ssh_port]/path/to/control.socket` + * `http://remote_host:unit_control_port` + +> **Note:** SSH is recommended for remote confguration. Consider the +> [security implications](https://unit.nginx.org/howto/security/#secure-socket-and-state) +> of managing remote configuration over plaintext HTTP. + +| Options | | +|---------|-| +| `ssh://…` | Specify the remote Unix control socket on the command line. +| `http://…`*URI* | For remote TCP control sockets, the URI may include the protocol, hostname, and port. + +#### Examples +```shell +unitc http://192.168.0.1:8080/status +UNIT_CTRL=http://192.168.0.1:8080 unitc /status + +export UNIT_CTRL=ssh://root@unithost/var/run/control.unit.sock +unitc /config/routes +cat catchall_route.json | unitc POST /config/routes +echo '{"match":{"uri":"/wp-admin/*"},"action":{"return":403}}' | unitc INSERT /config/routes +``` + +--- diff --git a/tools/unitc b/tools/unitc new file mode 100755 index 00000000..838f7ebf --- /dev/null +++ b/tools/unitc @@ -0,0 +1,235 @@ +#!/bin/bash +# unitc - a curl wrapper for configuring NGINX Unit +# https://github.com/nginx/unit/tree/master/tools +# NGINX, Inc. (c) 2022 + +# Defaults +# +ERROR_LOG=/dev/null +REMOTE=0 +SHOW_LOG=1 +NOLOG=0 +QUIET=0 +URI="" +SSH_CMD="" +METHOD=PUT +CONF_FILES=() + +while [ $# -gt 0 ]; do + OPTION=$(echo $1 | tr '[a-z]' '[A-Z]') + case $OPTION in + "-H" | "--HELP") + shift + ;; + + "-L" | "--NOLOG" | "--NO-LOG") + NOLOG=1 + shift + ;; + + "-Q" | "--QUIET") + QUIET=1 + shift + ;; + + "GET" | "PUT" | "POST" | "DELETE" | "INSERT") + METHOD=$OPTION + shift + ;; + + "HEAD" | "PATCH" | "PURGE" | "OPTIONS") + echo "${0##*/}: ERROR: Invalid HTTP method ($OPTION)" + exit 1 + ;; + + *) + if [ -r $1 ]; then + CONF_FILES+=($1) + elif [ "${1:0:1}" = "/" ] || [ "${1:0:4}" = "http" ] && [ "$URI" = "" ]; then + URI=$1 + elif [ "${1:0:6}" = "ssh://" ]; then + UNIT_CTRL=$1 + else + echo "${0##*/}: ERROR: Invalid option ($1)" + exit 1 + fi + shift + ;; + esac +done + +if [ "$URI" = "" ]; then + cat << __EOF__ +${0##*/} - a curl wrapper for managing NGINX Unit configuration + +USAGE: ${0##*/} [options] URI + +• URI is for Unit's control API target, e.g. /config +• A local Unit control socket is detected unless a remote one is specified. +• Configuration data is read from stdin. + +General options + filename … # Read configuration data from files instead of stdin + HTTP method # Default=GET, or PUT with config data (case-insensitive) + INSERT # Virtual HTTP method to prepend data to an existing array + -q | --quiet # No output to stdout + +Local options + -l | --nolog # Do not monitor the error log after applying config changes + +Remote options + ssh://[user@]remote_host[:port]/path/to/control.socket # Remote Unix socket + http://remote_host:port/URI # Remote TCP socket + + A remote Unit control socket may also be defined with the \$UNIT_CTRL + environment variable as http://remote_host:port -OR- ssh://… (as above) + +__EOF__ + exit 1 +fi + +# Figure out if we're running on the Unit host, or remotely +# +if [ "$UNIT_CTRL" = "" ]; then + if [ "${URI:0:4}" = "http" ]; then + REMOTE=1 + UNIT_CTRL=$(echo "$URI" | cut -f1-3 -d/) + URI=/$(echo "$URI" | cut -f4- -d/) + fi +elif [ "${UNIT_CTRL:0:6}" = "ssh://" ]; then + REMOTE=1 + SSH_CMD="ssh $(echo $UNIT_CTRL | cut -f1-3 -d/)" + UNIT_CTRL="--unix-socket /$(echo $UNIT_CTRL | cut -f4- -d/) _" +elif [ "${URI:0:1}" = "/" ]; then + REMOTE=1 +fi + +if [ $REMOTE -eq 0 ]; then + # Check if Unit is running, find the main process + # + PID=($(ps ax | grep unit:\ main | grep -v \ grep | awk '{print $1}')) + if [ ${#PID[@]} -eq 0 ]; then + echo "${0##*/}: ERROR: unitd not running (set \$UNIT_CTRL to configure a remote instance)" + exit 1 + elif [ ${#PID[@]} -gt 1 ]; then + echo "${0##*/}: ERROR: multiple unitd processes detected (${PID[@]})" + exit 1 + fi + + # Read the significant unitd conifuration from cache file (or create it) + # + if [ -r /tmp/${0##*/}.$PID.env ]; then + source /tmp/${0##*/}.$PID.env + else + # Check we have unitd in $PATH (and all the other tools we will need) + # + MISSING=$(hash unitd curl ps grep tr cut sed tail sleep 2>&1 | cut -f4 -d: | tr -d '\n') + if [ "$MISSING" != "" ]; then + echo "${0##*/}: ERROR: cannot find$MISSING: please install or add to \$PATH" + exit 1 + fi + + # Get control address + # + PARAMS=$(ps $PID | grep unitd | cut -f2- -dv | tr '[]' ' ' | cut -f4- -d ' ' | sed -e 's/ --/\n--/g') + CTRL_ADDR=$(echo "$PARAMS" | grep '\--control' | cut -f2 -d' ') + if [ "$CTRL_ADDR" = "" ]; then + CTRL_ADDR=$(unitd --help | grep -A1 '\--control' | tail -1 | cut -f2 -d\") + fi + + # Prepare for network or Unix socket addressing + # + if [ $(echo $CTRL_ADDR | grep -c ^unix:) -eq 1 ]; then + SOCK_FILE=$(echo $CTRL_ADDR | cut -f2- -d:) + if [ -r $SOCK_FILE ]; then + UNIT_CTRL="--unix-socket $SOCK_FILE _" + else + echo "${0##*/}: ERROR: cannot read unitd control socket: $SOCK_FILE" + ls -l $SOCK_FILE + exit 2 + fi + else + UNIT_CTRL="http://$CTRL_ADDR" + fi + + # Get error log filename + # + ERROR_LOG=$(echo "$PARAMS" | grep '\--log' | cut -f2 -d' ') + if [ "$ERROR_LOG" = "" ]; then + ERROR_LOG=$(unitd --help | grep -A1 '\--log' | tail -1 | cut -f2 -d\") + fi + + # Cache the discovery for this unit PID (and cleanup any old files) + # + rm /tmp/${0##*/}.* 2> /dev/null + echo UNIT_CTRL=\"${UNIT_CTRL}\" > /tmp/${0##*/}.$PID.env + echo ERROR_LOG=${ERROR_LOG} >> /tmp/${0##*/}.$PID.env + fi +fi + +# Choose presentation style +# +if [ $QUIET -eq 1 ]; then + OUTPUT="head -c 0" # Equivalent to >/dev/null +elif hash jq 2> /dev/null; then + OUTPUT="jq" +else + OUTPUT="cat" +fi + +# Get current length of error log before we make any changes +# +if [ -f $ERROR_LOG ] && [ -r $ERROR_LOG ]; then + LOG_LEN=$(wc -l < $ERROR_LOG) +else + NOLOG=1 +fi + +# Adjust HTTP method and curl params based on presence of stdin payload +# +if [ -t 0 ] && [ ${#CONF_FILES[@]} -eq 0 ]; then + if [ "$METHOD" = "DELETE" ]; then + $SSH_CMD curl -X $METHOD $UNIT_CTRL$URI 2> /tmp/${0##*/}.$$ | $OUTPUT + else + SHOW_LOG=$(echo $URI | grep -c ^/control/) + $SSH_CMD curl $UNIT_CTRL$URI 2> /tmp/${0##*/}.$$ | $OUTPUT + fi +else + if [ "$METHOD" = "INSERT" ]; then + if ! hash jq 2> /dev/null; then + echo "${0##*/}: ERROR: jq(1) is required to use the INSERT method; install at " + exit 1 + fi + NEW_ELEMENT=$(cat ${CONF_FILES[@]}) + echo $NEW_ELEMENT | jq > /dev/null || exit $? # Test the input is valid JSON before proceeding + OLD_ARRAY=$($SSH_CMD curl -s $UNIT_CTRL$URI) + if [ "$(echo $OLD_ARRAY | jq -r type)" = "array" ]; then + echo $OLD_ARRAY | jq ". |= [$NEW_ELEMENT] + ." | $SSH_CMD curl -X PUT --data-binary @- $UNIT_CTRL$URI 2> /tmp/${0##*/}.$$ | $OUTPUT + else + echo "${0##*/}: ERROR: the INSERT method expects an array" + exit 3 + fi + else + cat ${CONF_FILES[@]} | $SSH_CMD curl -X $METHOD --data-binary @- $UNIT_CTRL$URI 2> /tmp/${0##*/}.$$ | $OUTPUT + fi +fi + +CURL_STATUS=${PIPESTATUS[0]} +if [ $CURL_STATUS -ne 0 ]; then + echo "${0##*/}: ERROR: curl(1) exited with an error ($CURL_STATUS)" + if [ $CURL_STATUS -eq 7 ] && [ $REMOTE -eq 0 ]; then + echo "${0##*/}: Check that you have permission to access the Unit control socket, or try again with sudo(8)" + else + echo "${0##*/}: Trying to access $UNIT_CTRL$URI" + cat /tmp/${0##*/}.$$ && rm /tmp/${0##*/}.$$ + fi + exit 4 +fi +rm /tmp/${0##*/}.$$ 2> /dev/null + +if [ $SHOW_LOG -gt 0 ] && [ $NOLOG -eq 0 ] && [ $QUIET -eq 0 ]; then + echo -n "${0##*/}: Waiting for log..." + sleep $SHOW_LOG + echo "" + sed -n $((LOG_LEN+1)),\$p $ERROR_LOG +fi -- cgit From 3778877eb3be3904edadb9f85553f81cdf32ea43 Mon Sep 17 00:00:00 2001 From: Alejandro Colomar Date: Mon, 21 Nov 2022 18:20:26 +0100 Subject: Tools: Added subcommands to setup-unit. This script combines the old setup-unit (as the repo-config command), with new functionality, to provide an easy welcome website for first-time users, and also some more commands that are useful for administrating a running unitd(8) instance. Suggested-by: Liam Crilly Cc: Konstantin Pavlov Cc: Artem Konev Cc: Timo Start Cc: Andrew Clayton Signed-off-by: Alejandro Colomar --- tools/setup-unit | 1611 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 1400 insertions(+), 211 deletions(-) diff --git a/tools/setup-unit b/tools/setup-unit index 2e10e54d..286eef87 100755 --- a/tools/setup-unit +++ b/tools/setup-unit @@ -1,311 +1,1500 @@ -#!/bin/sh +#!/usr/bin/env bash ##################################################################### # # Copyright (C) NGINX, Inc. -# # Author: NGINX Unit Team, F5 Inc. -# Version: 0.0.1 -# Date: 2022-05-05 -# -# This script will configure the repositories for NGINX Unit on Ubuntu, -# Debian, RedHat, CentOS, Oracle Linux, Amazon Linux, Fedora. -# It must be run as root. -# -# Note: curl and awk are required by this script, so the script checks to make -# sure they are installed. # ##################################################################### + +if test -n ${BASH_VERSION} && test "${BASH_VERSINFO[0]}" -eq 3; then + >&2 echo 'Your version of bash(1) is not supported by this script.'; + >&2 echo "You're probably running on MacOS. We recommend that you either"; + >&2 echo 'install a newer version of bash(1), or you run this script with'; + >&2 echo 'another shell, like for example zsh(1):'; + >&2 echo " $ zsh ${SUDO_USER:+sudo }$0 ..."; + exit 1; +fi; + + +set -Eefuo pipefail; + +test -v BASH_VERSION \ +&& shopt -s lastpipe; + export LC_ALL=C -checkOSPrereqs () { +program_name="$0"; +prog_name="$(basename $program_name)"; - if ! command -v curl > /dev/null 2>&1 - then - echo "Error: curl not found in PATH. It must be installed to run this script." - exit 1 - fi +dry_run='no'; - if ! command -v awk > /dev/null 2>&1 - then - echo "Error: awk not found in PATH. It must be installed to run this script." - exit 1 - fi +help_unit() +{ + cat <<__EOF__ ; +SYNOPSIS + $program_name [-h] COMMAND [ARGS] + + Subcommands + +-- repo-config [-hn] [PKG-MANAGER OS-NAME OS-VERSION] + +-- welcome [-hn] - return 0 +DESCRIPTION + This script simplifies installing and configuring an NGINX Unit server + for first-time users. + + Run '$program_name COMMAND -h' for more information on a command. + +COMMANDS + repo-config + Configure your package manager with the NGINX Unit repository + for later installation. + + welcome + Creates an initial configuration to serve a welcome web page + for NGINX Unit. + +OPTIONS + -h, --help + Print this help. + + --help-more + Print help for more commands. They are experimental. This is + not recommended unless you know what you're doing. + +__EOF__ } -##################################################################### -# Function getOS -# -# Getting the OS is not the same on all distributions. First, we use -# uname to find out if we are running on Linux or FreeBSD. For all the -# supported versions of Debian and Ubuntu, we expect to find the -# /etc/os-release file which has multiple lines with name-value pairs -# from which we can get the OS name and version. For RedHat and its -# variants, the os-release file may or may not exist, depending on the -# version. If it doesn't, then we look for the release package and -# get the OS and version from the package name. For FreeBSD, we use the -# "uname -rs" command. -# -# A string is written to stdout with three values separated by ":": -# OS -# OS Name -# OS Version -# -# If none of these files was found, an empty string is written. -# -# Return: 0 for success, 1 for error -##################################################################### -getOS () { +help_more_unit() +{ + cat <<__EOF__ ; +SYNOPSIS + $program_name [-h] COMMAND [ARGS] - os="" - osName="" - osVersion="" + Subcommands + +-- cmd [-h] + +-- ctl [-h] [-s SOCK] SUBCOMMAND [ARGS] + | +-- http [-h] [-c CURLOPT] METHOD PATH + | +-- insert [-h] PATH INDEX + +-- freeport [-h] + +-- json-ins [-hn] JSON INDEX + +-- os-probe [-h] + +-- ps [-h] [-t TYPE] + +-- repo-config [-hn] [PKG-MANAGER OS-NAME OS-VERSION] + +-- sock [-h] SUBCOMMAND [ARGS] + | +-- filter [-chs] + | +-- find [-h] + +-- welcome [-hn] - LC_ALL=C +DESCRIPTION + This script simplifies installing and configuring + an NGINX Unit server for first-time users. - os=$(uname | tr '[:upper:]' '[:lower:]') + Run '$program_name COMMAND -h' for more information on a command. - if [ "$os" != "linux" ] && [ "$os" != "freebsd" ]; then - echoErr "Error: Operating system is not Linux or FreeBSD, can't proceed" - echo "On macOS, try 'brew install nginx/unit/unit'" - echo - return 1 - fi +COMMANDS + cmd Print the invocation line of unitd(8). - if [ "$os" = "linux" ]; then - if [ -f "$osRelease" ]; then - # The value for the ID and VERSION_ID may or may not be in quotes - osName=$( grep "^ID=" "$osRelease" | sed s/\"//g | awk -F= '{ print $2 }') - osVersion=$(grep "^VERSION_ID=" "$osRelease" | sed s/\"//g | awk -F= '{ print $2 }') - else - # rhel or centos 6.* - if rpm -q redhat-release-server >/dev/null 2>&1; then - osName=rhel - osVersion=$(rpm -q redhat-release-server |sed 's/.*-//' | awk -F. '{print $1"."$2;}') - elif rpm -q centos-release >/dev/null 2>&1; then - osName=centos - osVersion=$(rpm -q centos-release | sed 's/centos-release-//' | sed 's/\..*//' | awk -F- '{print $1"."$2;}') - else - echoErr "Error: Unable to determine the operating system and version, or the OS is not supported" - echo - return 1 - fi - fi + ctl Control a running unitd(8) instance through its control socket. + + freeport + Print an available TCP port. + + json-ins + Insert a JSON element read from standard input into a JSON + array read from a file at a given INDEX. + + os-probe + This script probes the OS, and prints details about the + version. + + ps List unitd(8) processes. + + repo-config + Configure your package manager with the NGINX Unit + repository for later installation + + sock Print the address of the API control socket. + + welcome + Creates an initial configuration to serve a welcome web page + for NGINX Unit. + +OPTIONS + -h, --help + Print the basic help (some commands are hidden). + + --help-more + Print the hidden help with more commands. + +__EOF__ +} + +warn() +{ + >&2 echo "$prog_name: error: $*"; +} + +err() +{ + >&2 echo "$prog_name: error: $*"; + exit 1; +} + +dry_run_echo() +{ + if test "$dry_run" = "yes"; then + echo "$*"; + fi; +} + +dry_run_eval() +{ + if test "$dry_run" = "yes"; then + echo " $*"; else - osName=$os - osVersion=$(uname -rs | awk -F '[ -]' '{print $2}') - if [ -z "$osVersion" ]; then - echoErr "Unable to get the FreeBSD version" - echo - return 1 - fi - fi + eval "$*"; + fi; +} - # Force osName to lowercase - osName=$(echo "$osName" | tr '[:upper:]' '[:lower:]') - echoDebug "getOS: os=$os osName=$osName osVersion=$osVersion" - echo "$os:$osName:$osVersion" - return 0 +help_unit_cmd() +{ + cat <<__EOF__ ; +SYNOPSIS + $program_name cmd [-h] + +DESCRIPTION + Print the invocation line of running instances of unitd(8). + +OPTIONS + -h, --help + Print this help. + +__EOF__ +} + + +unit_cmd() +{ + while test $# -ge 1; do + case "$1" in + -h | --help) + help_unit_cmd; + exit 0; + ;; + -*) + err "cmd: $1: Unknown option."; + ;; + *) + err "cmd: $1: Unknown argument."; + ;; + esac; + shift; + done; + + unit_ps -t m \ + | sed 's/.*\[\(.*\)].*/\1/'; } -installDebian () { +help_unit_ctl() +{ + cat <<__EOF__ ; +SYNOPSIS + $program_name ctl [-h] [-s SOCK] SUBCOMMAND [ARGS] + + Subcommands + +-- http [-h] [-c CURLOPT] METHOD PATH + +-- insert [-h] PATH INDEX + +DESCRIPTION + Control a running unitd(8) instance through its control socket. + + Run '$program_name ctl SUBCOMMAND -h' for more information on a + subcommand. - echoDebug "Install on Debian" +SUBCOMMANDS + http Send an HTTP request to the control socket. - curl --output /usr/share/keyrings/nginx-keyring.gpg https://unit.nginx.org/keys/nginx-keyring.gpg + insert Insert an element into a specified index in an array in the + JSON configuration. - apt install -y apt-transport-https lsb-release ca-certificates +OPTIONS + -h, --help + Print this help. - printf "deb [signed-by=/usr/share/keyrings/nginx-keyring.gpg] https://packages.nginx.org/unit/debian/ %s unit\n" "$(lsb_release -cs)" | tee /etc/apt/sources.list.d/unit.list - printf "deb-src [signed-by=/usr/share/keyrings/nginx-keyring.gpg] https://packages.nginx.org/unit/debian/ %s unit\n" "$(lsb_release -cs)" | tee -a /etc/apt/sources.list.d/unit.list + -s, --sock SOCK + Use SOCK as the API control socket address. If not specified, + the script will try to find it. This will be used by + subcommands. - apt update + The socket can be a tcp(7) socket or a unix(7) socket, and in + the case of a unix(7) socket, it can be local, or it can be in + a remote machine, accessed through ssh(1). Accepted syntax + for SOCK: - return 0 + unix:/path/to/control.sock + ssh://[user@]host[:port]/path/to/control.sock + [http[s]://]host[:port] + + The last form is less secure than the first two; you should + have a look at: + + +ENVIRONMENT + Options take precedence over their equivalent environment variables, + so if both are specified, the option will be used. + + UNIT_CTL_SOCK + Equivalent to the option -s (--sock). + +__EOF__ } -installUbuntu () { - echoDebug "Install on Ubuntu" +unit_ctl() +{ - curl --output /usr/share/keyrings/nginx-keyring.gpg https://unit.nginx.org/keys/nginx-keyring.gpg + if test -v UNIT_CTL_SOCK; then + local sock="$UNIT_CTL_SOCK"; + fi; - apt install -y apt-transport-https lsb-release ca-certificates + while test $# -ge 1; do + case "$1" in + -h | --help) + help_unit_ctl; + exit 0; + ;; + -s | --sock) + if ! test $# -ge 2; then + err "ctl: $1: Missing argument."; + fi; + local sock="$2"; + shift; + ;; + -*) + err "ctl: $1: Unknown option."; + ;; + *) + break; + ;; + esac; + shift; + done; + + if test ! $# -ge 1; then + err 'ctl: Missing subcommand.'; + fi; + + if test -v sock && echo $sock | grep '^ssh://' >/dev/null; then + local remote="$(echo $sock | sed 's,\(ssh://[^/]*\).*,\1,')"; + local sock="$(echo $sock | sed 's,ssh://[^/]*\(.*\),unix:\1,')"; + fi; + + case $1 in + http) + shift; + unit_ctl_http ${remote:+ ---r $remote} ${sock:+ ---s $sock} $@; + ;; + insert) + shift; + unit_ctl_insert ${remote:+ ---r $remote} ${sock:+ ---s $sock} $@; + ;; + *) + err "ctl: $1: Unknown argument."; + ;; + esac; +} - printf "deb [signed-by=/usr/share/keyrings/nginx-keyring.gpg] https://packages.nginx.org/unit/ubuntu/ %s unit\n" "$(lsb_release -cs)" | tee /etc/apt/sources.list.d/unit.list - printf "deb-src [signed-by=/usr/share/keyrings/nginx-keyring.gpg] https://packages.nginx.org/unit/ubuntu/ %s unit\n" "$(lsb_release -cs)" | tee -a /etc/apt/sources.list.d/unit.list - apt update +help_unit_ctl_http() +{ + cat <<__EOF__ ; +SYNOPSIS + $program_name ctl [CTL-OPTS] http [-h] [-c CURLOPT] METHOD PATH - return 0 +DESCRIPTION + Send an HTTP request to the unitd(8) control API socket. + + The payload is read from standard input. + +OPTIONS + -c, --curl CURLOPT + Pass CURLOPT as an option to curl. This script is implemented + in terms of curl(1), so it's useful to be able to tweak its + behavior. It can be used multiple times, which will be + appended (and also appended to the contents of + UNIT_CTL_HTTP_CURLOPTS). + + -h, --help + Print this help. + +ENVIRONMENT + UNIT_CTL_HTTP_CURLOPTS + Equivalent to the option -c (--curl). + +EXAMPLES + $program_name ctl http -c --no-progress-meter GET /config >tmp; + +SEE ALSO + + +__EOF__ } -installRedHat () { - echoDebug "Install on RedHat/CentOS/Oracle" +unit_ctl_http() +{ + local curl_options="${UNIT_CTL_HTTP_CURLOPTS:-}"; + + while test $# -ge 1; do + case "$1" in + -c | --curl) + if ! test $# -ge 2; then + err "ctl: http: $1: Missing argument."; + fi; + curl_options="$curl_options $2"; + shift; + ;; + -h | --help) + help_unit_ctl_http; + exit 0; + ;; + ---r | ----remote) + local remote="$2"; + shift; + ;; + ---s | ----sock) + local sock="$2"; + shift; + ;; + -*) + err "ctl: http: $1: Unknown option."; + ;; + *) + break; + ;; + esac; + shift; + done; + + if ! test $# -ge 1; then + err 'ctl: http: METHOD: Missing argument.'; + fi; + local method="$1"; + + if ! test $# -ge 2; then + err 'ctl: http: PATH: Missing argument.'; + fi; + local req_path="$2"; + + if test -v remote; then + local remote_sock="$(echo "$sock" | unit_sock_filter -s)"; + local local_sock="$(mktemp -u -p /var/run/unit/)"; + local ssh_ctrl="$(mktemp -u -p /var/run/unit/)"; + + mkdir -p /var/run/unit/; + + ssh -fMNnT -S "$ssh_ctrl" \ + -o 'ExitOnForwardFailure yes' \ + -L "$local_sock:$remote_sock" "$remote"; + + sock="unix:$local_sock"; + + elif ! test -v sock; then + local sock="$(unit_sock_find)"; + fi; + + curl $curl_options -X $method -d@- \ + $(echo "$sock" | unit_sock_filter -c)${req_path} \ + ||:; + + if test -v remote; then + ssh -S "$ssh_ctrl" -O exit "$remote" 2>/dev/null; + unlink "$local_sock"; + fi; +} + + +help_unit_ctl_insert() +{ + cat <<__EOF__ ; +SYNOPSIS + $program_name ctl [CTL-OPTS] insert [-h] PATH INDEX + +DESCRIPTION + Insert an element into a specified position (INDEX) in the JSON array + in the unitd(8) configuration API at PATH. + + The new element is read from standard input. + +OPTIONS + -h, --help + Print this help. + +SEE ALSO + $program_name ctl http -h; - case "$osVersion" in - 6|6.*|7|7.*|8|8.*) - cat << __EOF__ > /etc/yum.repos.d/unit.repo -[unit] -name=unit repo -baseurl=https://packages.nginx.org/unit/rhel/\$releasever/\$basearch/ -gpgcheck=0 -enabled=1 __EOF__ +} + + +unit_ctl_insert() +{ + while test $# -ge 1; do + case "$1" in + -h | --help) + help_unit_ctl_insert; + exit 0; + ;; + ---r | ----remote) + local remote="$2"; + shift; + ;; + ---s | ----sock) + local sock="$2"; + shift; + ;; + -*) + err "ctl: insert: $1: Unknown option."; ;; *) - echo "Unsupported $osName version: $osVersion" - exit 1 + break; ;; - esac + esac; + shift; + done; - yum makecache + if ! test $# -ge 1; then + err 'ctl: insert: PATH: Missing argument.'; + fi; + local req_path="$1"; - return 0 + if ! test $# -ge 2; then + err 'ctl: insert: INDEX: Missing argument.'; + fi; + local idx="$2"; + + if test -v remote; then + local remote_sock="$(echo "$sock" | unit_sock_filter -s)"; + local local_sock="$(mktemp -u -p /var/run/unit/)"; + local ssh_ctrl="$(mktemp -u -p /var/run/unit/)"; + + mkdir -p /var/run/unit/; + + ssh -fMNnT -S "$ssh_ctrl" \ + -o 'ExitOnForwardFailure yes' \ + -L "$local_sock:$remote_sock" "$remote"; + + sock="unix:$local_sock"; + + elif ! test -v sock; then + local sock="$(unit_sock_find)"; + fi; + + local old="$(mktemp ||:)"; + + unit_ctl_http ---s "$sock" -c --no-progress-meter GET "$req_path" \ + "$old" \ + ||:; + + unit_json_ins "$old" "$idx" \ + | unit_ctl_http ---s "$sock" PUT "$req_path" \ + ||:; + + if test -v remote; then + ssh -S "$ssh_ctrl" -O exit "$remote" 2>/dev/null; + unlink "$local_sock"; + fi; } -installAmazon () { - echoDebug "Install on Amazon" +help_unit_ctl_welcome() +{ + cat <<__EOF__ ; +SYNOPSIS + $program_name welcome [-hn] + +DESCRIPTION + This script tests an NGINX Unit instalation by creating an initial + configuration and serving a welcome web page. Recommended for + first-time users. + +OPTIONS + -h, --help + Print this help. + + -n, --dry-run + Dry run. Print the commands to be run instea of actually + running them. Each command is preceded by a line explaining + what it does. - case "$osVersion" in - 2) - cat << __EOF__ > /etc/yum.repos.d/unit.repo -[unit] -name=unit repo -baseurl=https://packages.nginx.org/unit/amzn2/\$releasever/\$basearch/ -gpgcheck=0 -enabled=1 __EOF__ - ;; +} + + +unit_ctl_welcome() +{ + while test $# -ge 1; do + case "$1" in + -f | --force) + local force='yes'; + ;; + -h | --help) + help_unit_ctl_welcome; + exit 0; + ;; + -n | --dry-run) + dry_run='yes'; + ;; + -*) + err "welcome: $1: Unknown option."; + ;; *) - cat << __EOF__ > /etc/yum.repos.d/unit.repo -[unit] -name=unit repo -baseurl=https://packages.nginx.org/unit/amzn/\$releasever/\$basearch/ -gpgcheck=0 -enabled=1 + err "welcome: $1: Unknown argument."; + ;; + esac; + shift; + done; + + id -u \ + | xargs test 0 -ne \ + && err 'welcome: This script requires root privileges to run.'; + + command -v curl >/dev/null \ + || err 'welcome: curl(1) not found in PATH. It must be installed to run this script.'; + + www='/srv/www/unit/index.html'; + if test -e "$www" && ! test -v force || ! test -w /srv; then + www="$(mktemp)"; + mv "$www" "$www.html"; + www="$www.html" + fi; + + unit_ps -t m \ + | wc -l \ + | read -r nprocs \ + ||: + + if test 0 -eq "$nprocs"; then + warn "welcome: NGINX Unit isn't running."; + warn 'For help starting NGINX Unit, see:'; + err " "; + elif test 1 -ne "$nprocs"; then + err 'welcome: Only one NGINX Unit instance should be running.'; + fi; + + local sock="$(unit_sock_find)"; + local curl_opt="$(unit_sock_find | unit_sock_filter -c)"; + + curl $curl_opt/ >/dev/null 2>&1 \ + || err "welcome: Can't reach the control socket."; + + if ! test -v force; then + unit_cmd \ + | read -r cmd; + + # Check unitd is not configured already. + echo "$cmd" \ + | if grep '\--state' >/dev/null; then + echo "$cmd" \ + | sed 's/ --/\n--/g' \ + | grep '\--state' \ + | cut -d' ' -f2; + else + $cmd --help \ + | sed -n '/\--state/,+1p' \ + | grep 'default:' \ + | sed 's/ *default: "\(.*\)"/\1/'; + fi \ + | sed 's,$,/conf.json,' \ + | read -r conffile \ + ||:; + + if test -e $conffile; then + if ! unit_ctl_http ---s "$sock" 'GET' '/config' /dev/null | grep -q '^{}.\?$'; # The '.\?' is for the possible carriage return. + then + warn 'welcome: NGINX Unit is already configured. If you are sure you want'; + err 'to overwrite its current configuration, run again with --force.'; + fi; + fi; + fi; + + ( + unit_freeport \ + || err "welcome: Can't find an available port."; + ) \ + | read -r port; + + dry_run_echo 'Create a file to serve:'; + dry_run_eval "mkdir -p $(dirname $www);"; + dry_run_eval "cat >'$www'"' <<__EOF__; + + + + Welcome to NGINX Unit + + + +

Welcome to NGINX Unit

+

Congratulations! NGINX Unit is installed and running.

+

Useful Links

+ + +

Next steps

+ +

Check Current Configuration

+
+

Unit'"'"'s control API is currently listening for configuration changes + on the '"$(unit_sock_find | grep -q '^unix:' && echo 'Unix socket' || echo 'socket')"' at + '"$(unit_sock_find)"'
+ To see the current configuration:

+
'"${SUDO_USER:+sudo }"'curl '"$curl_opt"'/config
+
+ +

Change Listener Port

+
+

This page is served over a random TCP high port. To choose the default HTTP port (80), + replace the "listeners" object:

+
echo '"'"'{"*:80": {"pass": "routes"}}'"'"' | '"${SUDO_USER:+sudo }"'curl -X PUT -d@- '"$curl_opt"'/config/listeners
+ Then remove the port number from the address bar and reload the page. +
+ +
+

NGINX Unit — the universal web app server
+ NGINX, Inc. © 2022

+ + +__EOF__'; + dry_run_echo; + dry_run_echo 'Give it appropriate permissions:'; + dry_run_eval "chmod 644 '$www';"; + dry_run_echo; + + dry_run_echo 'Configure unitd:' + dry_run_eval "cat <<__EOF__ \\ + | sed 's/8080/$port/' \\ + | curl -X PUT -d@- $curl_opt/config; + { + \"listeners\": { + \"*:8080\": { + \"pass\": \"routes\" + } + }, + \"routes\": [{ + \"action\": { + \"share\": \"$www\" + } + }] + } +__EOF__"; + + dry_run_echo; + + echo; + echo 'You may want to try the following commands now:'; + echo; + echo 'Check out current unitd configuration:'; + echo " ${SUDO_USER:+sudo} curl $curl_opt/config"; + echo; + echo 'Browse the welcome page:'; + echo " curl http://localhost:$port/"; +} + + +help_unit_freeport() +{ + cat <<__EOF__ ; +SYNOPSIS + $program_name freeport [-h] + +DESCRIPTION + Print an available TCP port. + +OPTIONS + -h, --help + Print this help. + __EOF__ - ;; - esac +} - yum makecache - return 0 +unit_freeport() +{ + while test $# -ge 1; do + case "$1" in + -h | --help) + help_unit_freeport; + exit 0; + ;; + -*) + err "freeport: $1: Unknown option."; + ;; + *) + err "freeport: $1: Unknown argument."; + ;; + esac; + shift; + done; + + freeport="$(mktemp -t freeport-XXXXXX)"; + + cat <<__EOF__ \ + | cc -x c -o $freeport -; + #include + #include + #include + #include + #include + #include + + + int32_t get_free_port(void); + + + int + main(void) + { + int32_t port; + + port = get_free_port(); + if (port == -1) + exit(EXIT_FAILURE); + + printf("%d\n", port); + exit(EXIT_SUCCESS); + } + + + int32_t + get_free_port(void) + { + int sfd; + int32_t port; + socklen_t len; + struct sockaddr_in addr; + + port = -1; + + sfd = socket(PF_INET, SOCK_STREAM, 0); + if (sfd == -1) { + perror("socket()"); + return -1; + } + + bzero(&addr, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + addr.sin_port = htons(0); // random port + + len = sizeof(addr); + if (bind(sfd, (struct sockaddr *) &addr, len)) { + perror("bind()"); + goto fail; + } + + if (getsockname(sfd, (struct sockaddr *) &addr, &len)) { + perror("getsockname()"); + goto fail; + } + + port = ntohs(addr.sin_port); + + fail: + close(sfd); + return port; + } +__EOF__ + + $freeport; } -installFedora () { - echoDebug "Install on Fedora" +help_unit_json_ins() +{ +cat <<__EOF__ ; +SYNOPSIS + $program_name json-ins [-hn] JSON INDEX + +ARGUMENTS + JSON Path to a JSON file containing a top-level array. + + INDEX Position in the array where to insert the element. + +DESCRIPTION + Insert a JSON element read from standard input into a JSON array read + from a file at a given INDEX. + + The resulting array is printed to standard output. + +OPTIONS + -h, --help + Print this help. + + -n, --dry-run + Dry run. Print the command to be run instead of actually + running it. - cat << __EOF__ > /etc/yum.repos.d/unit.repo -[unit] -name=unit repo -baseurl=https://packages.nginx.org/unit/fedora/\$releasever/\$basearch/ -gpgcheck=0 -enabled=1 __EOF__ +} - dnf makecache - return 0 +unit_json_ins() +{ + while test $# -ge 1; do + case "$1" in + -h | --help) + help_unit_json_ins; + exit 0; + ;; + -n | --dry-run) + dry_run='yes'; + ;; + -*) + err "json-ins: $1: Unknown option."; + ;; + *) + break; + ;; + esac; + shift; + done; + + if ! test $# -ge 1; then + err 'json-ins: JSON: Missing argument.'; + fi; + local arr=$1; + + if ! test $# -ge 2; then + err 'json-ins: INDEX: Missing argument.'; + fi; + local idx=$2; + + dry_run_eval "( + jq '.[0:$idx]' <'$arr'; + echo '['; + jq .; + echo ']'; + jq '.[$idx:]' <'$arr'; + ) \\ + | sed '/^\[]$/d' \\ + | sed '/^]$/{N;s/^]\n\[$/,/}' \\ + | jq .;" } -am_i_root() { +help_unit_os_probe() +{ + cat <<__EOF__ ; +SYNOPSIS + $program_name os-probe [-h] + +DESCRIPTION + This script probes the OS, and prints details about the version. It + prints three fields, delimited by ':'; the first is the package manager, + the second is the OS name, and the third is the OS version. + +OPTIONS + -h, --help + Print this help. + +__EOF__ +} + - USERID=$(id -u) - if [ 0 -ne "$USERID" ]; then - echoErr "This script requires root privileges to run; now exiting." - exit 1 +unit_os_probe() +{ + while test $# -ge 1; do + case "$1" in + -h | --help) + help_unit_os_probe; + exit 0; + ;; + -*) + err "os-probe: $1: Unknown option."; + ;; + *) + err "os-probe: $1: Unknown argument."; + ;; + esac; + shift; + done; + + local os=$(uname | tr '[:upper:]' '[:lower:]') + + if [ "$os" != 'linux' ] && [ "$os" != 'freebsd' ]; then + err "os-probe: The OS isn't Linux or FreeBSD, can't proceed." fi - return 0 + if [ "$os" = 'linux' ]; then + if command -v apt-get >/dev/null; then + local pkgMngr='apt'; + elif command -v dnf >/dev/null; then + local pkgMngr='dnf'; + elif command -v yum >/dev/null; then + local pkgMngr='yum'; + else + local pkgMngr=''; + fi; + + local osRelease='/etc/os-release'; + + if [ -f "$osRelease" ]; then + # The value for the ID and VERSION_ID may or may not be in quotes + local osName=$(grep "^ID=" "$osRelease" | sed s/\"//g | awk -F= '{ print $2 }' ||:) + local osVersion=$(grep '^VERSION_ID=' "$osRelease" | sed s/\"//g | awk -F= '{ print $2 }' || lsb_release -cs) + else + err "os-probe: Unable to determine OS and version, or the OS isn't supported" + fi + else + local pkgMngr='pkg'; + local osName=$os + local osVersion=$(uname -rs | awk -F '[ -]' '{print $2}' ||:) + if [ -z "$osVersion" ]; then + err 'os-probe: Unable to get the FreeBSD version' + fi + fi + + osName=$(echo "$osName" | tr '[:upper:]' '[:lower:]') + echo "$pkgMngr:$osName:$osVersion" } -echoErr () { - echo "$*" 1>&2; +help_unit_ps() +{ + cat <<__EOF__ ; +SYNOPSIS + $program_name ps [-h] [-t TYPE] + +DESCRIPTION + List unitd(8) processes. + +OPTIONS + -h, --help + Print this help. + + -t, --type TYPE + List only processes of type TYPE. The available types are: + + - controller (c) + - main (m) + - router (r) + +__EOF__ } -echoDebug () { - if [ "$debug" -eq 1 ]; then - echo "$@" 1>&2; - fi +unit_ps() +{ + while test $# -ge 1; do + case "$1" in + -h | --help) + help_unit_ps; + exit 0; + ;; + -t | --type) + if ! test $# -ge 2; then + err "ps: $1: Missing argument."; + fi; + local type=; + case "$2" in + c | controller) + local type_c='c'; + ;; + m | main) + local type_m='m'; + ;; + r | router) + local type_r='r'; + ;; + esac; + shift; + ;; + -*) + err "ps: $1: Unknown option."; + ;; + *) + err "ps: $1: Unknown argument."; + ;; + esac; + shift; + done; + + ps ax \ + | if test -v type; then + grep ${type_c:+-e 'unit: controller'} \ + ${type_m:+-e 'unit: main'} \ + ${type_r:+-e 'unit: router'}; + else + grep 'unit: '; + fi \ + | grep -v grep \ + ||: } -main() { -debug=0 # If set to 1, debug message will be displayed -checkOSPrereqs +help_unit_repo_config() +{ + cat <<__EOF__ ; +SYNOPSIS + $program_name repo-config [-hn] [PKG-MANAGER OS-NAME OS-VERSION] -# The name and location of the files that will be used to get Linux -# release info -osRelease="/etc/os-release" +DESCRIPTION + This script configures the NGINX Unit repository for the system + package manager. -os="" # Will be "linux" or "freebsd" -osName="" # Will be "ubuntu", "debian", "rhel", - # "centos", "suse", "amzn", or "freebsd" -osVersion="" + The script automatically detects your OS, and works accordingly. + However, in case the automatic selection fails, you may specify the + package manager and the OS name and version. -am_i_root +ARGUMENTS + PKG-MANAGER + Supported: 'apt', 'dnf', and 'yum'. -echo "This script will setup repositories for NGINX Unit" + OS-NAME + Supported: 'debian', 'ubuntu', 'fedora', 'rhel', and 'amzn2'. -# Check the OS -osNameVersion=$(getOS) -if [ -z "$osNameVersion" ]; then - echoErr "Error getting the operating system information" - exit 1 -fi + OS-VERSION + For most distributions this should be a numeric value, but for + debian derivatives, the codename should be used. -# Break out the OS, name, and version -os=$(echo "$osNameVersion" | awk -F: '{print $1}') -osName=$(echo "$osNameVersion" | awk -F: '{print $2}') -osVersion=$(echo "$osNameVersion" | awk -F: '{print $3}') +OPTIONS + -h, --help + Print this help. -# Call the appropriate installation function -case "$osName" in - debian) - installDebian - ;; - ubuntu) - installUbuntu - ;; - rhel) - installRedHat + -n, --dry-run + Dry run. Print the commands to be run instea of actually + running them. Each command is preceded by a line explaining + what it does. + +EXAMPLES + $ $prog_name repo-config apt debian bullseye; + $ $prog_name repo-config apt ubuntu jammy; + $ $prog_name repo-config dnf fedora 36; + $ $prog_name repo-config dnf rhel 9; + $ $prog_name repo-config yum amzn2 2; + +__EOF__ +} + + +unit_repo_config() +{ + installAPT () + { + local os_name="$2"; + + dry_run_echo "Install on $os_name"; + dry_run_echo; + dry_run_eval 'curl --output /usr/share/keyrings/nginx-keyring.gpg https://unit.nginx.org/keys/nginx-keyring.gpg;'; + dry_run_echo; + dry_run_eval 'apt-get install -y apt-transport-https lsb-release ca-certificates;'; + + if test $# -ge 3; then + local os_version="$3"; + else + local os_version='$(lsb_release -cs)'; + fi; + + dry_run_echo; + dry_run_eval "printf 'deb [signed-by=/usr/share/keyrings/nginx-keyring.gpg] https://packages.nginx.org/unit/$os_name/ %s unit\n' \"$os_version\" | tee /etc/apt/sources.list.d/unit.list;"; + dry_run_eval "printf 'deb-src [signed-by=/usr/share/keyrings/nginx-keyring.gpg] https://packages.nginx.org/unit/$os_name/ %s unit\n' \"$os_version\" | tee -a /etc/apt/sources.list.d/unit.list;"; + dry_run_echo; + dry_run_eval 'apt-get update;'; + } + + installYumDnf () + { + local pkg_mngr="$1"; + local os_name="$2"; + + if test $# -ge 3; then + local os_version="$3"; + else + local os_version='\$releasever'; + fi; + + dry_run_echo "Install on $os_name"; + dry_run_echo; + + dry_run_eval "cat >/etc/yum.repos.d/unit.repo <<__EOF__ +[unit] +name=unit repo +baseurl=https://packages.nginx.org/unit/$os_name/$os_version/\\\$basearch/ +gpgcheck=0 +enabled=1 +__EOF__"; + + dry_run_echo; + dry_run_eval "$pkg_mngr makecache;"; + } + + while test $# -ge 1; do + case "$1" in + -h | --help) + help_unit_repo_config; + exit 0; + ;; + -n | --dry-run) + dry_run='yes'; + ;; + -*) + err "repo-config: $1: Unknown option."; + ;; + *) + break; + ;; + esac; + shift; + done; + + if test $# -ge 1; then + local pkg_mngr="$1"; + + if ! test $# -ge 2; then + err "repo-config: OS-NAME: Missing argument."; + fi; + local os_name="$2"; + + if ! test $# -ge 3; then + err "repo-config: OS-VERSION: Missing argument."; + fi; + local os_version="$3"; + fi; + + command -v curl >/dev/null \ + || err 'repo-config: curl(1) not found in PATH. It must be installed to run this script.'; + + id -u \ + | xargs test 0 -ne \ + && err 'repo-config: This script requires root privileges to run.'; + + echo 'This script sets up the NGINX Unit repository'; + + if ! test $# -ge 3; then + local os_pkg_name_version=$(unit_os_probe || warn "On macOS, try 'brew install nginx/unit/unit'.") + local pkg_mngr=$(echo "$os_pkg_name_version" | awk -F: '{print $1}') + local os_name=$(echo "$os_pkg_name_version" | awk -F: '{print $2}') + local os_version=$(echo "$os_pkg_name_version" | awk -F: '{print $3}') + fi; + + # Call the appropriate installation function + case "$pkg_mngr" in + apt) + case "$os_name" in + debian | ubuntu) + installAPT "$pkg_mngr" "$os_name" ${3:+$os_version}; + ;; + *) + err "repo-config: $os_name: The OS isn't supported"; + ;; + esac ;; - centos) - installRedHat + yum | dnf) + case "$os_name" in + rhel | amzn | fedora) + installYumDnf "$pkg_mngr" "$os_name" "$os_version" ${3:+ovr}; + ;; + *) + err "repo-config: $os_name: The OS isn't supported"; + ;; + esac; ;; - ol) - installRedHat + *) + err "repo-config: $pkg_mngr: The package manager isn't supported"; ;; - amzn) - installAmazon + esac; + + echo + echo 'All done; the NGINX Unit repository is set up.'; + echo "Configured with '$pkg_mngr' on '$os_name' '$os_version'."; + echo 'Further steps: ' +} + + +help_unit_sock() +{ + cat <<__EOF__ ; +SYNOPSIS + $program_name sock [-h] SUBCOMMAND [ARGS] + + Subcommands + +-- filter [-ch] + +-- find [-h] + +DESCRIPTION + Print the address of the control API socket of running instances of + unitd(8). + + Run '$program_name sock SUBCOMMAND -h' for more information on a + subcommand. + +SUBCOMMANDS + filter Filter the output of the 'find' subcommand, and transform it + to something suitable to run other commands, such as curl(1) + or ssh(1). + + find Find and print the address of the control API socket of + running instances of unitd(8). + +OPTIONS + -h, --help + Print this help. + +__EOF__ +} + + +unit_sock() +{ + while test $# -ge 1; do + case "$1" in + -h | --help) + help_unit_sock; + exit 0; + ;; + -*) + err "sock: $1: Unknown option."; + ;; + *) + break; + ;; + esac; + shift; + done; + + if ! test $# -ge 1; then + err 'sock: Missing subcommand.'; + fi; + + case $1 in + filter) + shift; + unit_sock_filter $@; ;; - fedora) - installFedora + find) + shift; + unit_sock_find $@; ;; *) - echo "$osName is not supported" - exit 1 + err "sock: $1: Unknown subcommand."; ;; -esac + esac; +} + -echo -echo "All done - NGINX Unit repositories for "$osName" "$osVersion" are set up" -echo "Further steps: https://unit.nginx.org/installation/#official-packages" +help_unit_sock_filter() +{ + cat <<__EOF__ ; +SYNOPSIS + $program_name sock filter [-chs] +DESCRIPTION + Filter the output of the 'sock find' command, and transform it to + something suitable to run other commands, such as curl(1) or ssh(1). + +OPTIONS + -c, --curl + Print an argument suitable for curl(1). + + -h, --help + Print this help. + + -s, --ssh + Print a socket address suitable for use in an ssh(1) tunnel. + +__EOF__ +} + + +unit_sock_filter() +{ + while test $# -ge 1; do + case "$1" in + -c | --curl) + if test -v ssh_flag; then + err "sock: filter: $1: Missing argument."; + fi; + local curl_flag='yes'; + ;; + -h | --help) + help_unit_sock_filter; + exit 0; + ;; + -s | --ssh) + if test -v curl_flag; then + err "sock: filter: $1: Missing argument."; + fi; + local ssh_flag='yes'; + ;; + -*) + err "sock: filter: $1: Unknown option."; + ;; + *) + err "sock: filter: $1: Unknown argument."; + ;; + esac; + shift; + done; + + while read -r control; do + + if test -v curl_flag; then + if echo "$control" | grep '^unix:' >/dev/null; then + unix_socket="$(echo "$control" | sed 's/unix:/--unix-socket /')"; + host='http://localhost'; + else + unix_socket=''; + host="$control"; + fi; + + echo "$unix_socket $host"; + + elif test -v ssh_flag; then + echo "$control" \ + | sed -E 's,^(unix:|http://|https://),,'; + + else + echo "$control"; + fi; + done; } -main -exit 0 +help_unit_sock_find() +{ + cat <<__EOF__ ; +SYNOPSIS + $program_name sock find [-h] + +DESCRIPTION + Find and print the address of the control API socket of running + instances of unitd(8). + +OPTIONS + -h, --help + Print this help. + +__EOF__ +} + + +unit_sock_find() +{ + while test $# -ge 1; do + case "$1" in + -h | --help) + help_unit_sock_find; + exit 0; + ;; + -*) + err "sock: find: $1: Unknown option."; + ;; + *) + err "sock: find: $1: Unknown argument."; + ;; + esac; + shift; + done; + + unit_cmd \ + | while read -r cmd; do + if echo "$cmd" | grep '\--control' >/dev/null; then + echo "$cmd" \ + | sed 's/ --/\n--/g' \ + | grep '\--control' \ + | cut -d' ' -f2; + else + if ! command -v $cmd >/dev/null; then + local cmd='unitd'; + fi; + $cmd --help \ + | sed -n '/\--control/,+1p' \ + | grep 'default:' \ + | sed 's/ *default: "\(.*\)"/\1/'; + fi; + done; +} + + +while test $# -ge 1; do + case "$1" in + -h | --help) + help_unit; + exit 0; + ;; + --help-more) + help_more_unit; + exit 0; + ;; + -*) + err "$1: Unknown option."; + ;; + *) + break; + ;; + esac; + shift; +done; + +if ! test $# -ge 1; then + err "Missing command."; +fi; + +case $1 in +cmd) + shift; + unit_cmd $@; + ;; +ctl) + shift; + unit_ctl $@; + ;; +freeport) + shift; + unit_freeport $@; + ;; +json-ins) + shift; + unit_json_ins $@; + ;; +os-probe) + shift; + unit_os_probe $@; + ;; +ps) + shift; + unit_ps $@; + ;; +repo-config) + shift; + unit_repo_config $@; + ;; +sock) + shift; + unit_sock $@; + ;; +welcome) + shift; + unit_ctl_welcome $@; + ;; +*) + err "$1: Unknown command."; + ;; +esac; -- cgit From 11c66941ce9146ca85533e8a3169ef3941340896 Mon Sep 17 00:00:00 2001 From: Konstantin Pavlov Date: Tue, 29 Nov 2022 18:12:54 +0400 Subject: Added contribs and njs. --- pkg/Makefile | 4 ++ pkg/contrib/Makefile | 140 +++++++++++++++++++++++++++++++++++++++++ pkg/contrib/src/njs/Makefile | 19 ++++++ pkg/contrib/src/njs/SHA512SUMS | 1 + pkg/contrib/src/njs/version | 1 + pkg/contrib/tarballs/.hgignore | 3 + 6 files changed, 168 insertions(+) create mode 100644 pkg/contrib/Makefile create mode 100644 pkg/contrib/src/njs/Makefile create mode 100644 pkg/contrib/src/njs/SHA512SUMS create mode 100644 pkg/contrib/src/njs/version create mode 100644 pkg/contrib/tarballs/.hgignore diff --git a/pkg/Makefile b/pkg/Makefile index 4cf9ff80..c252969b 100644 --- a/pkg/Makefile +++ b/pkg/Makefile @@ -29,11 +29,15 @@ docker: npm: @cd npm && VERSION=$(VERSION) RELEASE=$(RELEASE) make all +njs: + @cd contrib && make .njs + clean: @cd rpm && make clean @cd deb && make clean @cd docker && make clean @cd npm && make clean + @cd contrib && make clean rm -f unit-$(VERSION).tar.gz rm -f unit-$(VERSION).tar.gz.sha512 diff --git a/pkg/contrib/Makefile b/pkg/contrib/Makefile new file mode 100644 index 00000000..7e3b8b97 --- /dev/null +++ b/pkg/contrib/Makefile @@ -0,0 +1,140 @@ +all: install + +TOPSRC := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) +SRC := $(TOPSRC)/src +TARBALLS := $(TOPSRC)/tarballs +VPATH := $(TARBALLS) +PREFIX = $(TOPSRC)/local +PREFIX := $(abspath $(PREFIX)) + +PKGS_ALL := $(patsubst $(SRC)/%/Makefile,%,$(wildcard $(SRC)/*/Makefile)) + +# Common download locations +CONTRIB_NGINX := https://packages.nginx.org/contrib + +# +# Tools +# +NPROC := $(shell getconf _NPROCESSORS_ONLN) +_SMP_MFLAGS := -j$(NPROC) + +ifndef GIT +ifeq ($(shell git --version >/dev/null 2>&1 || echo FAIL),) +GIT = git +endif +endif +GIT ?= $(error git not found) + +ifeq ($(shell curl --version >/dev/null 2>&1 || echo FAIL),) +download = curl -f -L -- "$(1)" > "$@" +else ifeq ($(shell wget --version >/dev/null 2>&1 || echo FAIL),) +download = (rm -f $@.tmp && \ + wget --passive -c -p -O $@.tmp "$(1)" && \ + touch $@.tmp && \ + mv $@.tmp $@ ) +else ifeq ($(which fetch >/dev/null 2>&1 || echo FAIL),) +download = (rm -f $@.tmp && \ + fetch -p -o $@.tmp "$(1)" && \ + touch $@.tmp && \ + mv $@.tmp $@) +else +download = $(error Neither curl nor wget found) +endif + +download_pkg = $(call download,$(CONTRIB_NGINX)/$(2)/$(lastword $(subst /, ,$(@)))) || \ + ( $(call download,$(1)) && echo "Please upload $(lastword $(subst /, ,$(@))) to $(CONTRIB_NGINX)" ) + +ifeq ($(shell which xz >/dev/null 2>&1 || echo FAIL),) +XZ = xz +else +XZ ?= $(error XZ (LZMA) compressor not found) +endif + +ifeq ($(shell sha512sum --version >/dev/null 2>&1 || echo FAIL),) +SHA512SUM = sha512sum --check +else ifeq ($(shell shasum --version >/dev/null 2>&1 || echo FAIL),) +SHA512SUM = shasum -a 512 --check +else ifeq ($(shell openssl version >/dev/null 2>&1 || echo FAIL),) +SHA512SUM = openssl dgst -sha512 +else +SHA512SUM = $(error SHA-512 checksumming not found) +endif + +# +# Common helpers +# +download_git = \ + rm -Rf -- "$(@:.tar.xz=)" && \ + $(GIT) init --bare "$(@:.tar.xz=)" && \ + (cd "$(@:.tar.xz=)" && \ + $(GIT) remote add origin "$(1)" && \ + $(GIT) fetch origin "$(2)") && \ + (cd "$(@:.tar.xz=)" && \ + $(GIT) archive --prefix="$(notdir $(@:.tar.xz=))/" \ + --format=tar "$(3)") > "$(@:.xz=)" && \ + echo "$(3) $(@)" > "$(@:.tar.xz=.githash)" && \ + rm -Rf -- "$(@:.tar.xz=)" && \ + $(XZ) --stdout "$(@:.xz=)" > "$@.tmp" && \ + rm -f "$(@:.xz=)" && \ + mv -f -- "$@.tmp" "$@" +check_githash = \ + h=`sed -e "s,^\([0-9a-fA-F]\{40\}\) .*/$(notdir $<),\1,g" \ + < "$(<:.tar.xz=.githash)"` && \ + test "$$h" = "$1" + +checksum = \ + $(foreach f,$(filter $(TARBALLS)/%,$^), \ + grep -- " $(f:$(TARBALLS)/%=%)$$" \ + "$(SRC)/$(patsubst $(3)%,%,$@)/$(2)SUMS" |) \ + (cd $(TARBALLS) && $(1)) +CHECK_SHA512 = $(call checksum,$(SHA512SUM),SHA512,.sum-) +UNPACK = $(RM) -R $@ \ + $(foreach f,$(filter %.tar.gz %.tgz,$^), && tar xvzfo $(f)) \ + $(foreach f,$(filter %.tar.bz2,$^), && tar xvjfo $(f)) \ + $(foreach f,$(filter %.tar.xz,$^), && tar xvJfo $(f)) \ + $(foreach f,$(filter %.zip,$^), && unzip $(f)) +UNPACK_DIR = $(patsubst %.tar,%,$(basename $(notdir $<))) +APPLY = (cd $(UNPACK_DIR) && patch -fp1) < +MOVE = mv $(UNPACK_DIR) $@ && touch $@ + +# Per-package build rules +include $(SRC)/*/Makefile + +# Targets +PKGS_DEPS := $(sort $(foreach p,$(PKGS),$(DEPS_$(p)))) + +fetch: $(PKGS:%=.sum-%) +install: $(PKGS:%=.%) + +clean: + -$(RM) $(foreach p,$(PKGS),.$(p) .sum-$(p) .dep-$(p)) + -$(RM) -R $(foreach p,$(PKGS),$(p)) + -$(RM) -R "$(PREFIX)" + -$(RM) $(TARBALLS)/*.* + +list: + @echo Packages: + @echo ' $(PKGS)' | tr " " "\n" | sort | tr "\n" " " |fmt + @echo Depended-on packages: + @echo ' $(PKGS_DEPS)' | tr " " "\n" | sort | tr "\n" " " |fmt + +.PHONY: all fetch install clean list + +# Default pattern rules +.sum-%: $(SRC)/%/SHA512SUMS + $(CHECK_SHA512) + touch $@ + +.sum-%: + $(error Download and check target not defined for $*) + +# Real dependency on missing packages +$(patsubst %,.dep-%,$(PKGS)): .dep-%: .% + touch -r $< $@ + +.SECONDEXPANSION: + +# Dependency propagation (convert 'DEPS_foo = bar' to '.foo: .bar') +$(foreach p,$(PKGS),.$(p)): .%: $$(foreach d,$$(DEPS_$$*),.dep-$$(d)) + +.DELETE_ON_ERROR: diff --git a/pkg/contrib/src/njs/Makefile b/pkg/contrib/src/njs/Makefile new file mode 100644 index 00000000..54255aef --- /dev/null +++ b/pkg/contrib/src/njs/Makefile @@ -0,0 +1,19 @@ +# njs + +include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/version +NJS_URL := https://hg.nginx.org/njs/archive/$(NJS_VERSION).tar.gz + +PKGS += njs + +$(TARBALLS)/njs-$(NJS_VERSION).tar.gz: + $(call download_pkg,$(NJS_URL),njs) + +.sum-njs: njs-$(NJS_VERSION).tar.gz + +njs: njs-$(NJS_VERSION).tar.gz .sum-njs + $(UNPACK) + $(MOVE) + +.njs: njs + cd $< && ./configure && $(MAKE) libnjs + touch $@ diff --git a/pkg/contrib/src/njs/SHA512SUMS b/pkg/contrib/src/njs/SHA512SUMS new file mode 100644 index 00000000..1bddec9b --- /dev/null +++ b/pkg/contrib/src/njs/SHA512SUMS @@ -0,0 +1 @@ +dc73029e7b570a7fbc94e90deb1e17c9a3d85072dc0e060f11dd96bd173e11b7c823c57115369d3c68af7acd97fabe619b70dfd73280694f8b5dc8b7929d850b njs-0.7.9.tar.gz diff --git a/pkg/contrib/src/njs/version b/pkg/contrib/src/njs/version new file mode 100644 index 00000000..511715d0 --- /dev/null +++ b/pkg/contrib/src/njs/version @@ -0,0 +1 @@ +NJS_VERSION := 0.7.9 diff --git a/pkg/contrib/tarballs/.hgignore b/pkg/contrib/tarballs/.hgignore new file mode 100644 index 00000000..8d876d7b --- /dev/null +++ b/pkg/contrib/tarballs/.hgignore @@ -0,0 +1,3 @@ +syntax:glob +*.tar.* +*.githash -- cgit From 24e3f171029da265ee8cc20fe1a8846a0b49cb43 Mon Sep 17 00:00:00 2001 From: Konstantin Pavlov Date: Wed, 7 Dec 2022 18:20:44 -0800 Subject: Packages: added njs support. --- pkg/deb/Makefile | 10 +++++++--- pkg/deb/debian/control.in | 3 ++- pkg/deb/debian/rules.in | 9 ++++++++- pkg/rpm/Makefile | 10 +++++++--- pkg/rpm/unit.spec.in | 6 ++++++ 5 files changed, 30 insertions(+), 8 deletions(-) diff --git a/pkg/deb/Makefile b/pkg/deb/Makefile index e9cf53b3..580cb655 100644 --- a/pkg/deb/Makefile +++ b/pkg/deb/Makefile @@ -115,7 +115,7 @@ include Makefile.jsc-common include Makefile.jsc11 endif -CONFIGURE_ARGS=\ +CONFIGURE_ARGS_COMMON=\ --prefix=/usr \ --state=/var/lib/unit \ --control="unix:/var/run/control.unit.sock" \ @@ -127,6 +127,10 @@ CONFIGURE_ARGS=\ --tests \ --openssl +CONFIGURE_ARGS=\ + $(CONFIGURE_ARGS_COMMON) \ + --njs + export CR=\\n default: @@ -195,7 +199,7 @@ endif debuild/unit_$(VERSION).orig.tar.gz: | debuild/$(SRCDIR)/debian cd ../.. && tar -czf pkg/deb/debuild/$(SRCDIR).tar.gz \ --transform "s#^#$(SRCDIR)/#" \ - LICENSE NOTICE CHANGES README.md CONTRIBUTING.md configure auto src test version go docs/man/unitd.8.in + LICENSE NOTICE CHANGES README.md CONTRIBUTING.md configure auto src test version go pkg/contrib docs/man/unitd.8.in mv debuild/$(SRCDIR).tar.gz debuild/unit_$(VERSION).orig.tar.gz cd debuild && tar zxf unit_$(VERSION).orig.tar.gz @@ -257,7 +261,7 @@ endif -e "s#%%CODENAME%%#$(CODENAME)#g" \ -e "s#%%UNIT_VERSION%%#$(VERSION)#g" \ -e "s#%%UNIT_RELEASE%%#$(RELEASE)#g" \ - -e "s#%%CONFIGURE_ARGS%%#$(CONFIGURE_ARGS)#g" \ + -e "s#%%CONFIGURE_ARGS%%#$(CONFIGURE_ARGS_COMMON)#g" \ -e "s#%%MODULE_CONFARGS%%#$(MODULE_CONFARGS_$*)#g" \ -e "s#%%MODULE_MAKEARGS%%#$(MODULE_MAKEARGS_$*)#g" \ -e "s#%%MODULE_INSTARGS%%#$(MODULE_INSTARGS_$*)#g" \ diff --git a/pkg/deb/debian/control.in b/pkg/deb/debian/control.in index acf834bc..579f41e3 100644 --- a/pkg/deb/debian/control.in +++ b/pkg/deb/debian/control.in @@ -5,7 +5,8 @@ Maintainer: %%PACKAGE_VENDOR%% Build-Depends: debhelper (>= 11), linux-libc-dev, libssl-dev, - libpcre2-dev + libpcre2-dev, + pkg-config Standards-Version: 4.1.4 Homepage: https://unit.nginx.org diff --git a/pkg/deb/debian/rules.in b/pkg/deb/debian/rules.in index d2e34796..23812926 100644 --- a/pkg/deb/debian/rules.in +++ b/pkg/deb/debian/rules.in @@ -20,7 +20,12 @@ BASEDIR = $(CURDIR) DOTESTS = 0 -config.env.%: +njs: + dh_testdir + cd pkg/contrib && make .njs + touch $@ + +config.env.%: njs dh_testdir mkdir -p $(BUILDDIR_$*) cp -Pa $(CURDIR)/auto $(BUILDDIR_$*)/ @@ -40,6 +45,7 @@ config.env.%: configure.unit: config.env.unit cd $(BUILDDIR_unit) && \ + PKG_CONFIG_PATH=$(CURDIR)/pkg/contrib/njs/build \ CFLAGS= ./configure \ %%CONFIGURE_ARGS%% \ --modules=/usr/lib/unit/modules \ @@ -50,6 +56,7 @@ configure.unit: config.env.unit configure.unit_debug: config.env.unit_debug cd $(BUILDDIR_unit_debug) && \ + PKG_CONFIG_PATH=$(CURDIR)/pkg/contrib/njs/build \ CFLAGS= ./configure \ %%CONFIGURE_ARGS%% \ --modules=/usr/lib/unit/debug-modules \ diff --git a/pkg/rpm/Makefile b/pkg/rpm/Makefile index a1ac2af9..d00a25ac 100644 --- a/pkg/rpm/Makefile +++ b/pkg/rpm/Makefile @@ -106,7 +106,7 @@ include Makefile.jsc8 include Makefile.jsc11 endif -CONFIGURE_ARGS=\ +CONFIGURE_ARGS_COMMON=\ --prefix=/usr \ --state=%{_sharedstatedir}/unit \ --control="unix:/var/run/unit/control.sock" \ @@ -118,6 +118,10 @@ CONFIGURE_ARGS=\ --tests \ --openssl +CONFIGURE_ARGS=\ + $(CONFIGURE_ARGS_COMMON) \ + --njs + export CR=\\n default: @@ -176,7 +180,7 @@ endif rpmbuild/SOURCES/unit-$(VERSION).tar.gz: cd ../.. && tar -czf pkg/rpm/rpmbuild/SOURCES/unit-$(VERSION).tar.gz \ --transform "s#^#unit-$(VERSION)/#" \ - LICENSE NOTICE CHANGES README.md CONTRIBUTING.md configure auto src test version go docs/man/unitd.8.in + LICENSE NOTICE CHANGES README.md CONTRIBUTING.md configure auto src test version go pkg/contrib docs/man/unitd.8.in unit: check-build-depends-unit rpmbuild/SPECS/unit.spec rpmbuild/SOURCES/unit-$(VERSION).tar.gz @echo "===> Building $@ package" ; \ @@ -212,7 +216,7 @@ rpmbuild/SPECS/unit-%.spec: unit.module.spec.in ../../docs/changes.xml | rpmbuil -e "s#%%UNIT_RELEASE%%#$(RELEASE)#g" \ -e "s#%%PACKAGE_VENDOR%%#$(PACKAGE_VENDOR)#g" \ -e "s#%%MODULE_SOURCES%%#$${sources}#g" \ - -e "s#%%CONFIGURE_ARGS%%#$(CONFIGURE_ARGS)#g" \ + -e "s#%%CONFIGURE_ARGS%%#$(CONFIGURE_ARGS_COMMON)#g" \ -e "s#%%MODULE_CONFARGS%%#$(MODULE_CONFARGS_$*)#g" \ -e "s#%%MODULE_MAKEARGS%%#$(MODULE_MAKEARGS_$*)#g" \ -e "s#%%MODULE_INSTARGS%%#$(MODULE_INSTARGS_$*)#g" \ diff --git a/pkg/rpm/unit.spec.in b/pkg/rpm/unit.spec.in index 50eee876..fddabb69 100644 --- a/pkg/rpm/unit.spec.in +++ b/pkg/rpm/unit.spec.in @@ -46,6 +46,7 @@ Requires(preun): systemd Requires(postun): systemd BuildRequires: pcre2-devel +BuildRequires: pkg-config Provides: unit-r%{version} @@ -74,6 +75,9 @@ Library and include files required for NGINX Unit modules development. %setup -q %build +%{__make} %{?_smp_mflags} -C pkg/contrib .njs + +PKG_CONFIG_PATH=%{bdir}/pkg/contrib/njs/build \ ./configure \ %{CONFIGURE_ARGS} \ --modules=%{_libdir}/unit/debug-modules \ @@ -84,6 +88,8 @@ Library and include files required for NGINX Unit modules development. %{__make} %{?_smp_mflags} %{__make} %{?_smp_mflags} build/libunit.a %{__mv} build build-debug + +PKG_CONFIG_PATH=%{bdir}/pkg/contrib/njs/build \ ./configure \ %{CONFIGURE_ARGS} \ --modules=%{_libdir}/unit/modules \ -- cgit From 4b39cb1fc75b42e944e41a522dbdf3d539eff8db Mon Sep 17 00:00:00 2001 From: Andrei Zeliankou Date: Wed, 14 Dec 2022 19:00:14 +0000 Subject: Tests: added tests for "path" option in isolation/cgroup. --- test/test_python_isolation.py | 123 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) diff --git a/test/test_python_isolation.py b/test/test_python_isolation.py index db398444..6d4ffaf3 100644 --- a/test/test_python_isolation.py +++ b/test/test_python_isolation.py @@ -1,3 +1,8 @@ +import os +import re +import subprocess +from pathlib import Path + import pytest from unit.applications.lang.python import TestApplicationPython from unit.option import option @@ -9,6 +14,23 @@ from unit.utils import waitforunmount class TestPythonIsolation(TestApplicationPython): prerequisites = {'modules': {'python': 'any'}, 'features': ['isolation']} + def get_cgroup(self, app_name): + output = subprocess.check_output( + ['ps', 'ax', '-o', 'pid', '-o', 'cmd'] + ).decode() + + pid = re.search( + r'(\d+)\s*unit: "' + app_name + '" application', output + ).group(1) + + cgroup = '/proc/' + pid + '/cgroup' + + if not os.path.isfile(cgroup): + pytest.skip('no cgroup at ' + cgroup) + + with open(cgroup, 'r') as f: + return f.read().rstrip() + def test_python_isolation_rootfs(self, is_su, temp_dir): isolation_features = option.available['features']['isolation'].keys() @@ -102,3 +124,104 @@ class TestPythonIsolation(TestApplicationPython): assert ( self.getjson(url='/?path=/proc/self')['body']['FileExists'] == True ), '/proc/self' + + def test_python_isolation_cgroup(self, is_su, temp_dir): + if not is_su: + pytest.skip('requires root') + + if not 'cgroup' in option.available['features']['isolation']: + pytest.skip('cgroup is not supported') + + def set_cgroup_path(path): + isolation = {'cgroup': {'path': path}} + self.load('empty', processes=1, isolation=isolation) + + set_cgroup_path('scope/python') + + cgroup_rel = Path(self.get_cgroup('empty')) + assert cgroup_rel.parts[-2:] == ('scope', 'python'), 'cgroup rel' + + set_cgroup_path('/scope2/python') + + cgroup_abs = Path(self.get_cgroup('empty')) + assert cgroup_abs.parts[-2:] == ('scope2', 'python'), 'cgroup abs' + + assert len(cgroup_rel.parts) >= len(cgroup_abs.parts) + + def test_python_isolation_cgroup_two(self, is_su, temp_dir): + if not is_su: + pytest.skip('requires root') + + if not 'cgroup' in option.available['features']['isolation']: + pytest.skip('cgroup is not supported') + + def set_two_cgroup_path(path, path2): + script_path = option.test_dir + '/python/empty' + + assert 'success' in self.conf( + { + "listeners": { + "*:7080": {"pass": "applications/one"}, + "*:7081": {"pass": "applications/two"}, + }, + "applications": { + "one": { + "type": "python", + "processes": 1, + "path": script_path, + "working_directory": script_path, + "module": "wsgi", + "isolation": { + 'cgroup': {'path': path}, + }, + }, + "two": { + "type": "python", + "processes": 1, + "path": script_path, + "working_directory": script_path, + "module": "wsgi", + "isolation": { + 'cgroup': {'path': path2}, + }, + }, + }, + } + ) + + set_two_cgroup_path('/scope/python', '/scope/python') + assert self.get_cgroup('one') == self.get_cgroup('two') + + set_two_cgroup_path('/scope/python', '/scope2/python') + assert self.get_cgroup('one') != self.get_cgroup('two') + + def test_python_isolation_cgroup_invalid(self, is_su): + if not is_su: + pytest.skip('requires root') + + if not 'cgroup' in option.available['features']['isolation']: + pytest.skip('cgroup is not supported') + + def check_invalid(path): + script_path = option.test_dir + '/python/empty' + assert 'error' in self.conf( + { + "listeners": {"*:7080": {"pass": "applications/empty"}}, + "applications": { + "empty": { + "type": "python", + "processes": {"spare": 0}, + "path": script_path, + "working_directory": script_path, + "module": "wsgi", + "isolation": { + 'cgroup': {'path': path}, + }, + } + }, + } + ) + + check_invalid('') + check_invalid('../scope') + check_invalid('scope/../python') -- cgit From cf3ffb8cf3621486afa3d6bf6b83f749352f2e13 Mon Sep 17 00:00:00 2001 From: Konstantin Pavlov Date: Wed, 14 Dec 2022 11:52:58 -0800 Subject: Packages: Used a more common name for pkg-config. pkg-config package is named differently on supported rpm-based systems: - Amazon Linux 2 has pkgconfig - Fedora has pkgconf-pkg-config - RHEL 7 has pkgconfig - RHEL 8 and 9 have pkgconfig-pkg-config What they share in common is they all provide 'pkgconfig', which we can use in the spec file so we don't have to specify it per-OS. --- pkg/rpm/unit.spec.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/rpm/unit.spec.in b/pkg/rpm/unit.spec.in index fddabb69..06880fcf 100644 --- a/pkg/rpm/unit.spec.in +++ b/pkg/rpm/unit.spec.in @@ -46,7 +46,7 @@ Requires(preun): systemd Requires(postun): systemd BuildRequires: pcre2-devel -BuildRequires: pkg-config +BuildRequires: pkgconfig Provides: unit-r%{version} -- cgit From 789095b8a04fcf16110f436c271213af054411cf Mon Sep 17 00:00:00 2001 From: Artem Konev Date: Wed, 14 Dec 2022 21:17:01 +0000 Subject: Tools: Updated built-in 'setup-unit' help, README.md command lines. --- README.md | 10 +++-- tools/setup-unit | 129 +++++++++++++++++++++++++++---------------------------- 2 files changed, 69 insertions(+), 70 deletions(-) diff --git a/README.md b/README.md index 61d5dd45..9ec7eca8 100644 --- a/README.md +++ b/README.md @@ -50,8 +50,9 @@ For a description of image tags, see the ### Amazon Linux, Fedora, RedHat ``` console -$ curl -sL 'https://unit.nginx.org/_downloads/setup-unit.sh' | sudo -E bash -# yum install unit +$ wget https://raw.githubusercontent.com/nginx/unit/master/tools/setup-unit && chmod +x setup-unit +# ./setup-unit repo-config && yum install unit +# ./setup-unit welcome ``` For details and available language packages, see the @@ -61,8 +62,9 @@ For details and available language packages, see the ### Debian, Ubuntu ``` console -$ curl -sL 'https://unit.nginx.org/_downloads/setup-unit.sh' | sudo -E bash -# apt install unit +$ wget https://raw.githubusercontent.com/nginx/unit/master/tools/setup-unit && chmod +x setup-unit +# ./setup-unit repo-config && apt install unit +# ./setup-unit welcome ``` For details and available language packages, see the diff --git a/tools/setup-unit b/tools/setup-unit index 286eef87..79dab850 100755 --- a/tools/setup-unit +++ b/tools/setup-unit @@ -9,10 +9,10 @@ if test -n ${BASH_VERSION} && test "${BASH_VERSINFO[0]}" -eq 3; then - >&2 echo 'Your version of bash(1) is not supported by this script.'; - >&2 echo "You're probably running on MacOS. We recommend that you either"; - >&2 echo 'install a newer version of bash(1), or you run this script with'; - >&2 echo 'another shell, like for example zsh(1):'; + >&2 echo 'Your version of bash(1) isn't supported by this script.'; + >&2 echo "You're probably running on macOS. We recommend that you either"; + >&2 echo 'install a newer version of bash(1) or run this script with'; + >&2 echo 'another shell, such as zsh(1):'; >&2 echo " $ zsh ${SUDO_USER:+sudo }$0 ..."; exit 1; fi; @@ -52,16 +52,16 @@ COMMANDS for later installation. welcome - Creates an initial configuration to serve a welcome web page - for NGINX Unit. + Create an initial configuration to serve a welcome web page + with NGINX Unit. OPTIONS -h, --help Print this help. --help-more - Print help for more commands. They are experimental. This is - not recommended unless you know what you're doing. + Print help for more commands. They are experimental. Using + these isn't recommended, unless you know what you're doing. __EOF__ } @@ -96,7 +96,7 @@ DESCRIPTION COMMANDS cmd Print the invocation line of unitd(8). - ctl Control a running unitd(8) instance through its control socket. + ctl Control a running unitd(8) instance via its control API socket. freeport Print an available TCP port. @@ -106,24 +106,23 @@ COMMANDS array read from a file at a given INDEX. os-probe - This script probes the OS, and prints details about the - version. + Probe the OS and print details about its version. ps List unitd(8) processes. repo-config Configure your package manager with the NGINX Unit - repository for later installation + repository for later installation. - sock Print the address of the API control socket. + sock Print the control API socket address. welcome - Creates an initial configuration to serve a welcome web page - for NGINX Unit. + Create an initial configuration to serve a welcome web page + with NGINX Unit. OPTIONS -h, --help - Print the basic help (some commands are hidden). + Print basic help (some commands are hidden). --help-more Print the hidden help with more commands. @@ -166,7 +165,7 @@ SYNOPSIS $program_name cmd [-h] DESCRIPTION - Print the invocation line of running instances of unitd(8). + Print the invocation line of running unitd(8) instances. OPTIONS -h, --help @@ -210,15 +209,15 @@ SYNOPSIS +-- insert [-h] PATH INDEX DESCRIPTION - Control a running unitd(8) instance through its control socket. + Control a running unitd(8) instance through its control API socket. Run '$program_name ctl SUBCOMMAND -h' for more information on a subcommand. SUBCOMMANDS - http Send an HTTP request to the control socket. + http Send an HTTP request to the control API socket. - insert Insert an element into a specified index in an array in the + insert Insert an element at the specified index into an array in the JSON configuration. OPTIONS @@ -226,12 +225,11 @@ OPTIONS Print this help. -s, --sock SOCK - Use SOCK as the API control socket address. If not specified, - the script will try to find it. This will be used by - subcommands. + Use SOCK as the control API socket address. If not specified, + the script tries to find it. This value is used by subcommands. - The socket can be a tcp(7) socket or a unix(7) socket, and in - the case of a unix(7) socket, it can be local, or it can be in + The socket can be a tcp(7) socket or a unix(7) socket; in + the case of a unix(7) socket, it can exist locally or on a remote machine, accessed through ssh(1). Accepted syntax for SOCK: @@ -239,13 +237,12 @@ OPTIONS ssh://[user@]host[:port]/path/to/control.sock [http[s]://]host[:port] - The last form is less secure than the first two; you should - have a look at: + The last form is less secure than the first two; have a look: ENVIRONMENT - Options take precedence over their equivalent environment variables, - so if both are specified, the option will be used. + Options take precedence over their equivalent environment variables; + if both are specified, the command-line option is used. UNIT_CTL_SOCK Equivalent to the option -s (--sock). @@ -324,9 +321,8 @@ OPTIONS -c, --curl CURLOPT Pass CURLOPT as an option to curl. This script is implemented in terms of curl(1), so it's useful to be able to tweak its - behavior. It can be used multiple times, which will be - appended (and also appended to the contents of - UNIT_CTL_HTTP_CURLOPTS). + behavior. The option can be cumulatively used multiple times + (the result is also appended to UNIT_CTL_HTTP_CURLOPTS). -h, --help Print this help. @@ -425,8 +421,8 @@ SYNOPSIS $program_name ctl [CTL-OPTS] insert [-h] PATH INDEX DESCRIPTION - Insert an element into a specified position (INDEX) in the JSON array - in the unitd(8) configuration API at PATH. + Insert an element at the specified position (INDEX) into the JSON array + located at PATH in unitd(8) control API. The new element is read from standard input. @@ -518,7 +514,7 @@ SYNOPSIS $program_name welcome [-hn] DESCRIPTION - This script tests an NGINX Unit instalation by creating an initial + This script tests an NGINX Unit installation by creating an initial configuration and serving a welcome web page. Recommended for first-time users. @@ -527,7 +523,7 @@ OPTIONS Print this help. -n, --dry-run - Dry run. Print the commands to be run instea of actually + Dry run. Print the commands to be run instead of actually running them. Each command is preceded by a line explaining what it does. @@ -580,7 +576,7 @@ unit_ctl_welcome() if test 0 -eq "$nprocs"; then warn "welcome: NGINX Unit isn't running."; - warn 'For help starting NGINX Unit, see:'; + warn 'For help with starting NGINX Unit, see:'; err " "; elif test 1 -ne "$nprocs"; then err 'welcome: Only one NGINX Unit instance should be running.'; @@ -590,7 +586,7 @@ unit_ctl_welcome() local curl_opt="$(unit_sock_find | unit_sock_filter -c)"; curl $curl_opt/ >/dev/null 2>&1 \ - || err "welcome: Can't reach the control socket."; + || err "welcome: Can't reach the control API socket."; if ! test -v force; then unit_cmd \ @@ -616,8 +612,8 @@ unit_ctl_welcome() if test -e $conffile; then if ! unit_ctl_http ---s "$sock" 'GET' '/config' /dev/null | grep -q '^{}.\?$'; # The '.\?' is for the possible carriage return. then - warn 'welcome: NGINX Unit is already configured. If you are sure you want'; - err 'to overwrite its current configuration, run again with --force.'; + warn 'welcome: NGINX Unit is already configured. To overwrite'; + err 'its current configuration, run the script again with --force.'; fi; fi; fi; @@ -835,7 +831,7 @@ SYNOPSIS ARGUMENTS JSON Path to a JSON file containing a top-level array. - INDEX Position in the array where to insert the element. + INDEX Position in the array to insert the element at. DESCRIPTION Insert a JSON element read from standard input into a JSON array read @@ -906,9 +902,9 @@ SYNOPSIS $program_name os-probe [-h] DESCRIPTION - This script probes the OS, and prints details about the version. It - prints three fields, delimited by ':'; the first is the package manager, - the second is the OS name, and the third is the OS version. + This script probes the OS and prints three fields, delimited by ':'; + the first is the package manager, the second is the OS name, the third + is the OS version. OPTIONS -h, --help @@ -939,7 +935,7 @@ unit_os_probe() local os=$(uname | tr '[:upper:]' '[:lower:]') if [ "$os" != 'linux' ] && [ "$os" != 'freebsd' ]; then - err "os-probe: The OS isn't Linux or FreeBSD, can't proceed." + err "os-probe: The OS isn't Linux or FreeBSD; can't proceed." fi if [ "$os" = 'linux' ]; then @@ -960,14 +956,14 @@ unit_os_probe() local osName=$(grep "^ID=" "$osRelease" | sed s/\"//g | awk -F= '{ print $2 }' ||:) local osVersion=$(grep '^VERSION_ID=' "$osRelease" | sed s/\"//g | awk -F= '{ print $2 }' || lsb_release -cs) else - err "os-probe: Unable to determine OS and version, or the OS isn't supported" + err "os-probe: Unable to determine OS and version, or the OS isn't supported." fi else local pkgMngr='pkg'; local osName=$os local osVersion=$(uname -rs | awk -F '[ -]' '{print $2}' ||:) if [ -z "$osVersion" ]; then - err 'os-probe: Unable to get the FreeBSD version' + err 'os-probe: Unable to get the FreeBSD version.' fi fi @@ -1059,8 +1055,8 @@ DESCRIPTION This script configures the NGINX Unit repository for the system package manager. - The script automatically detects your OS, and works accordingly. - However, in case the automatic selection fails, you may specify the + The script automatically detects the OS and proceeds accordingly. + However, if this automatic selection fails, you may specify the package manager and the OS name and version. ARGUMENTS @@ -1071,15 +1067,15 @@ ARGUMENTS Supported: 'debian', 'ubuntu', 'fedora', 'rhel', and 'amzn2'. OS-VERSION - For most distributions this should be a numeric value, but for - debian derivatives, the codename should be used. + For most distributions, this should be a numeric value; for + Debian derivatives, use the codename instead. OPTIONS -h, --help Print this help. -n, --dry-run - Dry run. Print the commands to be run instea of actually + Dry run. Print the commands to be run instead of actually running them. Each command is preceded by a line explaining what it does. @@ -1202,7 +1198,7 @@ __EOF__"; installAPT "$pkg_mngr" "$os_name" ${3:+$os_version}; ;; *) - err "repo-config: $os_name: The OS isn't supported"; + err "repo-config: $os_name: The OS isn't supported."; ;; esac ;; @@ -1212,12 +1208,12 @@ __EOF__"; installYumDnf "$pkg_mngr" "$os_name" "$os_version" ${3:+ovr}; ;; *) - err "repo-config: $os_name: The OS isn't supported"; + err "repo-config: $os_name: The OS isn't supported."; ;; esac; ;; *) - err "repo-config: $pkg_mngr: The package manager isn't supported"; + err "repo-config: $pkg_mngr: The package manager isn't supported."; ;; esac; @@ -1239,19 +1235,19 @@ SYNOPSIS +-- find [-h] DESCRIPTION - Print the address of the control API socket of running instances of - unitd(8). + Print the control API socket address of running unitd(8) + instances. Run '$program_name sock SUBCOMMAND -h' for more information on a subcommand. SUBCOMMANDS - filter Filter the output of the 'find' subcommand, and transform it - to something suitable to run other commands, such as curl(1) - or ssh(1). + filter Filter the output of the 'find' subcommand and transform it + to something suitable for running other commands, such as + curl(1) or ssh(1). - find Find and print the address of the control API socket of - running instances of unitd(8). + find Find and print the control API socket address of running + unitd(8) instances. OPTIONS -h, --help @@ -1306,8 +1302,9 @@ SYNOPSIS $program_name sock filter [-chs] DESCRIPTION - Filter the output of the 'sock find' command, and transform it to - something suitable to run other commands, such as curl(1) or ssh(1). + Filter the output of the 'sock find' command and transform it to + something suitable for running other commands, such as + curl(1) or ssh(1). OPTIONS -c, --curl @@ -1384,8 +1381,8 @@ SYNOPSIS $program_name sock find [-h] DESCRIPTION - Find and print the address of the control API socket of running - instances of unitd(8). + Find and print the control API socket address of running + unitd(8) instances. OPTIONS -h, --help -- cgit From f65efe73a43680219636e49be77596948c402d90 Mon Sep 17 00:00:00 2001 From: Andrei Zeliankou Date: Thu, 15 Dec 2022 12:30:38 +0000 Subject: Reordered changes for 1.29.0 by significance (subjective). --- docs/changes.xml | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/docs/changes.xml b/docs/changes.xml index 80febc54..c35ed984 100644 --- a/docs/changes.xml +++ b/docs/changes.xml @@ -53,68 +53,68 @@ removed $uri auto-append for "share" when loading configuration. -prefer system crypto policy, instead of hardcoding a default. +prefer system crypto policy instead of hardcoding a default. -support per-application cgroups on Linux. +njs support with the basic syntax of JS template literals. -compatibility with Python 3.11. +support per-application cgroups on Linux. -njs support with the basic syntax of JS template literals. +the $request_time variable contains the request processing time. -compatibility with OpenSSL 3. +"prefix" option in Python applications to set WSGI "SCRIPT_NAME" +and ASGI root-path variables. -compatibility with PHP 8.2. +compatibility with Python 3.11. -compatibility with Node.js 19.0. +compatibility with OpenSSL 3. -support rack v3 in ruby applications. +compatibility with PHP 8.2. -the $request_time variable contains the request processing time. +compatibility with Node.js 19.0. -report the regex status in configure summary. +Ruby Rack v3 support. - + -new "prefix" attribute in Python configurations to set WSGI SCRIPT_NAME -and ASGI root-path variables. +fix error in connection statistics when using proxy. @@ -126,14 +126,20 @@ fix HTTP cookie parsing when the value contains an equals sign. -fix error in connection statistics when using proxy. +PHP directory URLs without a trailing '/' would give a 503 error (fixed with +a 301 re-direct). -PHP directory URLs without a trailing '/' would give a 503 error (fixed with -a 301 re-direct). +missing error checks in the C API. + + + + + +report the regex status in configure summary. @@ -259,7 +265,7 @@ the Ruby application process could crash on SIGINT. -mutex leaks in the C API. +mutex leak in the C API. -- cgit From edd7ebaf60feb00a5bb996c73ce2de41fa4cf6cc Mon Sep 17 00:00:00 2001 From: Andrei Zeliankou Date: Thu, 15 Dec 2022 12:32:46 +0000 Subject: Added version 1.29.0 CHANGES. --- CHANGES | 40 ++++++++++++++++++++++++++++++++++++++++ docs/changes.xml | 4 ++-- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 29cd3301..959fdd32 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,44 @@ +Changes with Unit 1.29.0 15 Dec 2022 + + *) Change: removed $uri auto-append for "share" when loading + configuration. + + *) Change: prefer system crypto policy instead of hardcoding a default. + + *) Feature: njs support with the basic syntax of JS template literals. + + *) Feature: support per-application cgroups on Linux. + + *) Feature: the $request_time variable contains the request processing + time. + + *) Feature: "prefix" option in Python applications to set WSGI + "SCRIPT_NAME" and ASGI root-path variables. + + *) Feature: compatibility with Python 3.11. + + *) Feature: compatibility with OpenSSL 3. + + *) Feature: compatibility with PHP 8.2. + + *) Feature: compatibility with Node.js 19.0. + + *) Feature: Ruby Rack v3 support. + + *) Bugfix: fix error in connection statistics when using proxy. + + *) Bugfix: fix HTTP cookie parsing when the value contains an equals + sign. + + *) Bugfix: PHP directory URLs without a trailing '/' would give a 503 + error (fixed with a 301 re-direct). + + *) Bugfix: missing error checks in the C API. + + *) Bugfix: report the regex status in configure summary. + + Changes with Unit 1.28.0 13 Sep 2022 *) Change: increased the applications' startup timeout. diff --git a/docs/changes.xml b/docs/changes.xml index c35ed984..e569f474 100644 --- a/docs/changes.xml +++ b/docs/changes.xml @@ -16,7 +16,7 @@ unit-jsc14 unit-jsc15 unit-jsc16 unit-jsc17 unit-jsc18 unit-jsc19" ver="1.29.0" rev="1" - date="" time="" + date="2022-12-15" time="18:00:00 +0300" packager="Nginx Packaging <nginx-packaging@f5.com>"> @@ -42,7 +42,7 @@ Initial release of Java 19 module for NGINX Unit. -- cgit From 87a1a9c0d275c6869e50bc9f3dfca1227ec54868 Mon Sep 17 00:00:00 2001 From: Andrei Zeliankou Date: Thu, 15 Dec 2022 12:42:01 +0000 Subject: Generated Dockerfiles for Unit 1.29.0. --- pkg/docker/Dockerfile.go1.19 | 2 +- pkg/docker/Dockerfile.jsc11 | 2 +- pkg/docker/Dockerfile.minimal | 2 +- pkg/docker/Dockerfile.node18 | 2 +- pkg/docker/Dockerfile.perl5.36 | 2 +- pkg/docker/Dockerfile.php8.1 | 2 +- pkg/docker/Dockerfile.python3.11 | 2 +- pkg/docker/Dockerfile.ruby3.1 | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pkg/docker/Dockerfile.go1.19 b/pkg/docker/Dockerfile.go1.19 index 1625b64f..ec2b40da 100644 --- a/pkg/docker/Dockerfile.go1.19 +++ b/pkg/docker/Dockerfile.go1.19 @@ -8,7 +8,7 @@ RUN set -ex \ && mkdir -p /usr/lib/unit/modules /usr/lib/unit/debug-modules \ && hg clone https://hg.nginx.org/unit \ && cd unit \ - && hg up 1.28.0 \ + && hg up 1.29.0 \ && NCPU="$(getconf _NPROCESSORS_ONLN)" \ && DEB_HOST_MULTIARCH="$(dpkg-architecture -q DEB_HOST_MULTIARCH)" \ && CC_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_CFLAGS_MAINT_APPEND="-Wp,-D_FORTIFY_SOURCE=2 -fPIC" dpkg-buildflags --get CFLAGS)" \ diff --git a/pkg/docker/Dockerfile.jsc11 b/pkg/docker/Dockerfile.jsc11 index fe344d0e..b8391997 100644 --- a/pkg/docker/Dockerfile.jsc11 +++ b/pkg/docker/Dockerfile.jsc11 @@ -8,7 +8,7 @@ RUN set -ex \ && mkdir -p /usr/lib/unit/modules /usr/lib/unit/debug-modules \ && hg clone https://hg.nginx.org/unit \ && cd unit \ - && hg up 1.28.0 \ + && hg up 1.29.0 \ && NCPU="$(getconf _NPROCESSORS_ONLN)" \ && DEB_HOST_MULTIARCH="$(dpkg-architecture -q DEB_HOST_MULTIARCH)" \ && CC_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_CFLAGS_MAINT_APPEND="-Wp,-D_FORTIFY_SOURCE=2 -fPIC" dpkg-buildflags --get CFLAGS)" \ diff --git a/pkg/docker/Dockerfile.minimal b/pkg/docker/Dockerfile.minimal index c57379f7..ca3dec01 100644 --- a/pkg/docker/Dockerfile.minimal +++ b/pkg/docker/Dockerfile.minimal @@ -8,7 +8,7 @@ RUN set -ex \ && mkdir -p /usr/lib/unit/modules /usr/lib/unit/debug-modules \ && hg clone https://hg.nginx.org/unit \ && cd unit \ - && hg up 1.28.0 \ + && hg up 1.29.0 \ && NCPU="$(getconf _NPROCESSORS_ONLN)" \ && DEB_HOST_MULTIARCH="$(dpkg-architecture -q DEB_HOST_MULTIARCH)" \ && CC_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_CFLAGS_MAINT_APPEND="-Wp,-D_FORTIFY_SOURCE=2 -fPIC" dpkg-buildflags --get CFLAGS)" \ diff --git a/pkg/docker/Dockerfile.node18 b/pkg/docker/Dockerfile.node18 index 9a474564..bdf968b2 100644 --- a/pkg/docker/Dockerfile.node18 +++ b/pkg/docker/Dockerfile.node18 @@ -8,7 +8,7 @@ RUN set -ex \ && mkdir -p /usr/lib/unit/modules /usr/lib/unit/debug-modules \ && hg clone https://hg.nginx.org/unit \ && cd unit \ - && hg up 1.28.0 \ + && hg up 1.29.0 \ && NCPU="$(getconf _NPROCESSORS_ONLN)" \ && DEB_HOST_MULTIARCH="$(dpkg-architecture -q DEB_HOST_MULTIARCH)" \ && CC_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_CFLAGS_MAINT_APPEND="-Wp,-D_FORTIFY_SOURCE=2 -fPIC" dpkg-buildflags --get CFLAGS)" \ diff --git a/pkg/docker/Dockerfile.perl5.36 b/pkg/docker/Dockerfile.perl5.36 index d0b8006f..9c398c30 100644 --- a/pkg/docker/Dockerfile.perl5.36 +++ b/pkg/docker/Dockerfile.perl5.36 @@ -8,7 +8,7 @@ RUN set -ex \ && mkdir -p /usr/lib/unit/modules /usr/lib/unit/debug-modules \ && hg clone https://hg.nginx.org/unit \ && cd unit \ - && hg up 1.28.0 \ + && hg up 1.29.0 \ && NCPU="$(getconf _NPROCESSORS_ONLN)" \ && DEB_HOST_MULTIARCH="$(dpkg-architecture -q DEB_HOST_MULTIARCH)" \ && CC_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_CFLAGS_MAINT_APPEND="-Wp,-D_FORTIFY_SOURCE=2 -fPIC" dpkg-buildflags --get CFLAGS)" \ diff --git a/pkg/docker/Dockerfile.php8.1 b/pkg/docker/Dockerfile.php8.1 index c63e708e..76c7c428 100644 --- a/pkg/docker/Dockerfile.php8.1 +++ b/pkg/docker/Dockerfile.php8.1 @@ -8,7 +8,7 @@ RUN set -ex \ && mkdir -p /usr/lib/unit/modules /usr/lib/unit/debug-modules \ && hg clone https://hg.nginx.org/unit \ && cd unit \ - && hg up 1.28.0 \ + && hg up 1.29.0 \ && NCPU="$(getconf _NPROCESSORS_ONLN)" \ && DEB_HOST_MULTIARCH="$(dpkg-architecture -q DEB_HOST_MULTIARCH)" \ && CC_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_CFLAGS_MAINT_APPEND="-Wp,-D_FORTIFY_SOURCE=2 -fPIC" dpkg-buildflags --get CFLAGS)" \ diff --git a/pkg/docker/Dockerfile.python3.11 b/pkg/docker/Dockerfile.python3.11 index 73f8614c..3a83ec57 100644 --- a/pkg/docker/Dockerfile.python3.11 +++ b/pkg/docker/Dockerfile.python3.11 @@ -8,7 +8,7 @@ RUN set -ex \ && mkdir -p /usr/lib/unit/modules /usr/lib/unit/debug-modules \ && hg clone https://hg.nginx.org/unit \ && cd unit \ - && hg up 1.28.0 \ + && hg up 1.29.0 \ && NCPU="$(getconf _NPROCESSORS_ONLN)" \ && DEB_HOST_MULTIARCH="$(dpkg-architecture -q DEB_HOST_MULTIARCH)" \ && CC_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_CFLAGS_MAINT_APPEND="-Wp,-D_FORTIFY_SOURCE=2 -fPIC" dpkg-buildflags --get CFLAGS)" \ diff --git a/pkg/docker/Dockerfile.ruby3.1 b/pkg/docker/Dockerfile.ruby3.1 index f365bc96..1eb6ce5c 100644 --- a/pkg/docker/Dockerfile.ruby3.1 +++ b/pkg/docker/Dockerfile.ruby3.1 @@ -8,7 +8,7 @@ RUN set -ex \ && mkdir -p /usr/lib/unit/modules /usr/lib/unit/debug-modules \ && hg clone https://hg.nginx.org/unit \ && cd unit \ - && hg up 1.28.0 \ + && hg up 1.29.0 \ && NCPU="$(getconf _NPROCESSORS_ONLN)" \ && DEB_HOST_MULTIARCH="$(dpkg-architecture -q DEB_HOST_MULTIARCH)" \ && CC_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_CFLAGS_MAINT_APPEND="-Wp,-D_FORTIFY_SOURCE=2 -fPIC" dpkg-buildflags --get CFLAGS)" \ -- cgit From 4409a10ff0bd6bb45fb88716bd383cd867958a8a Mon Sep 17 00:00:00 2001 From: Andrei Zeliankou Date: Thu, 15 Dec 2022 12:52:00 +0000 Subject: Unit 1.29.0 release. --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 79aeaf89..ae6868cf 100644 --- a/.hgtags +++ b/.hgtags @@ -36,3 +36,4 @@ f804aaf7eee10a7d8116820840d6312dd4914a41 1.21.0 1a08f884b24effa8b843d6aeeaf016b6354d1256 1.26.1 8a9055cbe4ffd450fac4d7a849c00e0db5485ad3 1.27.0 ea073fb3cb75abfb4be5dc12402de73e0c20da60 1.28.0 +37cac7fec92e5656d8a03a8594ade131c3391f45 1.29.0 -- cgit