From 6ae7142840a0f5dd28fb0623dbd88d82d6b29f7a Mon Sep 17 00:00:00 2001 From: Zhidao HONG Date: Thu, 2 Nov 2023 17:07:34 +0800 Subject: Removed trailing 0 from debug message in nxt_credential_get(). --- src/nxt_credential.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nxt_credential.c b/src/nxt_credential.c index bda97024..1c9fa9a7 100644 --- a/src/nxt_credential.c +++ b/src/nxt_credential.c @@ -74,8 +74,11 @@ nxt_credential_get(nxt_task_t *task, nxt_mp_t *mp, nxt_credential_t *uc, end = msg + NXT_MAX_ERROR_STR; for (i = 0; i < uc->ngroups; i++) { - p = nxt_sprintf(p, end, "%d%c", uc->gids[i], - i+1 < uc->ngroups ? ',' : '\0'); + p = nxt_sprintf(p, end, "%d,", uc->gids[i]); + } + + if (uc->ngroups > 0) { + p--; } nxt_debug(task, "user \"%s\" has gids:%*s", uc->user, p - msg, msg); -- cgit From a88e857b5b4100bb62be8a73c1badd999561b328 Mon Sep 17 00:00:00 2001 From: Andrei Zeliankou Date: Wed, 8 Nov 2023 17:34:59 +0000 Subject: Var: $request_id variable. This variable contains a string that is formed using random data and can be used as a unique request identifier. This closes #714 issue on GitHub. --- src/nxt_http_variables.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'src') diff --git a/src/nxt_http_variables.c b/src/nxt_http_variables.c index 46594a6b..ec744317 100644 --- a/src/nxt_http_variables.c +++ b/src/nxt_http_variables.c @@ -28,6 +28,8 @@ static u_char *nxt_http_log_date(u_char *buf, nxt_realtime_t *now, struct tm *tm, size_t size, const char *format); static nxt_int_t nxt_http_var_request_line(nxt_task_t *task, nxt_str_t *str, void *ctx, void *data); +static nxt_int_t nxt_http_var_request_id(nxt_task_t *task, nxt_str_t *str, + void *ctx, void *data); static nxt_int_t nxt_http_var_status(nxt_task_t *task, nxt_str_t *str, void *ctx, void *data); static nxt_int_t nxt_http_var_body_bytes_sent(nxt_task_t *task, nxt_str_t *str, @@ -89,6 +91,10 @@ static nxt_var_decl_t nxt_http_vars[] = { .name = nxt_string("request_line"), .handler = nxt_http_var_request_line, .cacheable = 1, + }, { + .name = nxt_string("request_id"), + .handler = nxt_http_var_request_id, + .cacheable = 1, }, { .name = nxt_string("status"), .handler = nxt_http_var_status, @@ -395,6 +401,32 @@ nxt_http_var_request_line(nxt_task_t *task, nxt_str_t *str, void *ctx, } +static nxt_int_t +nxt_http_var_request_id(nxt_task_t *task, nxt_str_t *str, void *ctx, + void *data) +{ + nxt_random_t *rand; + nxt_http_request_t *r; + + r = ctx; + + str->start = nxt_mp_nget(r->mem_pool, 32); + if (nxt_slow_path(str->start == NULL)) { + return NXT_ERROR; + } + + str->length = 32; + + rand = &task->thread->random; + + (void) nxt_sprintf(str->start, str->start + 32, "%08xD%08xD%08xD%08xD", + nxt_random(rand), nxt_random(rand), + nxt_random(rand), nxt_random(rand)); + + return NXT_OK; +} + + static nxt_int_t nxt_http_var_body_bytes_sent(nxt_task_t *task, nxt_str_t *str, void *ctx, void *data) -- cgit From 78c133d0ca3c343135e123a087970837afdebed1 Mon Sep 17 00:00:00 2001 From: Andrei Zeliankou Date: Wed, 8 Nov 2023 17:38:07 +0000 Subject: Var: simplified length calculation for $status variable. --- src/nxt_http_variables.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nxt_http_variables.c b/src/nxt_http_variables.c index ec744317..a4205eec 100644 --- a/src/nxt_http_variables.c +++ b/src/nxt_http_variables.c @@ -455,7 +455,6 @@ nxt_http_var_body_bytes_sent(nxt_task_t *task, nxt_str_t *str, void *ctx, static nxt_int_t nxt_http_var_status(nxt_task_t *task, nxt_str_t *str, void *ctx, void *data) { - u_char *p; nxt_http_request_t *r; r = ctx; @@ -465,9 +464,9 @@ nxt_http_var_status(nxt_task_t *task, nxt_str_t *str, void *ctx, void *data) return NXT_ERROR; } - p = nxt_sprintf(str->start, str->start + 3, "%03d", r->status); + (void) nxt_sprintf(str->start, str->start + 3, "%03d", r->status); - str->length = p - str->start; + str->length = 3; return NXT_OK; } -- cgit From dd0c53a77dbd3c8b7e3496c5e15cef757346ef8b Mon Sep 17 00:00:00 2001 From: Andrew Clayton Date: Fri, 26 May 2023 20:51:35 +0100 Subject: Python: Do nxt_unit_sptr_get() earlier in nxt_python_field_value(). This is a preparatory patch for fixing an issue with the encoding of http header field values. This patch simply moves the nxt_unit_sptr_get() to the top of the function where we will need it in the next commit. Signed-off-by: Andrew Clayton --- src/python/nxt_python_wsgi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/python/nxt_python_wsgi.c b/src/python/nxt_python_wsgi.c index dfb31509..ce62534b 100644 --- a/src/python/nxt_python_wsgi.c +++ b/src/python/nxt_python_wsgi.c @@ -863,6 +863,8 @@ nxt_python_field_value(nxt_unit_field_t *f, int n, uint32_t vl) char *p, *src; PyObject *res; + src = nxt_unit_sptr_get(&f->value); + #if PY_MAJOR_VERSION == 3 res = PyUnicode_New(vl, 255); #else @@ -875,7 +877,6 @@ nxt_python_field_value(nxt_unit_field_t *f, int n, uint32_t vl) p = PyString_AS_STRING(res); - src = nxt_unit_sptr_get(&f->value); p = nxt_cpymem(p, src, f->value_length); for (i = 1; i < n; i++) { -- cgit From 5cfad9cc0bb3809f802cf83d2739739fdfaab7a8 Mon Sep 17 00:00:00 2001 From: Andrew Clayton Date: Fri, 26 May 2023 20:54:43 +0100 Subject: Python: Fix header field values character encoding. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On GitHub, @RomainMou reported an issue whereby HTTP header field values where being incorrectly reported as non-ascii by the Python .isacii() method. For example, using the following test application def application(environ, start_response): t = environ['HTTP_ASCIITEST'] t = "'" + t + "'" + " (" + str(len(t)) + ")" if t.isascii(): t = t + " [ascii]" else: t = t + " [non-ascii]" resp = t + "\n\n" start_response("200 OK", [("Content-Type", "text/plain")]) return (bytes(resp, 'latin1')) You would see the following $ curl -H "ASCIITEST: $" http://localhost:8080/ '$' (1) [non-ascii] '$' has an ASCII code of 0x24 (36). The initial idea was to adjust the second parameter to the PyUnicode_New() call from 255 to 127. This unfortunately had the opposite effect. $ curl -H "ASCIITEST: $" http://localhost:8080/ '$' (1) [ascii] Good. However... $ curl -H "ASCIITEST: £" http://localhost:8080/ '£' (2) [ascii] Not good. Let's take a closer look at this. '£' is not in basic ASCII, but is in extended ASCII with a value of 0xA3 (163). Its UTF-8 encoding is 0xC2 0xA3, hence the length of 2 bytes above. $ strace -s 256 -e sendto,recvfrom curl -H "ASCIITEST: £" http://localhost:8080/ sendto(5, "GET / HTTP/1.1\r\nHost: localhost:8080\r\nUser-Agent: curl/8.0.1\r\nAccept: */*\r\nASCIITEST: \302\243\r\n\r\n", 92, MSG_NOSIGNAL, NULL, 0) = 92 recvfrom(5, "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nServer: Unit/1.30.0\r\nDate: Mon, 22 May 2023 12:44:11 GMT\r\nTransfer-Encoding: chunked\r\n\r\n12\r\n'\302\243' (2) [ascii]\n\n\r\n0\r\n\r\n", 102400, 0, NULL, NULL) = 160 '£' (2) [ascii] So we can see curl sent it UTF-8 encoded '\302\243\' which is C octal escaped UTF-8 for 0xC2 0xA3, and we got the same back. But it should not be marked as ASCII. When doing PyUnicode_New(size, 127) it sets the buffer as ASCII. So we need to use another function and that function would appear to be PyUnicode_DecodeCharmap() Which creates an Unicode object with the correct ascii/non-ascii properties based on the character encoding. With this function we now get $ curl -H "ASCIITEST: $" http://localhost:8080/ '$' (1) [ascii] $ curl -H "ASCIITEST: £" http://localhost:8080/ '£' (2) [non-ascii] and for good measure $ curl -H "ASCIITEST: $ £" http://localhost:8080/ '$ £' (4) [non-ascii] $ curl -H "ASCIITEST: $" -H "ASCIITEST: £" http://localhost:8080/ '$, £' (5) [non-ascii] PyUnicode_DecodeCharmap() does require having the full string upfront so we need to build up the potentially comma separated header field values string before invoking this function. I did not want to touch the Python 2.7 code (which may or may not even be affected by this) so kept these changes completely isolated from that, hence a slight duplication with the for () loop. Python 2.7 was sunset on January 1st 2020[0], so this code will hopefully just disappear soon anyway. I also purposefully didn't touch other code that may well have similar issues (such as the HTTP header field names) if we ever get issue reports about them, we'll deal with them then. [0]: Link: Closes: Reported-by: RomainMou Tested-by: RomainMou Signed-off-by: Andrew Clayton --- src/python/nxt_python_wsgi.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/python/nxt_python_wsgi.c b/src/python/nxt_python_wsgi.c index ce62534b..c621097e 100644 --- a/src/python/nxt_python_wsgi.c +++ b/src/python/nxt_python_wsgi.c @@ -866,10 +866,35 @@ nxt_python_field_value(nxt_unit_field_t *f, int n, uint32_t vl) src = nxt_unit_sptr_get(&f->value); #if PY_MAJOR_VERSION == 3 - res = PyUnicode_New(vl, 255); + if (nxt_slow_path(n > 1)) { + char *ptr; + + p = nxt_unit_malloc(NULL, vl + 1); + if (nxt_slow_path(p == NULL)) { + return NULL; + } + + ptr = p; + p = nxt_cpymem(p, src, f->value_length); + + for (i = 1; i < n; i++) { + p = nxt_cpymem(p, ", ", 2); + + src = nxt_unit_sptr_get(&f[i].value); + p = nxt_cpymem(p, src, f[i].value_length); + } + *p = '\0'; + + src = ptr; + } + + res = PyUnicode_DecodeCharmap(src, vl, NULL, NULL); + + if (nxt_slow_path(n > 1)) { + nxt_unit_free(NULL, src); + } #else res = PyString_FromStringAndSize(NULL, vl); -#endif if (nxt_slow_path(res == NULL)) { return NULL; @@ -885,6 +910,7 @@ nxt_python_field_value(nxt_unit_field_t *f, int n, uint32_t vl) src = nxt_unit_sptr_get(&f[i].value); p = nxt_cpymem(p, src, f[i].value_length); } +#endif return res; } -- cgit From dfdf948f899bda9120750d28c1be32f255ed941f Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Thu, 31 Dec 2020 20:49:19 +0000 Subject: Define nxt_cpu_pause for ARM64. The isb instruction fits for spin loops where it allows to save cpu power. Reviewed-by: Andrew Clayton Signed-off-by: Andrew Clayton --- src/nxt_atomic.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src') diff --git a/src/nxt_atomic.h b/src/nxt_atomic.h index cd2e7253..dae999a9 100644 --- a/src/nxt_atomic.h +++ b/src/nxt_atomic.h @@ -58,6 +58,10 @@ typedef volatile nxt_atomic_uint_t nxt_atomic_t; #define nxt_cpu_pause() \ __asm__ ("pause") +#elif (__aarch64__ || __arm64__) +#define nxt_cpu_pause() \ + __asm__ ("isb") + #else #define nxt_cpu_pause() #endif -- cgit From 27c787f437ba94073d3349625c98c6d5c0a127ba Mon Sep 17 00:00:00 2001 From: Andrei Vasiliu Date: Sun, 21 May 2023 16:37:51 +0300 Subject: Fix comments for src/nxt_unit.h. This fixes some typos and grammatical errors in the comments of src/nxt_unit.h Link: [ Adjust summary and write commit message as this just contains the fixes from the PR and not actual changes - Andrew ] Signed-off-by: Andrew Clayton --- src/nxt_unit.h | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/nxt_unit.h b/src/nxt_unit.h index 35f9fa55..a50046a5 100644 --- a/src/nxt_unit.h +++ b/src/nxt_unit.h @@ -40,7 +40,7 @@ enum { /* * Mostly opaque structure with library state. * - * Only user defined 'data' pointer exposed here. The rest is unit + * Only the user defined 'data' pointer is exposed here. The rest is unit * implementation specific and hidden. */ struct nxt_unit_s { @@ -51,8 +51,8 @@ struct nxt_unit_s { * Thread context. * * First (main) context is provided 'for free'. To receive and process - * requests in other thread, one need to allocate context and use it - * further in this thread. + * requests in other threads, one needs to allocate a new context and use it + * further in that thread. */ struct nxt_unit_ctx_s { void *data; /* User context-specific data. */ @@ -72,7 +72,7 @@ struct nxt_unit_port_id_s { }; /* - * unit provides port storage which is able to store and find the following + * Unit provides port storage which is able to store and find the following * data structures. */ struct nxt_unit_port_s { @@ -114,13 +114,13 @@ struct nxt_unit_request_info_s { /* - * Set of application-specific callbacks. Application may leave all optional - * callbacks as NULL. + * Set of application-specific callbacks. The application may leave all + * optional callbacks as NULL. */ struct nxt_unit_callbacks_s { /* - * Process request. Unlike all other callback, this callback - * need to be defined by application. + * Process request. Unlike all other callbacks, this callback is required + * and needs to be defined by the application. */ void (*request_handler)(nxt_unit_request_info_t *req); @@ -201,15 +201,15 @@ struct nxt_unit_read_info_s { nxt_unit_ctx_t *nxt_unit_init(nxt_unit_init_t *); /* - * Main function useful in case when application does not have it's own - * event loop. nxt_unit_run() starts infinite message wait and process loop. + * Main function, useful in case the application does not have its own event + * loop. nxt_unit_run() starts an infinite message wait and process loop. * * for (;;) { * app_lib->port_recv(...); * nxt_unit_process_msg(...); * } * - * The normally function returns when QUIT message received from Unit. + * The function returns normally when a QUIT message is received from Unit. */ int nxt_unit_run(nxt_unit_ctx_t *); @@ -220,10 +220,10 @@ int nxt_unit_run_shared(nxt_unit_ctx_t *ctx); nxt_unit_request_info_t *nxt_unit_dequeue_request(nxt_unit_ctx_t *ctx); /* - * Receive and process one message, invoke configured callbacks. + * Receive and process one message, and invoke configured callbacks. * - * If application implements it's own event loop, each datagram received - * from port socket should be initially processed by unit. This function + * If the application implements its own event loop, each datagram received + * from the port socket should be initially processed by unit. This function * may invoke other application-defined callback for message processing. */ int nxt_unit_run_once(nxt_unit_ctx_t *ctx); @@ -234,8 +234,8 @@ int nxt_unit_process_port_msg(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port); void nxt_unit_done(nxt_unit_ctx_t *); /* - * Allocate and initialize new execution context with new listen port to - * process requests in other thread. + * Allocate and initialize a new execution context with a new listen port to + * process requests in another thread. */ nxt_unit_ctx_t *nxt_unit_ctx_alloc(nxt_unit_ctx_t *, void *); @@ -253,7 +253,7 @@ void nxt_unit_split_host(char *host_start, uint32_t host_length, void nxt_unit_request_group_dup_fields(nxt_unit_request_info_t *req); /* - * Allocate response structure capable to store limited numer of fields. + * Allocate response structure capable of storing a limited number of fields. * The structure may be accessed directly via req->response pointer or * filled step-by-step using functions add_field and add_content. */ @@ -273,8 +273,8 @@ int nxt_unit_response_add_content(nxt_unit_request_info_t *req, const void* src, uint32_t size); /* - * Send prepared response to Unit server. Response structure destroyed during - * this call. + * Send the prepared response to the Unit server. The Response structure is + * destroyed during this call. */ int nxt_unit_response_send(nxt_unit_request_info_t *req); -- cgit From 919cae7ff95a3ce5878731a8d23f34c75489d3b4 Mon Sep 17 00:00:00 2001 From: Andrew Clayton Date: Wed, 15 Nov 2023 03:34:49 +0000 Subject: PHP: Fix a possible file-pointer leak. In nxt_php_execute() it is possible we could bail out before cleaning up the FILE * representing the PHP script to execute. At this point we only need to call fclose(3) on it. We could have possibly moved the opening of this file to later in the function, but it is probably good to bail out as early as possible if we can't open it. This was found by Coverity. Fixes: bebc03c72 ("PHP: Implement better error handling.") Signed-off-by: Andrew Clayton --- src/nxt_php_sapi.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/nxt_php_sapi.c b/src/nxt_php_sapi.c index ba000fc0..77117533 100644 --- a/src/nxt_php_sapi.c +++ b/src/nxt_php_sapi.c @@ -1225,6 +1225,8 @@ nxt_php_execute(nxt_php_run_ctx_t *ctx, nxt_unit_request_t *r) nxt_unit_req_debug(ctx->req, "php_request_startup() failed"); nxt_unit_request_done(ctx->req, NXT_UNIT_ERROR); + fclose(fp); + return; } -- cgit From 1443d623d4b5d59e4463e025b4125be9a5aa3436 Mon Sep 17 00:00:00 2001 From: Andrei Zeliankou Date: Fri, 17 Nov 2023 17:27:31 +0000 Subject: Node.js: ServerResponse.flushHeaders() implemented. This closes #1006 issue on GitHub. Reviewed-by: Andrew Clayton --- src/nodejs/unit-http/http_server.js | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src') diff --git a/src/nodejs/unit-http/http_server.js b/src/nodejs/unit-http/http_server.js index 0f00b47f..8eb13d7f 100644 --- a/src/nodejs/unit-http/http_server.js +++ b/src/nodejs/unit-http/http_server.js @@ -138,6 +138,10 @@ ServerResponse.prototype.removeHeader = function removeHeader(name) { } }; +ServerResponse.prototype.flushHeaders = function flushHeaders() { + this._sendHeaders(); +}; + ServerResponse.prototype._removeHeader = function _removeHeader(lc_name) { let entry = this.headers[lc_name]; let name_len = Buffer.byteLength(entry[0] + "", 'latin1'); -- cgit From 6b6e3bd8971a5fade4a4c5ab90a4156789127e33 Mon Sep 17 00:00:00 2001 From: "Sergey A. Osokin" Date: Mon, 20 Nov 2023 10:56:41 -0500 Subject: Fixed the MD5Encoder deprecation warning. --- src/java/nginx/unit/websocket/DigestAuthenticator.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/java/nginx/unit/websocket/DigestAuthenticator.java b/src/java/nginx/unit/websocket/DigestAuthenticator.java index 9530c303..eb91a9b4 100644 --- a/src/java/nginx/unit/websocket/DigestAuthenticator.java +++ b/src/java/nginx/unit/websocket/DigestAuthenticator.java @@ -22,7 +22,7 @@ import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.Map; -import org.apache.tomcat.util.security.MD5Encoder; +import org.apache.tomcat.util.buf.HexUtils; /** * Authenticator supporting the DIGEST auth method. @@ -140,7 +140,7 @@ public class DigestAuthenticator extends Authenticator { MessageDigest md = MessageDigest.getInstance("MD5"); byte[] thedigest = md.digest(bytesOfMessage); - return MD5Encoder.encode(thedigest); + return HexUtils.toHexString(thedigest); } @Override -- cgit From d9f5f1fb741109cc232cedd3574aa587626789c1 Mon Sep 17 00:00:00 2001 From: Andrew Clayton Date: Mon, 23 Oct 2023 14:24:01 +0100 Subject: Ruby: Handle response field arrays @xeron on GitHub reported an issue whereby with a Rails 7.1 application they were getting the following error 2023/10/22 20:57:28 [error] 56#56 [unit] #8: Ruby: Wrong header entry 'value' from application 2023/10/22 20:57:28 [error] 56#56 [unit] #8: Ruby: Failed to run ruby script After some back and forth debugging it turns out rack was trying to send back a header comprised of an array of values. E.g app = Proc.new do |env| ["200", { "Content-Type" => "text/plain", "X-Array-Header" => ["Item-1", "Item-2"], }, ["Hello World\n"]] end run app It seems this became a possibility in rack v3.0[0] So along with a header value type of T_STRING we need to also allow T_ARRAY. If we get a T_ARRAY we need to build up the header field using the given values. E.g "X-Array-Header" => ["Item-1", "", "Item-3", "Item-4"], becomes X-Array-Header: Item-1; ; Item-3; Item-4 [0]: Reported-by: Ivan Larionov Closes: Link: Tested-by: Timo Stark Signed-off-by: Andrew Clayton --- src/ruby/nxt_ruby.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 68 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/ruby/nxt_ruby.c b/src/ruby/nxt_ruby.c index bcb48f6b..3a019c36 100644 --- a/src/ruby/nxt_ruby.c +++ b/src/ruby/nxt_ruby.c @@ -889,13 +889,37 @@ nxt_ruby_hash_info(VALUE r_key, VALUE r_value, VALUE arg) goto fail; } - if (nxt_slow_path(TYPE(r_value) != T_STRING)) { + if (nxt_slow_path(TYPE(r_value) != T_STRING && TYPE(r_value) != T_ARRAY)) { nxt_unit_req_error(headers_info->req, "Ruby: Wrong header entry 'value' from application"); goto fail; } + if (TYPE(r_value) == T_ARRAY) { + int i; + int arr_len = RARRAY_LEN(r_value); + VALUE item; + size_t len = 0; + + for (i = 0; i < arr_len; i++) { + item = rb_ary_entry(r_value, i); + if (TYPE(item) != T_STRING) { + nxt_unit_req_error(headers_info->req, + "Ruby: Wrong header entry in 'value' array " + "from application"); + goto fail; + } + + len += RSTRING_LEN(item) + 2; /* +2 for '; ' */ + } + + headers_info->fields++; + headers_info->size += RSTRING_LEN(r_key) + len - 2; + + return ST_CONTINUE; + } + value = RSTRING_PTR(r_value); value_end = value + RSTRING_LEN(r_value); @@ -941,11 +965,52 @@ nxt_ruby_hash_add(VALUE r_key, VALUE r_value, VALUE arg) headers_info = (void *) (uintptr_t) arg; rc = &headers_info->rc; + key_len = RSTRING_LEN(r_key); + + if (TYPE(r_value) == T_ARRAY) { + int i; + int arr_len = RARRAY_LEN(r_value); + char *field, *p; + VALUE item; + size_t len = 0; + + for (i = 0; i < arr_len; i++) { + item = rb_ary_entry(r_value, i); + + len += RSTRING_LEN(item) + 2; /* +2 for '; ' */ + } + + field = nxt_unit_malloc(NULL, len); + if (field == NULL) { + goto fail; + } + + p = field; + + for (i = 0; i < arr_len; i++) { + item = rb_ary_entry(r_value, i); + + p = nxt_cpymem(p, RSTRING_PTR(item), RSTRING_LEN(item)); + p = nxt_cpymem(p, "; ", 2); + } + + len -= 2; + + *rc = nxt_unit_response_add_field(headers_info->req, + RSTRING_PTR(r_key), key_len, + field, len); + nxt_unit_free(NULL, field); + + if (nxt_slow_path(*rc != NXT_UNIT_OK)) { + goto fail; + } + + return ST_CONTINUE; + } + value = RSTRING_PTR(r_value); value_end = value + RSTRING_LEN(r_value); - key_len = RSTRING_LEN(r_key); - pos = value; for ( ;; ) { -- cgit From 88854cf14688286f835f7177c6bfaa17f1962f67 Mon Sep 17 00:00:00 2001 From: Andrew Clayton Date: Wed, 13 Dec 2023 02:04:38 +0000 Subject: Ruby: Prevent a possible integer underflow Coverity picked up a potential issue with the previous commit d9f5f1fb7 ("Ruby: Handle response field arrays") in that a size_t could wrap around to SIZE_MAX - 1. This would happen if we were given an empty array of header values. Fixes: d9f5f1fb7 ("Ruby: Handle response field arrays") Signed-off-by: Andrew Clayton --- src/ruby/nxt_ruby.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/ruby/nxt_ruby.c b/src/ruby/nxt_ruby.c index 3a019c36..27b868fe 100644 --- a/src/ruby/nxt_ruby.c +++ b/src/ruby/nxt_ruby.c @@ -914,8 +914,12 @@ nxt_ruby_hash_info(VALUE r_key, VALUE r_value, VALUE arg) len += RSTRING_LEN(item) + 2; /* +2 for '; ' */ } + if (arr_len > 0) { + len -= 2; + } + headers_info->fields++; - headers_info->size += RSTRING_LEN(r_key) + len - 2; + headers_info->size += RSTRING_LEN(r_key) + len; return ST_CONTINUE; } @@ -994,7 +998,9 @@ nxt_ruby_hash_add(VALUE r_key, VALUE r_value, VALUE arg) p = nxt_cpymem(p, "; ", 2); } - len -= 2; + if (arr_len > 0) { + len -= 2; + } *rc = nxt_unit_response_add_field(headers_info->req, RSTRING_PTR(r_key), key_len, -- cgit From 49aee6760a26b14c06180e4f25088cb18e2037a7 Mon Sep 17 00:00:00 2001 From: Zhidao HONG Date: Mon, 11 Dec 2023 10:46:58 +0800 Subject: HTTP: added TSTR validation flag to the rewrite option. This is to improve error messages for rewrite configuration. Take the configuration as an example: { "rewrite": "`${a + " } Previously, when applying it the user would see this error message: failed to apply previous configuration After this change, the user will see this improved error message: the previous configuration is invalid: "SyntaxError: Unexpected end of input in default:1" in the "rewrite" value. --- src/nxt_conf_validation.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index f00b28b8..32ed4ffd 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -690,6 +690,7 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_action_common_members[] = { { .name = nxt_string("rewrite"), .type = NXT_CONF_VLDT_STRING, + .flags = NXT_CONF_VLDT_TSTR, }, { .name = nxt_string("response_headers"), -- cgit From a1e00b4e28d56365b4b5cc4aa44185c4b53f5c33 Mon Sep 17 00:00:00 2001 From: Andrei Zeliankou Date: Tue, 16 Jan 2024 15:37:07 +0000 Subject: White space formatting fixes Closes: --- src/java/nginx/unit/websocket/Util.java | 2 +- src/java/nginx/unit/websocket/server/WsSessionListener.java | 2 +- src/nodejs/unit-http/websocket_connection.js | 8 ++++---- src/nodejs/unit-http/websocket_request.js | 8 ++++---- src/nxt_clone.c | 8 ++++---- src/nxt_openssl.c | 2 +- src/nxt_router.c | 2 +- src/nxt_socket.c | 8 ++++---- 8 files changed, 20 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/java/nginx/unit/websocket/Util.java b/src/java/nginx/unit/websocket/Util.java index 6acf3ade..5388431f 100644 --- a/src/java/nginx/unit/websocket/Util.java +++ b/src/java/nginx/unit/websocket/Util.java @@ -332,7 +332,7 @@ public class Util { public static List getDecoders( List> decoderClazzes) - throws DeploymentException{ + throws DeploymentException { List result = new ArrayList<>(); if (decoderClazzes != null) { diff --git a/src/java/nginx/unit/websocket/server/WsSessionListener.java b/src/java/nginx/unit/websocket/server/WsSessionListener.java index fc2bc9c5..2921bd45 100644 --- a/src/java/nginx/unit/websocket/server/WsSessionListener.java +++ b/src/java/nginx/unit/websocket/server/WsSessionListener.java @@ -19,7 +19,7 @@ package nginx.unit.websocket.server; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; -public class WsSessionListener implements HttpSessionListener{ +public class WsSessionListener implements HttpSessionListener { private final WsServerContainer wsServerContainer; diff --git a/src/nodejs/unit-http/websocket_connection.js b/src/nodejs/unit-http/websocket_connection.js index 4eccf6bf..c04075d7 100644 --- a/src/nodejs/unit-http/websocket_connection.js +++ b/src/nodejs/unit-http/websocket_connection.js @@ -36,11 +36,11 @@ var idCounter = 0; function WebSocketConnection(socket, extensions, protocol, maskOutgoingPackets, config) { this._debug = utils.BufferingLogger('websocket:connection', ++idCounter); this._debug('constructor'); - + if (this._debug.enabled) { instrumentSocketForDebugging(this, socket); } - + // Superclass Constructor EventEmitter.call(this); @@ -432,8 +432,8 @@ WebSocketConnection.prototype.processFrame = function(frame) { // logic to emit the ping frame: this is only done when a listener is known to exist // Expose a function allowing the user to override the default ping() behavior var cancelled = false; - var cancel = function() { - cancelled = true; + var cancel = function() { + cancelled = true; }; this.emit('ping', cancel, frame.binaryPayload); diff --git a/src/nodejs/unit-http/websocket_request.js b/src/nodejs/unit-http/websocket_request.js index d84e428b..450ab629 100644 --- a/src/nodejs/unit-http/websocket_request.js +++ b/src/nodejs/unit-http/websocket_request.js @@ -247,7 +247,7 @@ WebSocketRequest.prototype.parseCookies = function(str) { WebSocketRequest.prototype.accept = function(acceptedProtocol, allowedOrigin, cookies) { this._verifyResolution(); - + // TODO: Handle extensions var protocolFullCase; @@ -418,7 +418,7 @@ WebSocketRequest.prototype.accept = function(acceptedProtocol, allowedOrigin, co // if (negotiatedExtensions) { // response += 'Sec-WebSocket-Extensions: ' + negotiatedExtensions.join(', ') + '\r\n'; // } - + // Mark the request resolved now so that the user can't call accept or // reject a second time. this._resolved = true; @@ -447,12 +447,12 @@ WebSocketRequest.prototype.accept = function(acceptedProtocol, allowedOrigin, co WebSocketRequest.prototype.reject = function(status, reason, extraHeaders) { this._verifyResolution(); - + // Mark the request resolved now so that the user can't call accept or // reject a second time. this._resolved = true; this.emit('requestResolved', this); - + if (typeof(status) !== 'number') { status = 403; } diff --git a/src/nxt_clone.c b/src/nxt_clone.c index 1cd70f6c..305f4261 100644 --- a/src/nxt_clone.c +++ b/src/nxt_clone.c @@ -152,7 +152,7 @@ nxt_clone_credential_map_set(nxt_task_t *task, const char* mapfile, pid_t pid, return NXT_ERROR; } - if (i+1 < map->size) { + if (i + 1 < map->size) { *p++ = '\n'; } else { @@ -382,13 +382,13 @@ nxt_clone_vldt_credential_gidmap(nxt_task_t *task, m = map->map[j]; if (!base_ok && creds->base_gid >= (nxt_gid_t) m.container - && creds->base_gid < (nxt_gid_t) (m.container+m.size)) + && creds->base_gid < (nxt_gid_t) (m.container + m.size)) { base_ok = 1; } if (creds->gids[i] >= (nxt_gid_t) m.container - && creds->gids[i] < (nxt_gid_t) (m.container+m.size)) + && creds->gids[i] < (nxt_gid_t) (m.container + m.size)) { gid_ok = 1; break; @@ -405,7 +405,7 @@ nxt_clone_vldt_credential_gidmap(nxt_task_t *task, m = map->map[i]; if (creds->base_gid >= (nxt_gid_t) m.container - && creds->base_gid < (nxt_gid_t) (m.container+m.size)) + && creds->base_gid < (nxt_gid_t) (m.container + m.size)) { base_ok = 1; break; diff --git a/src/nxt_openssl.c b/src/nxt_openssl.c index f56135f3..8f66f45b 100644 --- a/src/nxt_openssl.c +++ b/src/nxt_openssl.c @@ -73,7 +73,7 @@ static nxt_int_t nxt_ssl_conf_commands(nxt_task_t *task, SSL_CTX *ctx, static nxt_int_t nxt_tls_ticket_keys(nxt_task_t *task, SSL_CTX *ctx, nxt_tls_init_t *tls_init, nxt_mp_t *mp); static int nxt_tls_ticket_key_callback(SSL *s, unsigned char *name, - unsigned char *iv, EVP_CIPHER_CTX *ectx,HMAC_CTX *hctx, int enc); + unsigned char *iv, EVP_CIPHER_CTX *ectx, HMAC_CTX *hctx, int enc); #endif static void nxt_ssl_session_cache(SSL_CTX *ctx, size_t cache_size, time_t timeout); diff --git a/src/nxt_router.c b/src/nxt_router.c index 4e3cb303..0b979575 100644 --- a/src/nxt_router.c +++ b/src/nxt_router.c @@ -2971,7 +2971,7 @@ nxt_router_tls_rpc_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg, mp = tmcf->router_conf->mem_pool; - if (tls->socket_conf->tls == NULL){ + if (tls->socket_conf->tls == NULL) { tlscf = nxt_mp_zget(mp, sizeof(nxt_tls_conf_t)); if (nxt_slow_path(tlscf == NULL)) { goto fail; diff --git a/src/nxt_socket.c b/src/nxt_socket.c index a8e0d514..9ac8ecd2 100644 --- a/src/nxt_socket.c +++ b/src/nxt_socket.c @@ -322,15 +322,15 @@ nxt_socket_error(nxt_socket_t s) err = 0; len = sizeof(int); - /* - * Linux and BSDs return 0 and store a pending error in the err argument; + /* + * Linux and BSDs return 0 and store a pending error in the err argument; * Solaris returns -1 and sets the errno. - */ + */ ret = getsockopt(s, SOL_SOCKET, SO_ERROR, (void *) &err, &len); if (nxt_slow_path(ret == -1)) { err = nxt_errno; - } + } return err; } -- cgit From 02d1984c912261a1274534a24a2d94ac7c7abfa7 Mon Sep 17 00:00:00 2001 From: Andrew Clayton Date: Wed, 17 Jan 2024 17:18:30 +0000 Subject: HTTP: Remove short read check in nxt_http_static_buf_completion() On GH, @tonychuuy reported an issue when using Units 'share' action they would get the following error in the unit log 2024/01/15 17:53:41 [error] 49#52 *103 file "/var/www/html/public/vendor/telescope/app.css" has changed while sending response to a client This would happen when trying to serve files over a certain size and the requested file would not be sent. This is due to a somewhat bogus check in nxt_http_static_buf_completion() I say bogus because it's not clear what the check is trying to accomplish and the error message is not entirely accurate either. The check in question goes like n = pread(file->fd, buf, size, offset); return n; ... if (n != size) { if (n >= 0) { /* log file changed error and finish */ /* >> Problem is here << */ } /* log general error and finish */ } If the number of bytes read is not what we asked for and is > -1 (i.e not an error) then it says the file has changed, but really it only checks if the file has _shrunk_ (we can't get back _more_ bytes than we asked for) since it was stat'd. This is what happens recvfrom(22, "GET /tfile HTTP/1.1\r\nHost: local"..., 2048, 0, NULL, NULL) = 82 openat(AT_FDCWD, "/mnt/9p/tfile", O_RDONLY|O_NONBLOCK) = 23 newfstatat(23, "", {st_mode=S_IFREG|0644, st_size=149922, ...}, AT_EMPTY_PATH) = 0 We get a request from a client, open the requested file and stat(2) it to get the file size. We would then go into a pread/writev loop reading the file data and sending it to the client until it's all been sent. However what was happening in this case was this (showing a dummy file of 149922 bytes) pread64(23, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 131072, 0) = 61440 write(2, "2024/01/17 15:30:50 [error] 1849"..., 109) = 109 We wanted to read 131072 bytes but only read 61440 bytes, the above check triggered and the file transfer was aborted and the above error message logged. Normally for a regular file you will only get less bytes than asked for if the read call is interrupted by a signal or you're near the end of file. There is however at least another situation where this may happen, if the file in question is being served from a network filesystem. It turns out that was indeed the case here, the files where being served over the 9P filesystem protocol. Unit was running in a docker container in an Ubuntu VM under Windows/WSL2 and the files where being passed through to the VM from Windows over 9P. Whatever the intention of this check, it is clearly causing issues in real world scenarios. If it was really desired to check if the had changed since it was opened/stat'd then it would require a different methodology and be a patch for another day. But as it stands this current check does more harm than good, so lets just remove it. With it removed we now get for the above test file recvfrom(22, "GET /tfile HTTP/1.1\r\nHost: local"..., 2048, 0, NULL, NULL) = 82 openat(AT_FDCWD, "/mnt/9p/tfile", O_RDONLY|O_NONBLOCK) = 23 newfstatat(23, "", {st_mode=S_IFREG|0644, st_size=149922, ...}, AT_EMPTY_PATH) = 0 mmap(NULL, 135168, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f367817b000 pread64(23, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 131072, 0) = 61440 pread64(23, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 18850, 61440) = 18850 writev(22, [{iov_base="HTTP/1.1 200 OK\r\nLast-Modified: "..., iov_len=171}, {iov_base="\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., iov_len=61440}, {iov_base="\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., iov_len=18850}], 3) = 80461 pread64(23, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 69632, 80290) = 61440 pread64(23, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 8192, 141730) = 8192 close(23) = 0 writev(22, [{iov_base="\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., iov_len=61440}, {iov_base="\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., iov_len=8192}], 2) = 69632 So we can see we do two pread(2)s's and a writev(2), then another two pread(2)s and another writev(2) and all the file data has been read and sent to the client. Reported-by: tonychuuy Link: Fixes: 08a8d1510 ("Basic support for serving static files.") Closes: https://github.com/nginx/unit/issues/1064 Reviewed-by: Zhidao Hong Reviewed-by: Andrei Zeliankou Signed-off-by: Andrew Clayton --- src/nxt_http_static.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'src') diff --git a/src/nxt_http_static.c b/src/nxt_http_static.c index e51ba6b0..c4caab3c 100644 --- a/src/nxt_http_static.c +++ b/src/nxt_http_static.c @@ -879,12 +879,7 @@ complete_buf: n = nxt_file_read(fb->file, b->mem.start, size, fb->file_pos); - if (n != size) { - if (n >= 0) { - nxt_log(task, NXT_LOG_ERR, "file \"%FN\" has changed " - "while sending response to a client", fb->file->name); - } - + if (nxt_slow_path(n == NXT_ERROR)) { nxt_http_request_error_handler(task, r, r->proto.any); goto clean; } -- cgit From 6452ca111c71188ab2813c763e6a0e86b48fbd56 Mon Sep 17 00:00:00 2001 From: Andrei Zeliankou Date: Thu, 25 Jan 2024 12:49:47 +0000 Subject: Node.js: fixed "httpVersion" variable format According to the Node.js documenation this variable should only include numbering scheme. Thanks to @dbit-xia. Closes: https://github.com/nginx/unit/issues/1085 --- src/nodejs/unit-http/unit.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nodejs/unit-http/unit.cpp b/src/nodejs/unit-http/unit.cpp index 7912d0ac..7d9395bb 100644 --- a/src/nodejs/unit-http/unit.cpp +++ b/src/nodejs/unit-http/unit.cpp @@ -581,6 +581,7 @@ Unit::get_server_object() void Unit::create_headers(nxt_unit_request_info_t *req, napi_value request) { + char *p; uint32_t i; napi_value headers, raw_headers; napi_status status; @@ -602,7 +603,12 @@ Unit::create_headers(nxt_unit_request_info_t *req, napi_value request) set_named_property(request, "headers", headers); set_named_property(request, "rawHeaders", raw_headers); - set_named_property(request, "httpVersion", r->version, r->version_length); + + // trim the "HTTP/" protocol prefix + p = (char *) nxt_unit_sptr_get(&r->version); + p += 5; + + set_named_property(request, "httpVersion", create_string_latin1(p, 3)); set_named_property(request, "method", r->method, r->method_length); set_named_property(request, "url", r->target, r->target_length); -- cgit From 37abe2e4633d66241bf1766a740d2b2734c132fa Mon Sep 17 00:00:00 2001 From: Zhidao HONG Date: Tue, 23 Jan 2024 18:45:27 +0800 Subject: HTTP: refactored out nxt_http_request_access_log(). This is in preparation for adding conditional access logging. No functional changes. --- src/nxt_http_request.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/nxt_http_request.c b/src/nxt_http_request.c index e532baff..2e39e4e8 100644 --- a/src/nxt_http_request.c +++ b/src/nxt_http_request.c @@ -24,6 +24,8 @@ static void nxt_http_request_proto_info(nxt_task_t *task, static void nxt_http_request_mem_buf_completion(nxt_task_t *task, void *obj, void *data); static void nxt_http_request_done(nxt_task_t *task, void *obj, void *data); +static void nxt_http_request_access_log(nxt_task_t *task, nxt_http_request_t *r, + nxt_router_conf_t *rtcf); static u_char *nxt_http_date_cache_handler(u_char *buf, nxt_realtime_t *now, struct tm *tm, size_t size, const char *format); @@ -816,26 +818,23 @@ 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_tstr_t *log_format; nxt_http_proto_t proto; + nxt_router_conf_t *rtcf; nxt_http_request_t *r; nxt_http_protocol_t protocol; nxt_socket_conf_joint_t *conf; - nxt_router_access_log_t *access_log; r = obj; proto.any = data; conf = r->conf; + rtcf = conf->socket_conf->router_conf; if (!r->logged) { r->logged = 1; - access_log = conf->socket_conf->router_conf->access_log; - log_format = conf->socket_conf->router_conf->log_format; - - if (access_log != NULL) { - access_log->handler(task, r, access_log, log_format); + if (rtcf->access_log != NULL) { + nxt_http_request_access_log(task, r, rtcf); return; } } @@ -866,6 +865,18 @@ nxt_http_request_close_handler(nxt_task_t *task, void *obj, void *data) } +static void +nxt_http_request_access_log(nxt_task_t *task, nxt_http_request_t *r, + nxt_router_conf_t *rtcf) +{ + nxt_router_access_log_t *access_log; + + access_log = rtcf->access_log; + + access_log->handler(task, r, access_log, rtcf->log_format); +} + + static u_char * nxt_http_date_cache_handler(u_char *buf, nxt_realtime_t *now, struct tm *tm, size_t size, const char *format) -- cgit From 4c91bebb50d06b28e369d68b23022caa072cf62d Mon Sep 17 00:00:00 2001 From: Zhidao HONG Date: Tue, 23 Jan 2024 18:57:30 +0800 Subject: HTTP: enhanced access log with conditional filtering. This feature allows users to specify conditions to control if access log should be recorded. The "if" option supports a string and JavaScript code. If its value is empty, 0, false, null, or undefined, the logs will not be recorded. And the '!' as a prefix inverses the condition. Example 1: Only log requests that sent a session cookie. { "access_log": { "if": "$cookie_session", "path": "..." } } Example 2: Do not log health check requests. { "access_log": { "if": "`${uri == '/health' ? false : true}`", "path": "..." } } Example 3: Only log requests when the time is before 22:00. { "access_log": { "if": "`${new Date().getHours() < 22}`", "path": "..." } } or { "access_log": { "if": "!`${new Date().getHours() >= 22}`", "path": "..." } } Closes: https://github.com/nginx/unit/issues/594 --- src/nxt_conf_validation.c | 37 +++++++++++++++++++++++++++++++ src/nxt_http_request.c | 54 ++++++++++++++++++++++++++++++++++++++++----- src/nxt_router.h | 2 ++ src/nxt_router_access_log.c | 25 +++++++++++++++++++++ 4 files changed, 112 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index 32ed4ffd..32124ee9 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -77,6 +77,8 @@ static nxt_int_t nxt_conf_vldt_error(nxt_conf_validation_t *vldt, const char *fmt, ...); static nxt_int_t nxt_conf_vldt_var(nxt_conf_validation_t *vldt, nxt_str_t *name, nxt_str_t *value); +static nxt_int_t nxt_conf_vldt_if(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value, void *data); nxt_inline nxt_int_t nxt_conf_vldt_unsupported(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data) NXT_MAYBE_UNUSED; @@ -1369,6 +1371,10 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_access_log_members[] = { }, { .name = nxt_string("format"), .type = NXT_CONF_VLDT_STRING, + }, { + .name = nxt_string("if"), + .type = NXT_CONF_VLDT_STRING, + .validator = nxt_conf_vldt_if, }, NXT_CONF_VLDT_END @@ -1538,6 +1544,37 @@ nxt_conf_vldt_var(nxt_conf_validation_t *vldt, nxt_str_t *name, } +static nxt_int_t +nxt_conf_vldt_if(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, + void *data) +{ + nxt_str_t str; + + static nxt_str_t if_str = nxt_string("if"); + + if (nxt_conf_type(value) != NXT_CONF_STRING) { + return nxt_conf_vldt_error(vldt, "The \"if\" must be a string"); + } + + nxt_conf_get_string(value, &str); + + if (str.length == 0) { + return NXT_OK; + } + + if (str.start[0] == '!') { + str.start++; + str.length--; + } + + if (nxt_is_tstr(&str)) { + return nxt_conf_vldt_var(vldt, &if_str, &str); + } + + return NXT_OK; +} + + typedef struct { nxt_mp_t *pool; nxt_str_t *type; diff --git a/src/nxt_http_request.c b/src/nxt_http_request.c index 2e39e4e8..f8d8d887 100644 --- a/src/nxt_http_request.c +++ b/src/nxt_http_request.c @@ -24,8 +24,8 @@ static void nxt_http_request_proto_info(nxt_task_t *task, static void nxt_http_request_mem_buf_completion(nxt_task_t *task, void *obj, void *data); static void nxt_http_request_done(nxt_task_t *task, void *obj, void *data); -static void nxt_http_request_access_log(nxt_task_t *task, nxt_http_request_t *r, - nxt_router_conf_t *rtcf); +static nxt_int_t nxt_http_request_access_log(nxt_task_t *task, + nxt_http_request_t *r, nxt_router_conf_t *rtcf); static u_char *nxt_http_date_cache_handler(u_char *buf, nxt_realtime_t *now, struct tm *tm, size_t size, const char *format); @@ -818,6 +818,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_int_t ret; nxt_http_proto_t proto; nxt_router_conf_t *rtcf; nxt_http_request_t *r; @@ -834,8 +835,10 @@ nxt_http_request_close_handler(nxt_task_t *task, void *obj, void *data) r->logged = 1; if (rtcf->access_log != NULL) { - nxt_http_request_access_log(task, r, rtcf); - return; + ret = nxt_http_request_access_log(task, r, rtcf); + if (ret == NXT_OK) { + return; + } } } @@ -865,15 +868,54 @@ nxt_http_request_close_handler(nxt_task_t *task, void *obj, void *data) } -static void +static nxt_int_t nxt_http_request_access_log(nxt_task_t *task, nxt_http_request_t *r, nxt_router_conf_t *rtcf) { + nxt_int_t ret; + nxt_str_t str; + nxt_bool_t expr; nxt_router_access_log_t *access_log; access_log = rtcf->access_log; - access_log->handler(task, r, access_log, rtcf->log_format); + expr = 1; + + if (rtcf->log_expr != NULL) { + + if (nxt_tstr_is_const(rtcf->log_expr)) { + nxt_tstr_str(rtcf->log_expr, &str); + + } else { + 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)) { + return NXT_DECLINED; + } + + nxt_tstr_query(task, r->tstr_query, rtcf->log_expr, &str); + + if (nxt_slow_path(nxt_tstr_query_failed(r->tstr_query))) { + return NXT_DECLINED; + } + } + + if (str.length == 0 + || nxt_str_eq(&str, "0", 1) + || nxt_str_eq(&str, "false", 5) + || nxt_str_eq(&str, "null", 4) + || nxt_str_eq(&str, "undefined", 9)) + { + expr = 0; + } + } + + if (rtcf->log_negate ^ expr) { + access_log->handler(task, r, access_log, rtcf->log_format); + return NXT_OK; + } + + return NXT_DECLINED; } diff --git a/src/nxt_router.h b/src/nxt_router.h index b14f8410..3e523001 100644 --- a/src/nxt_router.h +++ b/src/nxt_router.h @@ -54,6 +54,8 @@ typedef struct { nxt_router_access_log_t *access_log; nxt_tstr_t *log_format; + nxt_tstr_t *log_expr; + uint8_t log_negate; /* 1 bit */ } nxt_router_conf_t; diff --git a/src/nxt_router_access_log.c b/src/nxt_router_access_log.c index ccbddb96..7fc59972 100644 --- a/src/nxt_router_access_log.c +++ b/src/nxt_router_access_log.c @@ -13,6 +13,7 @@ typedef struct { nxt_str_t path; nxt_str_t format; + nxt_conf_value_t *expr; } nxt_router_access_log_conf_t; @@ -53,6 +54,12 @@ static nxt_conf_map_t nxt_router_access_log_conf[] = { NXT_CONF_MAP_STR, offsetof(nxt_router_access_log_conf_t, format), }, + + { + nxt_string("if"), + NXT_CONF_MAP_PTR, + offsetof(nxt_router_access_log_conf_t, expr), + }, }; @@ -72,6 +79,8 @@ nxt_router_access_log_create(nxt_task_t *task, nxt_router_conf_t *rtcf, "[$time_local] \"$request_line\" $status $body_bytes_sent " "\"$header_referer\" \"$header_user_agent\""); + nxt_memzero(&alcf, sizeof(nxt_router_access_log_conf_t)); + alcf.format = log_format_str; if (nxt_conf_type(value) == NXT_CONF_STRING) { @@ -133,6 +142,22 @@ nxt_router_access_log_create(nxt_task_t *task, nxt_router_conf_t *rtcf, rtcf->access_log = access_log; rtcf->log_format = format; + if (alcf.expr != NULL) { + nxt_conf_get_string(alcf.expr, &str); + + if (str.length > 0 && str.start[0] == '!') { + rtcf->log_negate = 1; + + str.start++; + str.length--; + } + + rtcf->log_expr = nxt_tstr_compile(rtcf->tstr_state, &str, 0); + if (nxt_slow_path(rtcf->log_expr == NULL)) { + return NXT_ERROR; + } + } + return NXT_OK; } -- cgit From 9919b50aecb196ff9e005ab3d13689bc011233a7 Mon Sep 17 00:00:00 2001 From: Andrew Clayton Date: Wed, 24 Jan 2024 17:21:53 +0000 Subject: Isolation: Add a new nxt_cred_t type This is a generic type to represent a uid_t/gid_t on Linux when user namespaces are in use. Technically this only needs to be an unsigned int, but we make it an int64_t so we can make use of the existing NXT_CONF_MAP_INT64 type. This will be used in subsequent commits. Reviewed-by: Zhidao Hong Signed-off-by: Andrew Clayton --- src/nxt_clone.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/nxt_clone.h b/src/nxt_clone.h index 6cea1bd7..1677dc77 100644 --- a/src/nxt_clone.h +++ b/src/nxt_clone.h @@ -9,6 +9,8 @@ #if (NXT_HAVE_CLONE_NEWUSER) +typedef int64_t nxt_cred_t; + typedef struct { nxt_int_t container; nxt_int_t host; -- cgit From f7c9d3a8b3dbe083007e73c8c7b7e50094bf3763 Mon Sep 17 00:00:00 2001 From: Andrew Clayton Date: Wed, 24 Jan 2024 18:01:49 +0000 Subject: Isolation: Use an appropriate type for storing uid/gids Andrei reported an issue on arm64 where he was seeing the following error message when running the tests 2024/01/17 18:32:31.109 [error] 54904#54904 "gidmap" field has an entry with "size": 1, but for unprivileged unit it must be 1. This error message is guarded by the following if statement if (nxt_slow_path(m.size > 1) Turns out size was indeed > 1, in this case it was 289356276058554369, m.size is defined as a nxt_int_t, which on arm64 is actually 8 bytes, but was being printed as a signed int (4 bytes) and by chance/undefined behaviour comes out as 1. But why is size so big? In this case it should have just been 1 with a config of 'gidmap': [{'container': 0, 'host': os.getegid(), 'size': 1}], This is due to nxt_int_t being 64bits on arm64 but using a conf type of NXT_CONF_MAP_INT which means in nxt_conf_map_object() we would do (using our m.size variable as an example) ptr = nxt_pointer_to(data, map[i].offset); ... ptr->i = num; Where ptr is a union pointer and is now pointing at our m.size Next we set m.size to the value of num (which is 1 in this case), via ptr->i where i is a member of that union of type int. So here we are setting a 64bit memory location (nxt_int_t on arm64) through a 32bit (int) union alias, this means we are only setting the lower half (4) of the bytes. Whatever happens to be in the upper 4 bytes will remain, giving us our exceptionally large value. This is demonstrated by this program #include #include int main(void) { int64_t num = -1; /* All 1's in two's complement */ union { int32_t i32; int64_t i64; } *ptr; ptr = (void *)# ptr->i32 = 1; printf("num : %lu / %ld\n", num, num); ptr->i64 = 1; printf("num : %ld\n", num); return 0; } $ make union-32-64-issue cc union-32-64-issue.c -o union-32-64-issue $ ./union-32-64-issue num : 18446744069414584321 / -4294967295 num : 1 However that is not the only issue, because the members of nxt_clone_map_entry_t were specified as nxt_int_t's on the likes of x86_64 this would be a 32bit signed integer. However uid/gids on Linux at least are defined as unsigned integers, so a nxt_int_t would not be big enough to hold all potential values. We could make the nxt_uint_t's but then we're back to the above union aliasing problem. We could just set the memory for these variables to 0 and that would work, however that's really just papering over the problem. The right thing is to use a large enough sized type to store these things, hence the previously introduced nxt_cred_t. This is an int64_t which is plenty large enough. So we switch the nxt_clone_map_entry_t structure members over to nxt_cred_t's and use NXT_CONF_MAP_INT64 as the conf type, which then uses the right sized union member in nxt_conf_map_object() to set these variables. Reported-by: Andrei Zeliankou Reviewed-by: Zhidao Hong Signed-off-by: Andrew Clayton --- src/nxt_clone.c | 6 +++--- src/nxt_clone.h | 6 +++--- src/nxt_isolation.c | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/nxt_clone.c b/src/nxt_clone.c index 305f4261..e78a7822 100644 --- a/src/nxt_clone.c +++ b/src/nxt_clone.c @@ -143,7 +143,7 @@ nxt_clone_credential_map_set(nxt_task_t *task, const char* mapfile, pid_t pid, end = mapinfo + len; for (i = 0; i < map->size; i++) { - p = nxt_sprintf(p, end, "%d %d %d", map->map[i].container, + p = nxt_sprintf(p, end, "%L %L %L", map->map[i].container, map->map[i].host, map->map[i].size); if (nxt_slow_path(p == end)) { @@ -332,7 +332,7 @@ nxt_clone_vldt_credential_gidmap(nxt_task_t *task, if (nxt_slow_path((nxt_gid_t) m.host != nxt_egid)) { nxt_log(task, NXT_LOG_ERR, "\"gidmap\" field has an entry for " - "host gid %d but unprivileged unit can only map itself " + "host gid %L but unprivileged unit can only map itself " "(gid %d) into child namespaces.", m.host, nxt_egid); return NXT_ERROR; @@ -340,7 +340,7 @@ nxt_clone_vldt_credential_gidmap(nxt_task_t *task, if (nxt_slow_path(m.size > 1)) { nxt_log(task, NXT_LOG_ERR, "\"gidmap\" field has an entry with " - "\"size\": %d, but for unprivileged unit it must be 1.", + "\"size\": %L, but for unprivileged unit it must be 1.", m.size); return NXT_ERROR; diff --git a/src/nxt_clone.h b/src/nxt_clone.h index 1677dc77..bf28322f 100644 --- a/src/nxt_clone.h +++ b/src/nxt_clone.h @@ -12,9 +12,9 @@ typedef int64_t nxt_cred_t; typedef struct { - nxt_int_t container; - nxt_int_t host; - nxt_int_t size; + nxt_cred_t container; + nxt_cred_t host; + nxt_cred_t size; } nxt_clone_map_entry_t; typedef struct { diff --git a/src/nxt_isolation.c b/src/nxt_isolation.c index cfa494a8..ed5e0d76 100644 --- a/src/nxt_isolation.c +++ b/src/nxt_isolation.c @@ -326,19 +326,19 @@ nxt_isolation_credential_map(nxt_task_t *task, nxt_mp_t *mp, static nxt_conf_map_t nxt_clone_map_entry_conf[] = { { nxt_string("container"), - NXT_CONF_MAP_INT, + NXT_CONF_MAP_INT64, offsetof(nxt_clone_map_entry_t, container), }, { nxt_string("host"), - NXT_CONF_MAP_INT, + NXT_CONF_MAP_INT64, offsetof(nxt_clone_map_entry_t, host), }, { nxt_string("size"), - NXT_CONF_MAP_INT, + NXT_CONF_MAP_INT64, offsetof(nxt_clone_map_entry_t, size), }, }; -- cgit From eba7378d4f8816799032a0c086ab54d3c15157b3 Mon Sep 17 00:00:00 2001 From: Andrew Clayton Date: Wed, 24 Jan 2024 22:03:12 +0000 Subject: Configuration: Use the NXT_CONF_VLDT_REQUIRED flag for procmap Use the NXT_CONF_VLDT_REQUIRED flag on the app_procmap members. These three settings are required. These are for the uidmap & gidmap settings in the config. Suggested-by: Zhidao HONG Reviewed-by: Zhidao Hong Signed-off-by: Andrew Clayton --- src/nxt_conf_validation.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index 32124ee9..eb7ef530 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -1327,12 +1327,15 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_app_procmap_members[] = { { .name = nxt_string("container"), .type = NXT_CONF_VLDT_INTEGER, + .flags = NXT_CONF_VLDT_REQUIRED, }, { .name = nxt_string("host"), .type = NXT_CONF_VLDT_INTEGER, + .flags = NXT_CONF_VLDT_REQUIRED, }, { .name = nxt_string("size"), .type = NXT_CONF_VLDT_INTEGER, + .flags = NXT_CONF_VLDT_REQUIRED, }, NXT_CONF_VLDT_END -- cgit From 990fbe7010526bb97f2d414db866050ef5e8f244 Mon Sep 17 00:00:00 2001 From: Andrew Clayton Date: Wed, 24 Jan 2024 22:17:02 +0000 Subject: Configuration: Remove procmap validation code With the previous commit which introduced the use of the NXT_CONF_VLDT_REQUIRED flag, we no longer need to do this separate validation, it's only purpose was to check if the three uidmap/gidmap settings had been provided. Reviewed-by: Zhidao Hong Signed-off-by: Andrew Clayton --- src/nxt_conf_validation.c | 73 ++--------------------------------------------- 1 file changed, 2 insertions(+), 71 deletions(-) (limited to 'src') diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index eb7ef530..c843b265 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -218,8 +218,6 @@ static nxt_int_t nxt_conf_vldt_clone_namespaces(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data); #if (NXT_HAVE_CLONE_NEWUSER) -static nxt_int_t nxt_conf_vldt_clone_procmap(nxt_conf_validation_t *vldt, - const char* mapfile, nxt_conf_value_t *value); static nxt_int_t nxt_conf_vldt_clone_uidmap(nxt_conf_validation_t *vldt, nxt_conf_value_t *value); static nxt_int_t nxt_conf_vldt_clone_gidmap(nxt_conf_validation_t *vldt, @@ -3091,73 +3089,6 @@ nxt_conf_vldt_isolation(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, #if (NXT_HAVE_CLONE_NEWUSER) -typedef struct { - nxt_int_t container; - nxt_int_t host; - nxt_int_t size; -} nxt_conf_vldt_clone_procmap_conf_t; - - -static nxt_conf_map_t nxt_conf_vldt_clone_procmap_conf_map[] = { - { - nxt_string("container"), - NXT_CONF_MAP_INT32, - offsetof(nxt_conf_vldt_clone_procmap_conf_t, container), - }, - - { - nxt_string("host"), - NXT_CONF_MAP_INT32, - offsetof(nxt_conf_vldt_clone_procmap_conf_t, host), - }, - - { - nxt_string("size"), - NXT_CONF_MAP_INT32, - offsetof(nxt_conf_vldt_clone_procmap_conf_t, size), - }, - -}; - - -static nxt_int_t -nxt_conf_vldt_clone_procmap(nxt_conf_validation_t *vldt, const char *mapfile, - nxt_conf_value_t *value) -{ - nxt_int_t ret; - nxt_conf_vldt_clone_procmap_conf_t procmap; - - procmap.container = -1; - procmap.host = -1; - procmap.size = -1; - - ret = nxt_conf_map_object(vldt->pool, value, - nxt_conf_vldt_clone_procmap_conf_map, - nxt_nitems(nxt_conf_vldt_clone_procmap_conf_map), - &procmap); - if (ret != NXT_OK) { - return ret; - } - - if (procmap.container == -1) { - return nxt_conf_vldt_error(vldt, "The %s requires the " - "\"container\" field set.", mapfile); - } - - if (procmap.host == -1) { - return nxt_conf_vldt_error(vldt, "The %s requires the " - "\"host\" field set.", mapfile); - } - - if (procmap.size == -1) { - return nxt_conf_vldt_error(vldt, "The %s requires the " - "\"size\" field set.", mapfile); - } - - return NXT_OK; -} - - static nxt_int_t nxt_conf_vldt_clone_uidmap(nxt_conf_validation_t *vldt, nxt_conf_value_t *value) { @@ -3174,7 +3105,7 @@ nxt_conf_vldt_clone_uidmap(nxt_conf_validation_t *vldt, nxt_conf_value_t *value) return ret; } - return nxt_conf_vldt_clone_procmap(vldt, "uid_map", value); + return NXT_OK; } @@ -3194,7 +3125,7 @@ nxt_conf_vldt_clone_gidmap(nxt_conf_validation_t *vldt, nxt_conf_value_t *value) return ret; } - return nxt_conf_vldt_clone_procmap(vldt, "gid_map", value); + return NXT_OK; } #endif -- cgit From ecd573924f5dc31e279f12249fb44e8d00e144a2 Mon Sep 17 00:00:00 2001 From: Alejandro Colomar Date: Fri, 2 Feb 2024 23:29:48 +0100 Subject: Configuration: Add nxt_conf_get_string_dup() This function is like nxt_conf_get_string(), but creates a new copy, so that it can be modified without corrupting the configuration string. Reviewed-by: Andrew Clayton Cc: Zhidao Hong Signed-off-by: Alejandro Colomar --- src/nxt_conf.c | 11 +++++++++++ src/nxt_conf.h | 3 +++ 2 files changed, 14 insertions(+) (limited to 'src') diff --git a/src/nxt_conf.c b/src/nxt_conf.c index 664b5468..008cb968 100644 --- a/src/nxt_conf.c +++ b/src/nxt_conf.c @@ -3,6 +3,7 @@ * Copyright (C) Igor Sysoev * Copyright (C) Valentin V. Bartenev * Copyright (C) NGINX, Inc. + * Copyright 2024, Alejandro Colomar */ #include @@ -174,6 +175,16 @@ nxt_conf_get_string(nxt_conf_value_t *value, nxt_str_t *str) } +nxt_str_t * +nxt_conf_get_string_dup(nxt_conf_value_t *value, nxt_mp_t *mp, nxt_str_t *str) +{ + nxt_str_t s; + + nxt_conf_get_string(value, &s); + return nxt_str_dup(mp, str, &s); +} + + void nxt_conf_set_string(nxt_conf_value_t *value, nxt_str_t *str) { diff --git a/src/nxt_conf.h b/src/nxt_conf.h index 1b13f5ae..626b6d4d 100644 --- a/src/nxt_conf.h +++ b/src/nxt_conf.h @@ -3,6 +3,7 @@ * Copyright (C) Igor Sysoev * Copyright (C) Valentin V. Bartenev * Copyright (C) NGINX, Inc. + * Copyright 2024, Alejandro Colomar */ #ifndef _NXT_CONF_INCLUDED_ @@ -116,6 +117,8 @@ void nxt_conf_json_position(u_char *start, const u_char *pos, nxt_uint_t *line, nxt_int_t nxt_conf_validate(nxt_conf_validation_t *vldt); NXT_EXPORT void nxt_conf_get_string(nxt_conf_value_t *value, nxt_str_t *str); +NXT_EXPORT nxt_str_t *nxt_conf_get_string_dup(nxt_conf_value_t *value, + nxt_mp_t *mp, nxt_str_t *str); NXT_EXPORT void nxt_conf_set_string(nxt_conf_value_t *value, nxt_str_t *str); NXT_EXPORT nxt_int_t nxt_conf_set_string_dup(nxt_conf_value_t *value, nxt_mp_t *mp, const nxt_str_t *str); -- cgit From bb376c683877d2aec9ce4358536113ca5fd19d84 Mon Sep 17 00:00:00 2001 From: Alejandro Colomar Date: Fri, 2 Feb 2024 23:43:13 +0100 Subject: Simplify, by calling nxt_conf_get_string_dup() Refactor. Reviewed-by: Andrew Clayton Cc: Zhidao Hong Signed-off-by: Alejandro Colomar --- src/nxt_http_static.c | 4 +--- src/nxt_router.c | 23 +++++++++-------------- 2 files changed, 10 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/nxt_http_static.c b/src/nxt_http_static.c index c4caab3c..ee25015e 100644 --- a/src/nxt_http_static.c +++ b/src/nxt_http_static.c @@ -117,9 +117,7 @@ nxt_http_static_init(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_str_set(&conf->index, "index.html"); } else { - nxt_conf_get_string(acf->index, &str); - - ret = nxt_str_dup(mp, &conf->index, &str); + ret = nxt_conf_get_string_dup(acf->index, mp, &conf->index); if (nxt_slow_path(ret == NULL)) { return NXT_ERROR; } diff --git a/src/nxt_router.c b/src/nxt_router.c index 0b979575..947836c8 100644 --- a/src/nxt_router.c +++ b/src/nxt_router.c @@ -2275,7 +2275,7 @@ nxt_router_conf_process_static(nxt_task_t *task, nxt_router_conf_t *rtcf, { uint32_t next, i; nxt_mp_t *mp; - nxt_str_t *type, exten, str; + nxt_str_t *type, exten, str, *s; nxt_int_t ret; nxt_uint_t exts; nxt_conf_value_t *mtypes_conf, *ext_conf, *value; @@ -2311,9 +2311,8 @@ nxt_router_conf_process_static(nxt_task_t *task, nxt_router_conf_t *rtcf, } if (nxt_conf_type(ext_conf) == NXT_CONF_STRING) { - nxt_conf_get_string(ext_conf, &str); - - if (nxt_slow_path(nxt_str_dup(mp, &exten, &str) == NULL)) { + s = nxt_conf_get_string_dup(ext_conf, mp, &exten); + if (nxt_slow_path(s == NULL)) { return NXT_ERROR; } @@ -2331,9 +2330,8 @@ nxt_router_conf_process_static(nxt_task_t *task, nxt_router_conf_t *rtcf, for (i = 0; i < exts; i++) { value = nxt_conf_get_array_element(ext_conf, i); - nxt_conf_get_string(value, &str); - - if (nxt_slow_path(nxt_str_dup(mp, &exten, &str) == NULL)) { + s = nxt_conf_get_string_dup(value, mp, &exten); + if (nxt_slow_path(s == NULL)) { return NXT_ERROR; } @@ -2425,14 +2423,11 @@ static nxt_int_t nxt_router_conf_forward_header(nxt_mp_t *mp, nxt_conf_value_t *conf, nxt_http_forward_header_t *fh) { - char c; - size_t i; - uint32_t hash; - nxt_str_t header; - - nxt_conf_get_string(conf, &header); + char c; + size_t i; + uint32_t hash; - fh->header = nxt_str_dup(mp, NULL, &header); + fh->header = nxt_conf_get_string_dup(conf, mp, NULL); if (nxt_slow_path(fh->header == NULL)) { return NXT_ERROR; } -- cgit From 46cef09f296d9a3d54b98331d25920fc6322bea8 Mon Sep 17 00:00:00 2001 From: Alejandro Colomar Date: Wed, 31 Jan 2024 15:34:57 +0100 Subject: Configuration: Don't corrupt abstract socket names The commit that added support for Unix sockets accepts abstract sockets using '@' in the config, but we stored it internally using '\0'. We want to support abstract sockets transparently to the user, so that if the user configures unitd with '@', if we receive a query about the current configuration, the user should see the same exact thing that was configured. So, this commit avoids the transformation in the internal state file, storing user input pristine, and we only transform the '@' in temporary strings. This commit fixes another bug, where we try to connect to abstract sockets with a trailing '\0' in their name due to calling twice nxt_sockaddr_parse() on the same string. By calling that function only once with each copy of the string, we have fixed that bug. The following code was responsible for this bug, which the second time it was called, considered these sockets as file-backed (not abstract) Unix socket, and so appended a '\0' to the socket name. $ grepc -tfd nxt_sockaddr_unix_parse . | grep -A10 @ if (path[0] == '@') { path[0] = '\0'; socklen--; #if !(NXT_LINUX) nxt_thread_log_error(NXT_LOG_ERR, "abstract unix domain sockets are not supported"); return NULL; #endif } sa = nxt_sockaddr_alloc(mp, socklen, addr->length); This bug was found thanks to some experiment about using 'const' for some strings. And here's some history: - 9041d276fc6a ("nxt_sockaddr_parse() introducted.") This commit introduced support for abstract Unix sockets, but they only worked as "servers", and not as "listeners". We corrupted the JSON config file, and stored a \u0000. This also caused calling connect(2) with a bogus trailing null byte, which tried to connect to a different abstract socket. - d8e0768a5bae ("Fixed support for abstract Unix sockets.") This commit (partially) fixed support for abstract Unix sockets, so they they worked also as listeners. We still corrupted the JSON config file, and stored a \u0000. This caused calling connect(2) (and now bind(2) too) with a bogus trailing null byte. - e2aec6686a4d ("Storing abstract sockets with @ internally.") This commit fixed the problem by which we were corrupting the config file, but only for "listeners", not for "servers". (It also fixes the issue about the terminating '\0'.) We completely forgot about "servers", and other callers of the same function. To reproduce the problem, I used the following config: ```json { "listeners": { "*:80": { "pass": "routes/u" }, "unix:@abstract": { "pass": "routes/a" } }, "routes": { "u": [{ "action": { "pass": "upstreams/u" } }], "a": [{ "action": { "return": 302, "location": "/i/am/not/at/home/" } }] }, "upstreams": { "u": { "servers": { "unix:@abstract": {} } } } } ``` And then check the state file: $ sudo cat /opt/local/nginx/unit/master/var/lib/unit/conf.json \ | jq . \ | grep unix; "unix:@abstract": { "unix:\u0000abstract": {} After this patch, the state file has a '@' as expected: $ sudo cat /opt/local/nginx/unit/unix/var/lib/unit/conf.json \ | jq . \ | grep unix; "unix:@abstract": { "unix:@abstract": {} Regarding the trailing null byte, here are some tests: $ sudo strace -f -e 'bind,connect' /opt/local/nginx/unit/d8e0/sbin/unitd \ |& grep abstract; [pid 22406] bind(10, {sa_family=AF_UNIX, sun_path=@"abstract\0"}, 12) = 0 [pid 22410] connect(134, {sa_family=AF_UNIX, sun_path=@"abstract\0"}, 12) = 0 ^C $ sudo killall unitd $ sudo strace -f -e 'bind,connect' /opt/local/nginx/unit/master/sbin/unitd \ |& grep abstract; [pid 22449] bind(10, {sa_family=AF_UNIX, sun_path=@"abstract"}, 11) = 0 [pid 22453] connect(134, {sa_family=AF_UNIX, sun_path=@"abstract\0"}, 12) = -1 ECONNREFUSED (Connection refused) ^C $ sudo killall unitd $ sudo strace -f -e 'bind,connect' /opt/local/nginx/unit/unix/sbin/unitd \ |& grep abstract; [pid 22488] bind(10, {sa_family=AF_UNIX, sun_path=@"abstract"}, 11) = 0 [pid 22492] connect(134, {sa_family=AF_UNIX, sun_path=@"abstract"}, 11) = 0 ^C Fixes: 9041d276fc6a ("nxt_sockaddr_parse() introducted.") Fixes: d8e0768a5bae ("Fixed support for abstract Unix sockets.") Fixes: e2aec6686a4d ("Storing abstract sockets with @ internally.") Link: Reviewed-by: Andrew Clayton Cc: Liam Crilly Cc: Zhidao Hong Signed-off-by: Alejandro Colomar --- src/nxt_conf_validation.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index c843b265..bf18cd1a 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -2,6 +2,7 @@ /* * Copyright (C) Valentin V. Bartenev * Copyright (C) NGINX, Inc. + * Copyright 2024, Alejandro Colomar */ #include @@ -1936,10 +1937,13 @@ static nxt_int_t nxt_conf_vldt_proxy(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data) { - nxt_str_t name; + nxt_str_t name, *ret; nxt_sockaddr_t *sa; - nxt_conf_get_string(value, &name); + ret = nxt_conf_get_string_dup(value, vldt->pool, &name); + if (nxt_slow_path(ret == NULL)) { + return NXT_ERROR; + } if (nxt_str_start(&name, "http://", 7)) { name.length -= 7; @@ -2913,13 +2917,11 @@ nxt_conf_vldt_object_iterator(nxt_conf_validation_t *vldt, for ( ;; ) { member = nxt_conf_next_object_member(value, &name, &index); - if (member == NULL) { return NXT_OK; } ret = validator(vldt, &name, member); - if (ret != NXT_OK) { return ret; } @@ -3268,16 +3270,19 @@ nxt_conf_vldt_server(nxt_conf_validation_t *vldt, nxt_str_t *name, nxt_conf_value_t *value) { nxt_int_t ret; + nxt_str_t str; nxt_sockaddr_t *sa; ret = nxt_conf_vldt_type(vldt, name, value, NXT_CONF_VLDT_OBJECT); - if (ret != NXT_OK) { return ret; } - sa = nxt_sockaddr_parse(vldt->pool, name); + if (nxt_slow_path(nxt_str_dup(vldt->pool, &str, name) == NULL)) { + return NXT_ERROR; + } + sa = nxt_sockaddr_parse(vldt->pool, &str); if (sa == NULL) { return nxt_conf_vldt_error(vldt, "The \"%V\" is not valid " "server address.", name); -- cgit From 9e986704480de0d6b74dafa5ebcf775eaa88333a Mon Sep 17 00:00:00 2001 From: Alejandro Colomar Date: Thu, 8 Feb 2024 11:15:34 +0100 Subject: Configuration: Fix validation of "processes" It's an integer, not a floating number. Fixes: 68c6b67ffc84 ("Configuration: support for rational numbers.") Closes: https://github.com/nginx/unit/issues/1115 Link: Reviewed-by: Zhidao Hong Reviewed-by: Andrew Clayton Cc: Dan Callahan Cc: Valentin Bartenev Signed-off-by: Alejandro Colomar --- src/nxt_conf_validation.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index bf18cd1a..caa068d2 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -2830,7 +2830,7 @@ nxt_conf_vldt_processes(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, nxt_int_t ret; nxt_conf_vldt_processes_conf_t proc; - if (nxt_conf_type(value) == NXT_CONF_NUMBER) { + if (nxt_conf_type(value) == NXT_CONF_INTEGER) { int_value = nxt_conf_get_number(value); if (int_value < 1) { -- cgit From fbeb2065b180e2376088387ee150d3975dc08cd5 Mon Sep 17 00:00:00 2001 From: Gabor Javorszky Date: Wed, 14 Feb 2024 18:16:01 +0000 Subject: fix: Take options as well as requestListener (#1091) * Take options as well as requestListener Unit-http have not kept up with the signature of nodejs's http package development. Nodejs allows an optional `options` object to be passed to the `createServer` function, we didn't. This resulted in function signature errors when user code that did make use of the options arg tried to call unit's replaced function. This change changes the signature to be more in line with how nodejs does it discarding it and printing a message to stdout. * Add test file to start node application with options * Add changes to docs/changes.xml Closes: https://github.com/nginx/unit/issues/1043 --- src/nodejs/unit-http/http.js | 4 ++-- src/nodejs/unit-http/http_server.js | 10 +++++++++- 2 files changed, 11 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nodejs/unit-http/http.js b/src/nodejs/unit-http/http.js index d298a35f..60b8004f 100644 --- a/src/nodejs/unit-http/http.js +++ b/src/nodejs/unit-http/http.js @@ -11,8 +11,8 @@ const { ServerResponse, } = require('./http_server'); -function createServer (requestHandler) { - return new Server(requestHandler); +function createServer (options, requestHandler) { + return new Server(options, requestHandler); } const http = require("http") diff --git a/src/nodejs/unit-http/http_server.js b/src/nodejs/unit-http/http_server.js index 8eb13d7f..b78f309a 100644 --- a/src/nodejs/unit-http/http_server.js +++ b/src/nodejs/unit-http/http_server.js @@ -5,6 +5,7 @@ 'use strict'; +const { stderr } = require('process'); const EventEmitter = require('events'); const http = require('http'); const util = require('util'); @@ -413,7 +414,14 @@ ServerRequest.prototype._read = function _read(n) { }; -function Server(requestListener) { +function Server(options, requestListener) { + if (typeof options === 'function') { + requestListener = options; + options = {}; + } else { + stderr.write("http.Server constructor was called with unsupported options, using default settings\n"); + } + EventEmitter.call(this); this.unit = new unit_lib.Unit(); -- cgit From 34b3a812b110e8fba0669fc843aebffddf89ab17 Mon Sep 17 00:00:00 2001 From: Andrew Clayton Date: Mon, 6 Nov 2023 18:43:19 +0000 Subject: Add nxt_file_chown() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This wraps chown(2) but takes the user/owner and group as strings. It's a little long winded as it uses the thread safe versions of getpwnam()/getgrname() which require a little more work. This function will be used by the following commit that allows to set the permissions of the Unix domain control socket. We need to cast uid & gid to long in the call to nxt_thread_log_alert() to appease clang-ast as it's adamant that uid/gid are unsigned ints, but chown(2) takes -1 for these values to indicate don't change this item, and it'd be nice to show them in the error message. Note that getpwnam()/getgrname() don't define "not found" as an error as per their man page The formulation given above under "RETURN VALUE" is from POSIX.1-2001. It does not call "not found" an error, and hence does not specify what value errno might have in this situation. But that makes it impossible to recognize errors. One might argue that according to POSIX errno should be left unchanged if an entry is not found. Experiments on var‐ ious UNIX-like systems show that lots of different values occur in this situation: 0, ENOENT, EBADF, ESRCH, EWOULDBLOCK, EPERM, and probably others. Thus if we log an error from these functions we can end up with the slightly humorous error message 2024/02/12 15:15:12 [alert] 99404#99404 getpwnam_r("noddy", ...) failed (0: Success) (User not found) while creating listening socket on unix:/opt/unit/control.unit.sock Reviewed-by: Zhidao Hong Signed-off-by: Andrew Clayton --- src/nxt_file.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/nxt_file.h | 2 ++ 2 files changed, 84 insertions(+) (limited to 'src') diff --git a/src/nxt_file.c b/src/nxt_file.c index 6f1a93e4..4047d9d7 100644 --- a/src/nxt_file.c +++ b/src/nxt_file.c @@ -325,6 +325,88 @@ nxt_file_set_access(nxt_file_name_t *name, nxt_file_access_t access) } +nxt_int_t +nxt_file_chown(nxt_file_name_t *name, const char *owner, const char *group) +{ + int err; + char *buf; + long bufsize; + gid_t gid = ~0; + uid_t uid = ~0; + + if (owner == NULL && group == NULL) { + return NXT_OK; + } + + if (owner != NULL) { + struct passwd pwd, *result; + + bufsize = sysconf(_SC_GETPW_R_SIZE_MAX); + if (bufsize == -1) { + bufsize = 32768; + } + + buf = nxt_malloc(bufsize); + if (buf == NULL) { + return NXT_ERROR; + } + + err = getpwnam_r(owner, &pwd, buf, bufsize, &result); + if (result == NULL) { + nxt_thread_log_alert("getpwnam_r(\"%s\", ...) failed %E %s", + owner, nxt_errno, + err == 0 ? "(User not found)" : ""); + goto out_err_free; + } + + uid = pwd.pw_uid; + + nxt_free(buf); + } + + if (group != NULL) { + struct group grp, *result; + + bufsize = sysconf(_SC_GETGR_R_SIZE_MAX); + if (bufsize == -1) { + bufsize = 32768; + } + + buf = nxt_malloc(bufsize); + if (buf == NULL) { + return NXT_ERROR; + } + + err = getgrnam_r(group, &grp, buf, bufsize, &result); + if (result == NULL) { + nxt_thread_log_alert("getgrnam_r(\"%s\", ...) failed %E %s", + group, nxt_errno, + err == 0 ? "(Group not found)" : ""); + goto out_err_free; + } + + gid = grp.gr_gid; + + nxt_free(buf); + } + + if (nxt_fast_path(chown((const char *) name, uid, gid) == 0)) { + return NXT_OK; + } + + nxt_thread_log_alert("chown(\"%FN\", %l, %l) failed %E", name, + owner != NULL ? (long) uid : -1, + group != NULL ? (long) gid : -1, nxt_errno); + + return NXT_ERROR; + +out_err_free: + nxt_free(buf); + + return NXT_ERROR; +} + + nxt_int_t nxt_file_rename(nxt_file_name_t *old_name, nxt_file_name_t *new_name) { diff --git a/src/nxt_file.h b/src/nxt_file.h index 97636db6..0c03a5f9 100644 --- a/src/nxt_file.h +++ b/src/nxt_file.h @@ -177,6 +177,8 @@ NXT_EXPORT nxt_int_t nxt_file_info(nxt_file_t *file, nxt_file_info_t *fi); NXT_EXPORT nxt_int_t nxt_file_delete(nxt_file_name_t *name); NXT_EXPORT nxt_int_t nxt_file_set_access(nxt_file_name_t *name, nxt_file_access_t access); +NXT_EXPORT nxt_int_t nxt_file_chown(nxt_file_name_t *name, const char *owner, + const char *group); NXT_EXPORT nxt_int_t nxt_file_rename(nxt_file_name_t *old_name, nxt_file_name_t *new_name); -- cgit From b500c36d2e808e123f11a243724f8fdeecd53f86 Mon Sep 17 00:00:00 2001 From: Andrew Clayton Date: Mon, 6 Nov 2023 18:48:51 +0000 Subject: Allow to set the permissions of the Unix domain control socket Several users in GitHub have asked for the ability to set the permissions of the unitd UNIX Domain control socket. This can of course be done externally, but can be done much cleaner by Unit itself. This commit adds three new options --control-mode Set the mode of the socket, e.g 644 --control-user Set the user/owner of the socket, e.g unit --control-group Set the group of the socket, e.g unit Of course these only have an affect when using a UNIX Domain Socket for the control socket. Requested-by: michaelkosir Requested-by: chopanovv Link: Link: Closes: https://github.com/nginx/unit/issues/840 Tested-by: Liam Crilly Reviewed-by: Zhidao Hong Signed-off-by: Andrew Clayton --- src/nxt_listen_socket.c | 14 +++++++++++-- src/nxt_runtime.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++++ src/nxt_runtime.h | 6 +++++- 3 files changed, 72 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nxt_listen_socket.c b/src/nxt_listen_socket.c index d477eef1..047c1ef9 100644 --- a/src/nxt_listen_socket.c +++ b/src/nxt_listen_socket.c @@ -127,13 +127,23 @@ nxt_listen_socket_create(nxt_task_t *task, nxt_mp_t *mp, #if (NXT_HAVE_UNIX_DOMAIN) if (family == AF_UNIX) { - name = (nxt_file_name_t *) sa->u.sockaddr_un.sun_path; + const char *user; + const char *group; + nxt_runtime_t *rt = thr->runtime; - access = (S_IRUSR | S_IWUSR); + name = (nxt_file_name_t *) sa->u.sockaddr_un.sun_path; + access = rt->control_mode > 0 ? rt->control_mode : S_IRUSR | S_IWUSR; if (nxt_file_set_access(name, access) != NXT_OK) { goto listen_fail; } + + user = rt->control_user; + group = rt->control_group; + + if (nxt_file_chown(name, user, group) != NXT_OK) { + goto listen_fail; + } } #endif diff --git a/src/nxt_runtime.c b/src/nxt_runtime.c index 9bfabc75..0e7f879e 100644 --- a/src/nxt_runtime.c +++ b/src/nxt_runtime.c @@ -956,6 +956,12 @@ nxt_runtime_conf_read_cmd(nxt_task_t *task, nxt_runtime_t *rt) static const char no_control[] = "option \"--control\" requires socket address\n"; + static const char no_control_mode[] = + "option \"--control-mode\" requires a mode\n"; + static const char no_control_user[] = + "option \"--control-user\" requires a username\n"; + static const char no_control_group[] = + "option \"--control-group\" requires a group name\n"; static const char no_user[] = "option \"--user\" requires username\n"; static const char no_group[] = "option \"--group\" requires group name\n"; static const char no_pid[] = "option \"--pid\" requires filename\n"; @@ -984,6 +990,13 @@ nxt_runtime_conf_read_cmd(nxt_task_t *task, nxt_runtime_t *rt) " --control ADDRESS set address of control API socket\n" " default: \"" NXT_CONTROL_SOCK "\"\n" "\n" + " --control-mode MODE set mode of the control API socket\n" + " default: 0600\n" + "\n" + " --control-user USER set the owner of the control API socket\n" + "\n" + " --control-group GROUP set the group of the control API socket\n" + "\n" " --pid FILE set pid filename\n" " default: \"" NXT_PID "\"\n" "\n" @@ -1032,6 +1045,48 @@ nxt_runtime_conf_read_cmd(nxt_task_t *task, nxt_runtime_t *rt) continue; } + if (nxt_strcmp(p, "--control-mode") == 0) { + if (*argv == NULL) { + write(STDERR_FILENO, no_control_mode, + nxt_length(no_control_mode)); + return NXT_ERROR; + } + + p = *argv++; + + rt->control_mode = strtoul(p, NULL, 8); + + continue; + } + + if (nxt_strcmp(p, "--control-user") == 0) { + if (*argv == NULL) { + write(STDERR_FILENO, no_control_user, + nxt_length(no_control_user)); + return NXT_ERROR; + } + + p = *argv++; + + rt->control_user = p; + + continue; + } + + if (nxt_strcmp(p, "--control-group") == 0) { + if (*argv == NULL) { + write(STDERR_FILENO, no_control_group, + nxt_length(no_control_group)); + return NXT_ERROR; + } + + p = *argv++; + + rt->control_group = p; + + continue; + } + if (nxt_strcmp(p, "--user") == 0) { if (*argv == NULL) { write(STDERR_FILENO, no_user, nxt_length(no_user)); diff --git a/src/nxt_runtime.h b/src/nxt_runtime.h index 66ec0106..7bd490d7 100644 --- a/src/nxt_runtime.h +++ b/src/nxt_runtime.h @@ -70,8 +70,12 @@ struct nxt_runtime_s { const char *ver_tmp; const char *conf; const char *conf_tmp; - const char *control; const char *tmp; + const char *control; + + mode_t control_mode; + const char *control_user; + const char *control_group; nxt_str_t certs; nxt_str_t scripts; -- cgit From 756feafd541160e97edfb6faf13e9942271629ce Mon Sep 17 00:00:00 2001 From: Dan Callahan Date: Mon, 19 Feb 2024 12:22:05 +0000 Subject: Node.js: Use console.warn instead of stderr.write Functionally identical, but marginally more idiomatic. Refines: fbeb2065b180e2376088387ee150d3975dc08cd5 --- src/nodejs/unit-http/http_server.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src') diff --git a/src/nodejs/unit-http/http_server.js b/src/nodejs/unit-http/http_server.js index b78f309a..4e1c190e 100644 --- a/src/nodejs/unit-http/http_server.js +++ b/src/nodejs/unit-http/http_server.js @@ -5,7 +5,6 @@ 'use strict'; -const { stderr } = require('process'); const EventEmitter = require('events'); const http = require('http'); const util = require('util'); @@ -419,7 +418,7 @@ function Server(options, requestListener) { requestListener = options; options = {}; } else { - stderr.write("http.Server constructor was called with unsupported options, using default settings\n"); + console.warn("http.Server constructor was called with unsupported options, using default settings"); } EventEmitter.call(this); -- cgit From 30b410e49098c5016b9295263a15c479aec77e86 Mon Sep 17 00:00:00 2001 From: Andrew Clayton Date: Mon, 19 Feb 2024 15:20:51 +0000 Subject: Avoid a segfault in nxt_conn_io_sendbuf() This is a simple temporary fix (doesn't address the underlying problem) for an issue reported by a user on GitHub whereby downloading of files from a PHP application would cause the router process to crash. This is actually a generic problem that will affect anything sending data via nxt_unit_response_write(). This is just a simple fix for the 1.32 release, after which the full correct fix will be worked out. Link: Reported-by: rustedsword Co-developed-by: rustedsword Tested-by: rustedsword Tested-by: Andrew Clayton Reviewed-by: Zhidao Hong Signed-off-by: Andrew Clayton --- src/nxt_conn_write.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src') diff --git a/src/nxt_conn_write.c b/src/nxt_conn_write.c index 714a3e15..7d0a579f 100644 --- a/src/nxt_conn_write.c +++ b/src/nxt_conn_write.c @@ -172,6 +172,13 @@ nxt_conn_io_sendbuf(nxt_task_t *task, nxt_sendbuf_t *sb) return 0; } + /* + * XXX Temporary fix for + */ + if (niov == 0 && sb->buf == NULL) { + return 0; + } + if (niov == 0 && nxt_buf_is_file(sb->buf)) { return nxt_conn_io_sendfile(task, sb); } -- cgit From 62894ae77b806e84e7e2c16950850f62825869c2 Mon Sep 17 00:00:00 2001 From: Zhidao HONG Date: Wed, 31 Jan 2024 14:08:57 +0800 Subject: Var: Refactored nxt_var_ref_get() --- src/nxt_var.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/nxt_var.c b/src/nxt_var.c index 729de788..e45c4f14 100644 --- a/src/nxt_var.c +++ b/src/nxt_var.c @@ -132,18 +132,13 @@ nxt_var_ref_get(nxt_tstr_state_t *state, nxt_str_t *name) ref->index = state->var_refs->nelts - 1; - ref->name = nxt_str_dup(state->pool, NULL, name); - if (nxt_slow_path(ref->name == NULL)) { - return NULL; - } - decl = nxt_var_hash_find(name); if (decl != NULL) { ref->handler = decl->handler; ref->cacheable = decl->cacheable; - return ref; + goto done; } ret = nxt_http_unknown_var_ref(state, ref, name); @@ -151,6 +146,13 @@ nxt_var_ref_get(nxt_tstr_state_t *state, nxt_str_t *name) return NULL; } +done: + + ref->name = nxt_str_dup(state->pool, NULL, name); + if (nxt_slow_path(ref->name == NULL)) { + return NULL; + } + return ref; } -- cgit From 01fd121c4e442039662889bb54ce24f21af649f0 Mon Sep 17 00:00:00 2001 From: Zhidao HONG Date: Wed, 31 Jan 2024 14:11:29 +0800 Subject: Var: Refactored nxt_http_unknown_var_ref() --- src/nxt_http_variables.c | 13 ++++++------- src/nxt_var.c | 2 +- src/nxt_var.h | 2 +- 3 files changed, 8 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/nxt_http_variables.c b/src/nxt_http_variables.c index a4205eec..85ae6004 100644 --- a/src/nxt_http_variables.c +++ b/src/nxt_http_variables.c @@ -135,8 +135,7 @@ nxt_http_register_variables(void) nxt_int_t -nxt_http_unknown_var_ref(nxt_tstr_state_t *state, nxt_var_ref_t *ref, - nxt_str_t *name) +nxt_http_unknown_var_ref(nxt_mp_t *mp, nxt_var_ref_t *ref, nxt_str_t *name) { int64_t hash; nxt_str_t str, *lower; @@ -152,7 +151,7 @@ nxt_http_unknown_var_ref(nxt_tstr_state_t *state, nxt_var_ref_t *ref, return NXT_ERROR; } - lower = nxt_str_alloc(state->pool, str.length); + lower = nxt_str_alloc(mp, str.length); if (nxt_slow_path(lower == NULL)) { return NXT_ERROR; } @@ -175,7 +174,7 @@ nxt_http_unknown_var_ref(nxt_tstr_state_t *state, nxt_var_ref_t *ref, return NXT_ERROR; } - hash = nxt_http_header_hash(state->pool, &str); + hash = nxt_http_header_hash(mp, &str); if (nxt_slow_path(hash == -1)) { return NXT_ERROR; } @@ -191,7 +190,7 @@ nxt_http_unknown_var_ref(nxt_tstr_state_t *state, nxt_var_ref_t *ref, return NXT_ERROR; } - hash = nxt_http_argument_hash(state->pool, &str); + hash = nxt_http_argument_hash(mp, &str); if (nxt_slow_path(hash == -1)) { return NXT_ERROR; } @@ -207,7 +206,7 @@ nxt_http_unknown_var_ref(nxt_tstr_state_t *state, nxt_var_ref_t *ref, return NXT_ERROR; } - hash = nxt_http_cookie_hash(state->pool, &str); + hash = nxt_http_cookie_hash(mp, &str); if (nxt_slow_path(hash == -1)) { return NXT_ERROR; } @@ -216,7 +215,7 @@ nxt_http_unknown_var_ref(nxt_tstr_state_t *state, nxt_var_ref_t *ref, return NXT_ERROR; } - ref->data = nxt_var_field_new(state->pool, &str, (uint32_t) hash); + ref->data = nxt_var_field_new(mp, &str, (uint32_t) hash); if (nxt_slow_path(ref->data == NULL)) { return NXT_ERROR; } diff --git a/src/nxt_var.c b/src/nxt_var.c index e45c4f14..a297b5b3 100644 --- a/src/nxt_var.c +++ b/src/nxt_var.c @@ -141,7 +141,7 @@ nxt_var_ref_get(nxt_tstr_state_t *state, nxt_str_t *name) goto done; } - ret = nxt_http_unknown_var_ref(state, ref, name); + ret = nxt_http_unknown_var_ref(state->pool, ref, name); if (nxt_slow_path(ret != NXT_OK)) { return NULL; } diff --git a/src/nxt_var.h b/src/nxt_var.h index fde64f1e..d04ba48a 100644 --- a/src/nxt_var.h +++ b/src/nxt_var.h @@ -62,7 +62,7 @@ nxt_int_t nxt_var_interpreter(nxt_task_t *task, nxt_tstr_state_t *state, nxt_str_t *nxt_var_get(nxt_task_t *task, nxt_var_cache_t *cache, nxt_str_t *name, void *ctx); -nxt_int_t nxt_http_unknown_var_ref(nxt_tstr_state_t *state, nxt_var_ref_t *ref, +nxt_int_t nxt_http_unknown_var_ref(nxt_mp_t *mp, nxt_var_ref_t *ref, nxt_str_t *name); -- cgit From 63507c499e2ff3fa4af416c4d69dbaba2ac1c774 Mon Sep 17 00:00:00 2001 From: Zhidao HONG Date: Wed, 31 Jan 2024 14:14:28 +0800 Subject: Var: Make nxt_var_cache_value() more general This commit enhances nxt_var_cache_value() to enable variable access using string names, complementing the existing reference index method. The modification ensures future compatibility with njs variable access. --- src/nxt_var.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/nxt_var.c b/src/nxt_var.c index a297b5b3..53700f40 100644 --- a/src/nxt_var.c +++ b/src/nxt_var.c @@ -54,7 +54,7 @@ static nxt_var_ref_t *nxt_var_ref_get(nxt_tstr_state_t *state, nxt_str_t *name); 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_tstr_state_t *state, - nxt_var_cache_t *cache, uint32_t index, void *ctx); + nxt_var_cache_t *cache, nxt_var_ref_t *ref, void *ctx); static u_char *nxt_var_next_part(u_char *start, u_char *end, nxt_str_t *part); @@ -205,16 +205,12 @@ nxt_var_cache_test(nxt_lvlhsh_query_t *lhq, void *data) static nxt_str_t * nxt_var_cache_value(nxt_task_t *task, nxt_tstr_state_t *state, - nxt_var_cache_t *cache, uint32_t index, void *ctx) + nxt_var_cache_t *cache, nxt_var_ref_t *ref, void *ctx) { nxt_int_t ret; nxt_str_t *value; - nxt_var_ref_t *ref; nxt_lvlhsh_query_t lhq; - ref = state->var_refs->elts; - ref = &ref[index]; - value = cache->spare; if (value == NULL) { @@ -230,10 +226,10 @@ nxt_var_cache_value(nxt_task_t *task, nxt_tstr_state_t *state, goto not_cached; } - lhq.key_hash = nxt_murmur_hash2_uint32(&index); + lhq.key_hash = nxt_murmur_hash2_uint32(&ref->index); lhq.replace = 0; lhq.key.length = sizeof(uint32_t); - lhq.key.start = (u_char *) &index; + lhq.key.start = (u_char *) &ref->index; lhq.value = value; lhq.proto = &nxt_var_cache_proto; lhq.pool = cache->pool; @@ -493,20 +489,24 @@ nxt_var_interpreter(nxt_task_t *task, nxt_tstr_state_t *state, { u_char *p, *src; size_t length, last, next; + uint32_t index; nxt_str_t *value, **part; nxt_uint_t i; nxt_array_t parts; + nxt_var_ref_t *ref; nxt_var_sub_t *subs; nxt_memzero(&parts, sizeof(nxt_array_t)); nxt_array_init(&parts, cache->pool, sizeof(nxt_str_t *)); + ref = state->var_refs->elts; subs = nxt_var_subs(var); length = var->length; for (i = 0; i < var->vars; i++) { - value = nxt_var_cache_value(task, state, cache, subs[i].index, ctx); + index = subs[i].index; + value = nxt_var_cache_value(task, state, cache, &ref[index], ctx); if (nxt_slow_path(value == NULL)) { return NXT_ERROR; } -- cgit From 465540157fe116db4082f52af4f3f67de33ed7c8 Mon Sep 17 00:00:00 2001 From: Zhidao HONG Date: Wed, 31 Jan 2024 14:32:08 +0800 Subject: Var: Introduced nxt_var_get() This commit is for subsequent commits that will support njs variable accessing. In this commit, nxt_var_get() is introduced to extend the variable handling capabilities. Concurrently, nxt_var_ref_get() has been refactored to use in both configuration and request phases. --- src/nxt_var.c | 48 +++++++++++++++++++++++++++++++++++++----------- src/nxt_var.h | 4 ++-- 2 files changed, 39 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/nxt_var.c b/src/nxt_var.c index 53700f40..2600371b 100644 --- a/src/nxt_var.c +++ b/src/nxt_var.c @@ -50,7 +50,8 @@ struct nxt_var_query_s { static nxt_int_t nxt_var_hash_test(nxt_lvlhsh_query_t *lhq, void *data); static nxt_var_decl_t *nxt_var_hash_find(nxt_str_t *name); -static nxt_var_ref_t *nxt_var_ref_get(nxt_tstr_state_t *state, nxt_str_t *name); +static nxt_var_ref_t *nxt_var_ref_get(nxt_tstr_state_t *state, nxt_str_t *name, + nxt_mp_t *mp); 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_tstr_state_t *state, @@ -109,7 +110,7 @@ nxt_var_hash_find(nxt_str_t *name) static nxt_var_ref_t * -nxt_var_ref_get(nxt_tstr_state_t *state, nxt_str_t *name) +nxt_var_ref_get(nxt_tstr_state_t *state, nxt_str_t *name, nxt_mp_t *mp) { nxt_int_t ret; nxt_uint_t i; @@ -125,12 +126,22 @@ nxt_var_ref_get(nxt_tstr_state_t *state, nxt_str_t *name) } } - ref = nxt_array_add(state->var_refs); - if (nxt_slow_path(ref == NULL)) { - return NULL; - } + if (mp != NULL) { + ref = nxt_mp_alloc(mp, sizeof(nxt_var_ref_t)); + if (nxt_slow_path(ref == NULL)) { + return NULL; + } - ref->index = state->var_refs->nelts - 1; + } else { + ref = nxt_array_add(state->var_refs); + if (nxt_slow_path(ref == NULL)) { + return NULL; + } + + ref->index = state->var_refs->nelts - 1; + + mp = state->pool; + } decl = nxt_var_hash_find(name); @@ -141,14 +152,14 @@ nxt_var_ref_get(nxt_tstr_state_t *state, nxt_str_t *name) goto done; } - ret = nxt_http_unknown_var_ref(state->pool, ref, name); + ret = nxt_http_unknown_var_ref(mp, ref, name); if (nxt_slow_path(ret != NXT_OK)) { return NULL; } done: - ref->name = nxt_str_dup(state->pool, NULL, name); + ref->name = nxt_str_dup(mp, NULL, name); if (nxt_slow_path(ref->name == NULL)) { return NULL; } @@ -355,7 +366,7 @@ nxt_var_compile(nxt_tstr_state_t *state, nxt_str_t *str) next = nxt_var_next_part(p, end, &part); if (part.start != NULL) { - ref = nxt_var_ref_get(state, &part); + ref = nxt_var_ref_get(state, &part, NULL); if (nxt_slow_path(ref == NULL)) { return NULL; } @@ -395,7 +406,7 @@ nxt_var_test(nxt_tstr_state_t *state, nxt_str_t *str, u_char *error) } if (part.start != NULL) { - ref = nxt_var_ref_get(state, &part); + ref = nxt_var_ref_get(state, &part, NULL); if (ref == NULL) { nxt_sprintf(error, error + NXT_MAX_ERROR_STR, @@ -560,3 +571,18 @@ nxt_var_interpreter(nxt_task_t *task, nxt_tstr_state_t *state, return NXT_OK; } + + +nxt_str_t * +nxt_var_get(nxt_task_t *task, nxt_tstr_state_t *state, nxt_var_cache_t *cache, + nxt_str_t *name, void *ctx) +{ + nxt_var_ref_t *ref; + + ref = nxt_var_ref_get(state, name, cache->pool); + if (nxt_slow_path(ref == NULL)) { + return NULL; + } + + return nxt_var_cache_value(task, state, cache, ref, ctx); +} diff --git a/src/nxt_var.h b/src/nxt_var.h index d04ba48a..08a92c08 100644 --- a/src/nxt_var.h +++ b/src/nxt_var.h @@ -59,8 +59,8 @@ nxt_int_t nxt_var_test(nxt_tstr_state_t *state, nxt_str_t *str, u_char *error); nxt_int_t nxt_var_interpreter(nxt_task_t *task, nxt_tstr_state_t *state, 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); +nxt_str_t *nxt_var_get(nxt_task_t *task, nxt_tstr_state_t *state, + nxt_var_cache_t *cache, nxt_str_t *name, void *ctx); nxt_int_t nxt_http_unknown_var_ref(nxt_mp_t *mp, nxt_var_ref_t *ref, nxt_str_t *name); -- cgit From 63ad4deb8a9a7955c5eec3098a2acc3e149831c7 Mon Sep 17 00:00:00 2001 From: Zhidao HONG Date: Wed, 31 Jan 2024 14:38:00 +0800 Subject: NJS: Simplified nxt_js_call() --- src/nxt_js.c | 57 +++++++++++++++++---------------------------------------- 1 file changed, 17 insertions(+), 40 deletions(-) (limited to 'src') diff --git a/src/nxt_js.c b/src/nxt_js.c index 74663660..0e1fe463 100644 --- a/src/nxt_js.c +++ b/src/nxt_js.c @@ -388,16 +388,19 @@ nxt_js_call(nxt_task_t *task, nxt_js_conf_t *jcf, nxt_js_cache_t *cache, njs_vm_t *vm; njs_int_t ret; njs_str_t res; + njs_uint_t i, n; njs_value_t *value; njs_function_t *func; njs_opaque_value_t retval, 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"); + static const njs_str_t js_args[] = { + njs_str("uri"), + njs_str("host"), + njs_str("remoteAddr"), + njs_str("args"), + njs_str("headers"), + njs_str("cookies"), + }; vm = cache->vm; @@ -424,43 +427,17 @@ nxt_js_call(nxt_task_t *task, nxt_js_conf_t *jcf, nxt_js_cache_t *cache, 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; - } + n = nxt_nitems(js_args); - value = njs_vm_object_prop(vm, njs_value_arg(&opaque_value), &cookies_str, - &arguments[5]); - if (nxt_slow_path(value == NULL)) { - return NXT_ERROR; + for (i = 0; i < n; i++) { + value = njs_vm_object_prop(vm, njs_value_arg(&opaque_value), + &js_args[i], &arguments[i]); + if (nxt_slow_path(value == NULL)) { + return NXT_ERROR; + } } - ret = njs_vm_invoke(vm, func, njs_value_arg(&arguments), 6, + ret = njs_vm_invoke(vm, func, njs_value_arg(&arguments), n, njs_value_arg(&retval)); if (ret != NJS_OK) { -- cgit From 33c6c4d4c0e060a974791a472b739214366dead6 Mon Sep 17 00:00:00 2001 From: Zhidao HONG Date: Wed, 31 Jan 2024 14:51:21 +0800 Subject: NJS: variable access support This commit introduces the 'vars' JavaScript object to NJS, enabling direct access to native variables such as $uri and $arg_foo. The syntax is `${vars.var_name}` or `${'vars[var_name]'}`. For example: { "action": { "share": "`/www/html${vars.uri}`" } } --- src/nxt_http_js.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ src/nxt_js.c | 5 +++-- 2 files changed, 52 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nxt_http_js.c b/src/nxt_http_js.c index 72ba761f..e3beb8b4 100644 --- a/src/nxt_http_js.c +++ b/src/nxt_http_js.c @@ -28,6 +28,8 @@ static njs_int_t nxt_http_js_ext_get_cookie(njs_vm_t *vm, njs_value_t *retval); static njs_int_t nxt_http_js_ext_keys_cookie(njs_vm_t *vm, njs_value_t *value, njs_value_t *keys); +static njs_int_t nxt_http_js_ext_get_var(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[] = { @@ -88,6 +90,14 @@ static njs_external_t nxt_http_js_proto[] = { .keys = nxt_http_js_ext_keys_cookie, } }, + + { + .flags = NJS_EXTERN_OBJECT, + .name.string = njs_str("vars"), + .u.object = { + .prop_handler = nxt_http_js_ext_get_var, + } + }, }; @@ -338,3 +348,42 @@ nxt_http_js_ext_keys_cookie(njs_vm_t *vm, njs_value_t *value, njs_value_t *keys) return NJS_OK; } + + +static njs_int_t +nxt_http_js_ext_get_var(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_str_t name, *vv; + nxt_router_conf_t *rtcf; + 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; + } + + rtcf = r->conf->socket_conf->router_conf; + + name.start = key.start; + name.length = key.length; + + vv = nxt_var_get(&r->task, rtcf->tstr_state, &r->tstr_cache.var, &name, r); + + if (vv != NULL) { + return njs_vm_value_string_set(vm, retval, vv->start, vv->length); + } + + njs_value_undefined_set(retval); + + return NJS_DECLINED; +} diff --git a/src/nxt_js.c b/src/nxt_js.c index 0e1fe463..6885afb7 100644 --- a/src/nxt_js.c +++ b/src/nxt_js.c @@ -240,7 +240,7 @@ nxt_js_add_tpl(nxt_js_conf_t *jcf, nxt_str_t *str, nxt_bool_t strz) nxt_str_t *func; static nxt_str_t func_str = nxt_string("function(uri, host, remoteAddr, " - "args, headers, cookies) {" + "args, headers, cookies, vars) {" " return "); /* @@ -391,7 +391,7 @@ nxt_js_call(nxt_task_t *task, nxt_js_conf_t *jcf, nxt_js_cache_t *cache, njs_uint_t i, n; njs_value_t *value; njs_function_t *func; - njs_opaque_value_t retval, opaque_value, arguments[6]; + njs_opaque_value_t retval, opaque_value, arguments[7]; static const njs_str_t js_args[] = { njs_str("uri"), @@ -400,6 +400,7 @@ nxt_js_call(nxt_task_t *task, nxt_js_conf_t *jcf, nxt_js_cache_t *cache, njs_str("args"), njs_str("headers"), njs_str("cookies"), + njs_str("vars"), }; vm = cache->vm; -- cgit From d24ae5a9a4b1140695e027087e72dcfdeb484ec0 Mon Sep 17 00:00:00 2001 From: Gabor Javorszky Date: Thu, 7 Dec 2023 15:07:24 +0000 Subject: Add additional replace rules for node:* modules In that particular issue the compiled nuxt files end up importing the http module as node:http rather than http only. This bypasses unit's custom loader implementation which only check for the http or unit-http modules, and their websocket counterparts. This changeset adds replace sources for both the node:http and node:websocket import signatures. Closes: https://github.com/nginx/unit/issues/1013 Reviewed-by: Andrew Clayton --- src/nodejs/unit-http/loader.js | 2 ++ src/nodejs/unit-http/loader.mjs | 2 ++ 2 files changed, 4 insertions(+) (limited to 'src') diff --git a/src/nodejs/unit-http/loader.js b/src/nodejs/unit-http/loader.js index e5aa3558..849df3d1 100644 --- a/src/nodejs/unit-http/loader.js +++ b/src/nodejs/unit-http/loader.js @@ -11,10 +11,12 @@ if (module.parent && module.parent.id === "internal/preload") { Module.prototype.require = function (id) { switch(id) { case "http": + case "node:http": case "unit-http": return http case "websocket": + case "node:websocket": case "unit-http/websocket": return websocket } diff --git a/src/nodejs/unit-http/loader.mjs b/src/nodejs/unit-http/loader.mjs index 83985b0f..01fa2920 100644 --- a/src/nodejs/unit-http/loader.mjs +++ b/src/nodejs/unit-http/loader.mjs @@ -2,6 +2,7 @@ export async function resolve(specifier, context, defaultResolver) { switch (specifier) { case "websocket": + case "node:websocket": return { url: new URL("./websocket.js", import.meta.url).href, format: "commonjs", @@ -9,6 +10,7 @@ export async function resolve(specifier, context, defaultResolver) { } case "http": + case "node:http": return { url: new URL("./http.js", import.meta.url).href, format: "commonjs", -- cgit From f71ead5fa5b8bae378a0eca2bedb689cf08a8eff Mon Sep 17 00:00:00 2001 From: Andrei Zeliankou Date: Tue, 30 Jan 2024 09:14:39 +0000 Subject: Updated copyright notice. --- src/test/nxt_rbtree1.c | 2 +- src/test/nxt_rbtree1.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/test/nxt_rbtree1.c b/src/test/nxt_rbtree1.c index 1ae059ab..ec024858 100644 --- a/src/test/nxt_rbtree1.c +++ b/src/test/nxt_rbtree1.c @@ -1,7 +1,7 @@ /* * Copyright (C) Igor Sysoev - * Copyright (C) Nginx, Inc. + * Copyright (C) NGINX, Inc. */ diff --git a/src/test/nxt_rbtree1.h b/src/test/nxt_rbtree1.h index 60048dad..d6230ab0 100644 --- a/src/test/nxt_rbtree1.h +++ b/src/test/nxt_rbtree1.h @@ -1,7 +1,7 @@ /* * Copyright (C) Igor Sysoev - * Copyright (C) Nginx, Inc. + * Copyright (C) NGINX, Inc. */ -- cgit From 697a58506235e89af1c8cc3cafc92b3d85a3467d Mon Sep 17 00:00:00 2001 From: Andrei Zeliankou Date: Fri, 26 Jan 2024 14:58:43 +0000 Subject: Python: bytearray body support for ASGI module. @filiphanes requested support for bytearray and memoryview in the request body here: This patch implements bytearray body support only. Memoryview body still need to be implemented. --- src/python/nxt_python_asgi_http.c | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/python/nxt_python_asgi_http.c b/src/python/nxt_python_asgi_http.c index 05c0da4f..cdd6357e 100644 --- a/src/python/nxt_python_asgi_http.c +++ b/src/python/nxt_python_asgi_http.c @@ -362,16 +362,6 @@ nxt_py_asgi_http_response_body(nxt_py_asgi_http_t *http, PyObject *dict) Py_ssize_t body_len, body_off; nxt_py_asgi_ctx_data_t *ctx_data; - body = PyDict_GetItem(dict, nxt_py_body_str); - if (nxt_slow_path(body != NULL && !PyBytes_Check(body))) { - return PyErr_Format(PyExc_TypeError, "'body' is not a byte string"); - } - - more_body = PyDict_GetItem(dict, nxt_py_more_body_str); - if (nxt_slow_path(more_body != NULL && !PyBool_Check(more_body))) { - return PyErr_Format(PyExc_TypeError, "'more_body' is not a bool"); - } - if (nxt_slow_path(http->complete)) { return PyErr_Format(PyExc_RuntimeError, "Unexpected ASGI message 'http.response.body' " @@ -382,9 +372,26 @@ nxt_py_asgi_http_response_body(nxt_py_asgi_http_t *http, PyObject *dict) return PyErr_Format(PyExc_RuntimeError, "Concurrent send"); } + more_body = PyDict_GetItem(dict, nxt_py_more_body_str); + if (nxt_slow_path(more_body != NULL && !PyBool_Check(more_body))) { + return PyErr_Format(PyExc_TypeError, "'more_body' is not a bool"); + } + + body = PyDict_GetItem(dict, nxt_py_body_str); + if (body != NULL) { - body_str = PyBytes_AS_STRING(body); - body_len = PyBytes_GET_SIZE(body); + if (PyBytes_Check(body)) { + body_str = PyBytes_AS_STRING(body); + body_len = PyBytes_GET_SIZE(body); + + } else if (PyByteArray_Check(body)) { + body_str = PyByteArray_AS_STRING(body); + body_len = PyByteArray_GET_SIZE(body); + + } else { + return PyErr_Format(PyExc_TypeError, + "'body' is not a byte string or bytearray"); + } nxt_unit_req_debug(http->req, "asgi_http_response_body: %d, %d", (int) body_len, (more_body == Py_True) ); -- cgit From f2e6447567eef6eeafa833adae0ef155568ec20f Mon Sep 17 00:00:00 2001 From: Andrew Clayton Date: Mon, 5 Feb 2024 21:32:00 +0000 Subject: Wasm-wc: Register a new Wasm component model language module type This is the first commit in adding WebAssembly Component Model language module support. This just adds a new NXT_APP_WASM_WC type, required by subsequent commits. The WC stands for WASI_COMPONENT This new module will have a type of 'wasm-wasi-component'. Link: Signed-off-by: Andrew Clayton --- src/nxt_application.h | 1 + src/nxt_router.c | 1 + 2 files changed, 2 insertions(+) (limited to 'src') diff --git a/src/nxt_application.h b/src/nxt_application.h index 64866db6..f526c20d 100644 --- a/src/nxt_application.h +++ b/src/nxt_application.h @@ -22,6 +22,7 @@ typedef enum { NXT_APP_RUBY, NXT_APP_JAVA, NXT_APP_WASM, + NXT_APP_WASM_WC, NXT_APP_UNKNOWN, } nxt_app_type_t; diff --git a/src/nxt_router.c b/src/nxt_router.c index 947836c8..1a1aca2b 100644 --- a/src/nxt_router.c +++ b/src/nxt_router.c @@ -281,6 +281,7 @@ static const nxt_str_t *nxt_app_msg_prefix[] = { [NXT_APP_RUBY] = &http_prefix, [NXT_APP_JAVA] = &empty_prefix, [NXT_APP_WASM] = &empty_prefix, + [NXT_APP_WASM_WC] = &empty_prefix, }; -- cgit From f0782722654158c38193183e4c9b74925a0c52ef Mon Sep 17 00:00:00 2001 From: Andrew Clayton Date: Mon, 5 Feb 2024 21:43:14 +0000 Subject: Wasm-wc: Add core configuration data structure This is required to actually _build_ the 'wasm-wasi-componet' language module. The nxt_wasm_wc_app_conf_t structure consists of the component name, e.g my_component.wasm, this is required. It also consists of an object to store the directories that are allowed access to by the component, this is optional. The bulk of the configuration infrastructure will be added in a subsequent commit. Signed-off-by: Andrew Clayton --- src/nxt_application.h | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src') diff --git a/src/nxt_application.h b/src/nxt_application.h index f526c20d..f5d7a9df 100644 --- a/src/nxt_application.h +++ b/src/nxt_application.h @@ -105,6 +105,13 @@ typedef struct { } nxt_wasm_app_conf_t; +typedef struct { + const char *component; + + nxt_conf_value_t *access; +} nxt_wasm_wc_app_conf_t; + + struct nxt_common_app_conf_s { nxt_str_t name; nxt_str_t type; @@ -134,6 +141,7 @@ struct nxt_common_app_conf_s { nxt_ruby_app_conf_t ruby; nxt_java_app_conf_t java; nxt_wasm_app_conf_t wasm; + nxt_wasm_wc_app_conf_t wasm_wc; } u; nxt_conf_value_t *self; -- cgit From 20ada4b5c135862104ca724a6d9d17730286aa82 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sun, 29 Oct 2023 15:13:56 -0700 Subject: Wasm-wc: Core of initial Wasm component model language module support This is the work of Alex Crichton. This is written in Rust. The problem is that there is currently no support on the C side of things for the component model, which is the point of this module. It talks to Unit via automatically generated bindings. I've (Andrew) just made some minor tweaks to src/lib.rs, build.rs & Cargo.toml to adjust some paths, adjust where we get the language module config from and the module name and where it's located in the source tree, I also removed and disabled the tracking of the Cargo.lock file, this is constantly changing and not tracking it seems right for 'libraries' and dropped the README's... Other than that I have tried to leave his work intact, subsequent commits will make some larger changes, but I didn't want to intermix them with Alex's work. One such commit will update the module to use wasmtime 17 which brings WASI 0.2.0 support. Signed-off-by: Andrew Clayton --- src/wasm-wasi-component/.gitignore | 3 + src/wasm-wasi-component/Cargo.toml | 30 ++ src/wasm-wasi-component/build.rs | 33 +++ src/wasm-wasi-component/src/lib.rs | 549 +++++++++++++++++++++++++++++++++++++ src/wasm-wasi-component/wrapper.h | 5 + 5 files changed, 620 insertions(+) create mode 100644 src/wasm-wasi-component/.gitignore create mode 100644 src/wasm-wasi-component/Cargo.toml create mode 100644 src/wasm-wasi-component/build.rs create mode 100644 src/wasm-wasi-component/src/lib.rs create mode 100644 src/wasm-wasi-component/wrapper.h (limited to 'src') diff --git a/src/wasm-wasi-component/.gitignore b/src/wasm-wasi-component/.gitignore new file mode 100644 index 00000000..159e3885 --- /dev/null +++ b/src/wasm-wasi-component/.gitignore @@ -0,0 +1,3 @@ +Cargo.lock + +target diff --git a/src/wasm-wasi-component/Cargo.toml b/src/wasm-wasi-component/Cargo.toml new file mode 100644 index 00000000..8f42d128 --- /dev/null +++ b/src/wasm-wasi-component/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "wasm-wasi-component" +version = "0.1.0" +edition = "2021" +publish = false + +[lib] +crate-type = ["cdylib"] + +[dependencies] +anyhow = "1.0.75" +bytes = "1.5.0" +futures-util = { version = "0.3.29", default-features = false } +http = "0.2.9" +http-body = { version = "1.0.0-rc.2", default-features = false } +http-body-util = "0.1.0-rc.2" +tokio = { version = "1.33.0", default-features = false } +wasmtime = "14.0.2" +wasmtime-wasi = "14.0.2" +wasmtime-wasi-http = "14.0.2" + +[build-dependencies] +bindgen = "0.68.1" +cc = "1.0.83" + +[profile.dev] +panic = 'abort' + +[profile.release] +panic = 'abort' diff --git a/src/wasm-wasi-component/build.rs b/src/wasm-wasi-component/build.rs new file mode 100644 index 00000000..5ea74f17 --- /dev/null +++ b/src/wasm-wasi-component/build.rs @@ -0,0 +1,33 @@ +use std::env; +use std::path::PathBuf; + +fn main() { + // Tell cargo to invalidate the built crate whenever the wrapper changes + println!("cargo:rerun-if-changed=wrapper.h"); + + let bindings = bindgen::Builder::default() + .clang_args(["-I", "../"]) + .clang_args(["-I", "../../build/include"]) + .header("./wrapper.h") + // only generate bindings for `nxt_*` header files + .allowlist_file(".*nxt_.*.h") + // generates an "improper_ctypes" warning and we don't need it anyway + .blocklist_function("nxt_vsprintf") + // Tell cargo to invalidate the built crate whenever any of the + // included header files changed. + .parse_callbacks(Box::new(bindgen::CargoCallbacks)) + // disable some features which aren't necessary + .layout_tests(false) + .derive_debug(false) + .generate() + .expect("Unable to generate bindings"); + + cc::Build::new() + .object("../../build/src/nxt_unit.o") + .compile("nxt-unit"); + + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); + bindings + .write_to_file(out_path.join("bindings.rs")) + .expect("Couldn't write bindings!"); +} diff --git a/src/wasm-wasi-component/src/lib.rs b/src/wasm-wasi-component/src/lib.rs new file mode 100644 index 00000000..54d99616 --- /dev/null +++ b/src/wasm-wasi-component/src/lib.rs @@ -0,0 +1,549 @@ +use anyhow::{bail, Context, Result}; +use bytes::{Bytes, BytesMut}; +use http_body_util::combinators::BoxBody; +use http_body_util::{BodyExt, Full}; +use std::ffi::{CStr, CString}; +use std::mem::MaybeUninit; +use std::ptr; +use std::sync::OnceLock; +use tokio::sync::mpsc; +use wasmtime::component::{Component, InstancePre, Linker}; +use wasmtime::{Config, Engine, Store}; +use wasmtime_wasi::preview2::{DirPerms, FilePerms, Table, WasiCtx, WasiCtxBuilder, WasiView}; +use wasmtime_wasi::{ambient_authority, Dir}; +use wasmtime_wasi_http::{WasiHttpCtx, WasiHttpView}; + +#[allow( + non_camel_case_types, + non_upper_case_globals, + non_snake_case, + dead_code +)] +mod bindings { + include!(concat!(env!("OUT_DIR"), "/bindings.rs")); + + pub const fn nxt_string(s: &'static str) -> nxt_str_t { + nxt_str_t { + start: s.as_ptr().cast_mut(), + length: s.len(), + } + } + + pub unsafe fn nxt_unit_sptr_get(sptr: &nxt_unit_sptr_t) -> *const u8 { + sptr.base.as_ptr().offset(sptr.offset as isize) + } +} + +#[no_mangle] +pub static mut nxt_app_module: bindings::nxt_app_module_t = { + const COMPAT: [u32; 2] = [bindings::NXT_VERNUM, bindings::NXT_DEBUG]; + let version = "0.1\0"; + bindings::nxt_app_module_t { + compat: COMPAT.as_ptr().cast_mut(), + compat_length: COMPAT.len() * 4, + mounts: ptr::null(), + nmounts: 0, + type_: bindings::nxt_string("wasm-wasi-component"), + version: version.as_ptr().cast(), + setup: Some(setup), + start: Some(start), + } +}; + +static GLOBAL_CONFIG: OnceLock = OnceLock::new(); +static GLOBAL_STATE: OnceLock = OnceLock::new(); + +unsafe extern "C" fn setup( + task: *mut bindings::nxt_task_t, + // TODO: should this get used? + _process: *mut bindings::nxt_process_t, + conf: *mut bindings::nxt_common_app_conf_t, +) -> bindings::nxt_int_t { + handle_result(task, || { + let wasm_conf = &(*conf).u.wasm_wc; + let component = CStr::from_ptr(wasm_conf.component).to_str()?; + let mut dirs = Vec::new(); + if !wasm_conf.access.is_null() { + let dirs_ptr = bindings::nxt_conf_get_object_member( + wasm_conf.access, + &mut bindings::nxt_string("filesystem"), + ptr::null_mut(), + ); + for i in 0..bindings::nxt_conf_object_members_count(dirs_ptr) { + let value = bindings::nxt_conf_get_array_element(dirs_ptr, i.try_into().unwrap()); + let mut s = bindings::nxt_string(""); + bindings::nxt_conf_get_string(value, &mut s); + dirs.push( + std::str::from_utf8(std::slice::from_raw_parts(s.start, s.length))?.to_string(), + ); + } + } + + let result = GLOBAL_CONFIG.set(GlobalConfig { + component: component.to_string(), + dirs, + }); + assert!(result.is_ok()); + Ok(()) + }) +} + +unsafe extern "C" fn start( + task: *mut bindings::nxt_task_t, + data: *mut bindings::nxt_process_data_t, +) -> bindings::nxt_int_t { + handle_result(task, || { + let config = GLOBAL_CONFIG.get().unwrap(); + let state = GlobalState::new(&config).context("failed to create initial state")?; + let res = GLOBAL_STATE.set(state); + assert!(res.is_ok()); + + let conf = (*data).app; + let mut wasm_init = MaybeUninit::uninit(); + let ret = bindings::nxt_unit_default_init(task, wasm_init.as_mut_ptr(), conf); + if ret != bindings::NXT_OK as bindings::nxt_int_t { + bail!("nxt_unit_default_init() failed"); + } + let mut wasm_init = wasm_init.assume_init(); + wasm_init.callbacks.request_handler = Some(request_handler); + + let unit_ctx = bindings::nxt_unit_init(&mut wasm_init); + if unit_ctx.is_null() { + bail!("nxt_unit_init() failed"); + } + + bindings::nxt_unit_run(unit_ctx); + bindings::nxt_unit_done(unit_ctx); + + Ok(()) + }) +} + +unsafe fn handle_result( + task: *mut bindings::nxt_task_t, + func: impl FnOnce() -> Result<()>, +) -> bindings::nxt_int_t { + let rc = match func() { + Ok(()) => bindings::NXT_OK as bindings::nxt_int_t, + Err(e) => { + alert(task, &format!("{e:?}")); + bindings::NXT_ERROR as bindings::nxt_int_t + } + }; + return rc; + + unsafe fn alert(task: *mut bindings::nxt_task_t, msg: &str) { + let log = (*task).log; + let msg = CString::new(msg).unwrap(); + ((*log).handler).unwrap()( + bindings::NXT_LOG_ALERT as bindings::nxt_uint_t, + log, + "%s\0".as_ptr().cast(), + msg.as_ptr(), + ); + } +} + +unsafe extern "C" fn request_handler(info: *mut bindings::nxt_unit_request_info_t) { + // Enqueue this request to get processed by the Tokio event loop, and + // otherwise immediately return. + let state = GLOBAL_STATE.get().unwrap(); + state.sender.blocking_send(NxtRequestInfo { info }).unwrap(); +} + +struct GlobalConfig { + component: String, + dirs: Vec, +} + +struct GlobalState { + engine: Engine, + component: InstancePre, + global_config: &'static GlobalConfig, + sender: mpsc::Sender, +} + +impl GlobalState { + fn new(global_config: &'static GlobalConfig) -> Result { + // Configure Wasmtime, e.g. the component model and async support are + // enabled here. Other configuration can include: + // + // * Epochs/fuel - enables async yielding to prevent any one request + // starving others. + // * Pooling allocator - accelerates instantiation at the cost of a + // large virtual memory reservation. + // * Memory limits/etc. + let mut config = Config::new(); + config.wasm_component_model(true); + config.async_support(true); + let engine = Engine::new(&config)?; + + // Compile the binary component on disk in Wasmtime. This is then + // pre-instantiated with host APIs defined by WASI. The result of + // this is a "pre-instantiated instance" which can be used to + // repeatedly instantiate later on. This will frontload + // compilation/linking/type-checking/etc to happen once rather than on + // each request. + let component = Component::from_file(&engine, &global_config.component) + .context("failed to compile component")?; + let mut linker = Linker::::new(&engine); + wasmtime_wasi_http::proxy::add_to_linker(&mut linker)?; + let component = linker + .instantiate_pre(&component) + .context("failed to pre-instantiate the provided component")?; + + // Spin up the Tokio async runtime in a separate thread with a + // communication channel into it. This thread will send requests to + // Tokio and the results will be calculated there. + let (sender, receiver) = mpsc::channel(10); + std::thread::spawn(|| GlobalState::run(receiver)); + + Ok(GlobalState { + engine, + component, + sender, + global_config, + }) + } + + /// Worker thread that executes the Tokio runtime, infinitely receiving + /// messages from the provided `receiver` and handling those requests. + /// + /// Each request is handled in a separate subtask so processing can all + /// happen concurrently. + fn run(mut receiver: mpsc::Receiver) { + let rt = tokio::runtime::Runtime::new().unwrap(); + rt.block_on(async { + while let Some(msg) = receiver.recv().await { + let state = GLOBAL_STATE.get().unwrap(); + tokio::task::spawn(async move { + state.handle(msg).await.expect("failed to handle request") + }); + } + }); + } + + async fn handle(&'static self, mut info: NxtRequestInfo) -> Result<()> { + // Create a "Store" which is the unit of per-request isolation in + // Wasmtime. + let data = StoreState { + ctx: { + let mut cx = WasiCtxBuilder::new(); + // NB: while useful for debugging untrusted code probably + // shouldn't get raw access to stdout/stderr. + cx.inherit_stdout(); + cx.inherit_stderr(); + for dir in self.global_config.dirs.iter() { + let fd = Dir::open_ambient_dir(dir, ambient_authority()) + .with_context(|| format!("failed to open directory '{dir}'"))?; + cx.preopened_dir(fd, DirPerms::all(), FilePerms::all(), dir); + } + cx.build() + }, + table: Table::default(), + http: WasiHttpCtx, + }; + let mut store = Store::new(&self.engine, data); + + // Convert the `nxt_*` representation into the representation required + // by Wasmtime's `wasi-http` implementation using the Rust `http` + // crate. + let request = self.to_request_builder(&info)?; + let body = self.to_request_body(&mut info); + let request = request.body(body)?; + + let (sender, receiver) = tokio::sync::oneshot::channel(); + + // Instantiate the WebAssembly component and invoke its `handle` + // function which receives a request and where to put a response. + // + // Note that this is done in a sub-task to work concurrently with + // writing the response when it's available. This enables wasm to + // generate headers, write those below, and then compute the body + // afterwards. + let task = tokio::spawn(async move { + let (proxy, _) = + wasmtime_wasi_http::proxy::Proxy::instantiate_pre(&mut store, &self.component) + .await + .context("failed to instantiate")?; + let req = store.data_mut().new_incoming_request(request)?; + let out = store.data_mut().new_response_outparam(sender)?; + proxy + .wasi_http_incoming_handler() + .call_handle(&mut store, req, out) + .await + .context("failed to invoke wasm `handle`")?; + Ok::<_, anyhow::Error>(()) + }); + + // Wait for the wasm to produce the initial response. If this succeeds + // then propagate that failure. If this fails then wait for the above + // task to complete to see if it failed, otherwise panic since that's + // unexpected. + let response = match receiver.await { + Ok(response) => response.context("response generation failed")?, + Err(_) => { + task.await.unwrap()?; + panic!("sender of response disappeared"); + } + }; + + // Send the headers/status which will extract the body for the next + // phase. + let body = self.send_response(&mut info, response); + + // Send the body, a blocking operation, over time as it becomes + // available. + self.send_response_body(&mut info, body) + .await + .context("failed to write response body")?; + + // Join on completion of the wasm task which should be done by this + // point. + task.await.unwrap()?; + + // And finally signal that we're done. + info.request_done(); + + Ok(()) + } + + fn to_request_builder(&self, info: &NxtRequestInfo) -> Result { + let mut request = http::Request::builder(); + + request = request.method(info.method()); + request = match info.version() { + "HTTP/0.9" => request.version(http::Version::HTTP_09), + "HTTP/1.0" => request.version(http::Version::HTTP_10), + "HTTP/1.1" => request.version(http::Version::HTTP_11), + "HTTP/2.0" => request.version(http::Version::HTTP_2), + "HTTP/3.0" => request.version(http::Version::HTTP_3), + version => { + println!("unknown version: {version}"); + request + } + }; + + let uri = http::Uri::builder() + .scheme(if info.tls() { "https" } else { "http" }) + .authority(info.server_name()) + .path_and_query(info.target()) + .build() + .context("failed to build URI")?; + request = request.uri(uri); + + for (name, value) in info.fields() { + request = request.header(name, value); + } + Ok(request) + } + + fn to_request_body(&self, info: &mut NxtRequestInfo) -> BoxBody { + // TODO: should convert the body into a form of `Stream` to become an async + // stream of frames. The return value can represent that here but for now + // this slurps up the entire body into memory and puts it all in a single + // `BytesMut` which is then converted to `Bytes`. + let mut body = BytesMut::with_capacity(info.content_length().try_into().unwrap()); + + // TODO: can this perform a partial read? + // TODO: how to make this async at the nxt level? + info.request_read(&mut body); + + Full::new(body.freeze()).map_err(|e| match e {}).boxed() + } + + fn send_response(&self, info: &mut NxtRequestInfo, response: http::Response) -> T { + info.init_response( + response.status().as_u16(), + response.headers().len().try_into().unwrap(), + response + .headers() + .iter() + .map(|(k, v)| k.as_str().len() + v.len()) + .sum::() + .try_into() + .unwrap(), + ); + for (k, v) in response.headers() { + info.add_field(k.as_str().as_bytes(), v.as_bytes()); + } + info.send_response(); + + response.into_body() + } + + async fn send_response_body( + &self, + info: &mut NxtRequestInfo, + mut body: BoxBody, + ) -> Result<()> { + loop { + // Acquire the next frame, and because nothing is actually async at the + // moment this should never block meaning that the `Pending` case + // should not happen. + let frame = match body.frame().await { + Some(Ok(frame)) => frame, + Some(Err(e)) => break Err(e), + None => break Ok(()), + }; + match frame.data_ref() { + Some(data) => { + info.response_write(&data); + } + None => { + // TODO: what to do with trailers? + } + } + } + } +} + +struct NxtRequestInfo { + info: *mut bindings::nxt_unit_request_info_t, +} + +// TODO: is this actually safe? +unsafe impl Send for NxtRequestInfo {} +unsafe impl Sync for NxtRequestInfo {} + +impl NxtRequestInfo { + fn method(&self) -> &str { + unsafe { + let raw = (*self.info).request; + self.get_str(&(*raw).method, (*raw).method_length.into()) + } + } + + fn tls(&self) -> bool { + unsafe { (*(*self.info).request).tls != 0 } + } + + fn version(&self) -> &str { + unsafe { + let raw = (*self.info).request; + self.get_str(&(*raw).version, (*raw).version_length.into()) + } + } + + fn server_name(&self) -> &str { + unsafe { + let raw = (*self.info).request; + self.get_str(&(*raw).server_name, (*raw).server_name_length.into()) + } + } + + fn target(&self) -> &str { + unsafe { + let raw = (*self.info).request; + self.get_str(&(*raw).target, (*raw).target_length.into()) + } + } + + fn content_length(&self) -> u64 { + unsafe { + let raw_request = (*self.info).request; + (*raw_request).content_length + } + } + + fn fields(&self) -> impl Iterator { + unsafe { + let raw = (*self.info).request; + (0..(*raw).fields_count).map(move |i| { + let field = (*raw).fields.as_ptr().add(i as usize); + let name = self.get_str(&(*field).name, (*field).name_length.into()); + let value = self.get_str(&(*field).value, (*field).value_length.into()); + (name, value) + }) + } + } + + fn request_read(&mut self, dst: &mut BytesMut) { + unsafe { + let rest = dst.spare_capacity_mut(); + let amt = + bindings::nxt_unit_request_read(self.info, rest.as_mut_ptr().cast(), rest.len()); + // TODO: handle failure when `amt` is negative + let amt: usize = amt.try_into().unwrap(); + dst.set_len(dst.len() + amt); + } + } + + fn response_write(&mut self, data: &[u8]) { + unsafe { + let rc = bindings::nxt_unit_response_write(self.info, data.as_ptr().cast(), data.len()); + assert_eq!(rc, 0); + } + } + + fn init_response(&mut self, status: u16, headers: u32, headers_size: u32) { + unsafe { + let rc = bindings::nxt_unit_response_init(self.info, status, headers, headers_size); + assert_eq!(rc, 0); + } + } + + fn add_field(&mut self, key: &[u8], val: &[u8]) { + unsafe { + let rc = bindings::nxt_unit_response_add_field( + self.info, + key.as_ptr().cast(), + key.len().try_into().unwrap(), + val.as_ptr().cast(), + val.len().try_into().unwrap(), + ); + assert_eq!(rc, 0); + } + } + + fn send_response(&mut self) { + unsafe { + let rc = bindings::nxt_unit_response_send(self.info); + assert_eq!(rc, 0); + } + } + + fn request_done(self) { + unsafe { + bindings::nxt_unit_request_done(self.info, bindings::NXT_UNIT_OK as i32); + } + } + + unsafe fn get_str(&self, ptr: &bindings::nxt_unit_sptr_t, len: u32) -> &str { + let ptr = bindings::nxt_unit_sptr_get(ptr); + let slice = std::slice::from_raw_parts(ptr, len.try_into().unwrap()); + std::str::from_utf8(slice).unwrap() + } +} + +struct StoreState { + ctx: WasiCtx, + http: WasiHttpCtx, + table: Table, +} + +impl WasiView for StoreState { + fn table(&self) -> &Table { + &self.table + } + fn table_mut(&mut self) -> &mut Table { + &mut self.table + } + fn ctx(&self) -> &WasiCtx { + &self.ctx + } + fn ctx_mut(&mut self) -> &mut WasiCtx { + &mut self.ctx + } +} + +impl WasiHttpView for StoreState { + fn ctx(&mut self) -> &mut WasiHttpCtx { + &mut self.http + } + fn table(&mut self) -> &mut Table { + &mut self.table + } +} + +impl StoreState {} diff --git a/src/wasm-wasi-component/wrapper.h b/src/wasm-wasi-component/wrapper.h new file mode 100644 index 00000000..93f3014a --- /dev/null +++ b/src/wasm-wasi-component/wrapper.h @@ -0,0 +1,5 @@ +#include +#include +#include +#include +#include -- cgit From 79c817724799a3c6ce88693b188926b4e626807f Mon Sep 17 00:00:00 2001 From: Andrew Clayton Date: Tue, 6 Feb 2024 16:33:59 +0000 Subject: Wasm-wc: Run src/lib.rs through rustfmt Run from the repository root like $ rustfmt --edition 2021 src/wasm-wasi-component/src/lib.rs Also manually fix up some overly long comments. Signed-off-by: Andrew Clayton --- src/wasm-wasi-component/src/lib.rs | 115 +++++++++++++++++++++++++++---------- 1 file changed, 84 insertions(+), 31 deletions(-) (limited to 'src') diff --git a/src/wasm-wasi-component/src/lib.rs b/src/wasm-wasi-component/src/lib.rs index 54d99616..da78ea6e 100644 --- a/src/wasm-wasi-component/src/lib.rs +++ b/src/wasm-wasi-component/src/lib.rs @@ -9,7 +9,9 @@ use std::sync::OnceLock; use tokio::sync::mpsc; use wasmtime::component::{Component, InstancePre, Linker}; use wasmtime::{Config, Engine, Store}; -use wasmtime_wasi::preview2::{DirPerms, FilePerms, Table, WasiCtx, WasiCtxBuilder, WasiView}; +use wasmtime_wasi::preview2::{ + DirPerms, FilePerms, Table, WasiCtx, WasiCtxBuilder, WasiView, +}; use wasmtime_wasi::{ambient_authority, Dir}; use wasmtime_wasi_http::{WasiHttpCtx, WasiHttpView}; @@ -70,11 +72,17 @@ unsafe extern "C" fn setup( ptr::null_mut(), ); for i in 0..bindings::nxt_conf_object_members_count(dirs_ptr) { - let value = bindings::nxt_conf_get_array_element(dirs_ptr, i.try_into().unwrap()); + let value = bindings::nxt_conf_get_array_element( + dirs_ptr, + i.try_into().unwrap(), + ); let mut s = bindings::nxt_string(""); bindings::nxt_conf_get_string(value, &mut s); dirs.push( - std::str::from_utf8(std::slice::from_raw_parts(s.start, s.length))?.to_string(), + std::str::from_utf8(std::slice::from_raw_parts( + s.start, s.length, + ))? + .to_string(), ); } } @@ -94,13 +102,15 @@ unsafe extern "C" fn start( ) -> bindings::nxt_int_t { handle_result(task, || { let config = GLOBAL_CONFIG.get().unwrap(); - let state = GlobalState::new(&config).context("failed to create initial state")?; + let state = GlobalState::new(&config) + .context("failed to create initial state")?; let res = GLOBAL_STATE.set(state); assert!(res.is_ok()); let conf = (*data).app; let mut wasm_init = MaybeUninit::uninit(); - let ret = bindings::nxt_unit_default_init(task, wasm_init.as_mut_ptr(), conf); + let ret = + bindings::nxt_unit_default_init(task, wasm_init.as_mut_ptr(), conf); if ret != bindings::NXT_OK as bindings::nxt_int_t { bail!("nxt_unit_default_init() failed"); } @@ -144,7 +154,9 @@ unsafe fn handle_result( } } -unsafe extern "C" fn request_handler(info: *mut bindings::nxt_unit_request_info_t) { +unsafe extern "C" fn request_handler( + info: *mut bindings::nxt_unit_request_info_t, +) { // Enqueue this request to get processed by the Tokio event loop, and // otherwise immediately return. let state = GLOBAL_STATE.get().unwrap(); @@ -235,8 +247,15 @@ impl GlobalState { cx.inherit_stderr(); for dir in self.global_config.dirs.iter() { let fd = Dir::open_ambient_dir(dir, ambient_authority()) - .with_context(|| format!("failed to open directory '{dir}'"))?; - cx.preopened_dir(fd, DirPerms::all(), FilePerms::all(), dir); + .with_context(|| { + format!("failed to open directory '{dir}'") + })?; + cx.preopened_dir( + fd, + DirPerms::all(), + FilePerms::all(), + dir, + ); } cx.build() }, @@ -262,10 +281,12 @@ impl GlobalState { // generate headers, write those below, and then compute the body // afterwards. let task = tokio::spawn(async move { - let (proxy, _) = - wasmtime_wasi_http::proxy::Proxy::instantiate_pre(&mut store, &self.component) - .await - .context("failed to instantiate")?; + let (proxy, _) = wasmtime_wasi_http::proxy::Proxy::instantiate_pre( + &mut store, + &self.component, + ) + .await + .context("failed to instantiate")?; let req = store.data_mut().new_incoming_request(request)?; let out = store.data_mut().new_response_outparam(sender)?; proxy @@ -308,7 +329,10 @@ impl GlobalState { Ok(()) } - fn to_request_builder(&self, info: &NxtRequestInfo) -> Result { + fn to_request_builder( + &self, + info: &NxtRequestInfo, + ) -> Result { let mut request = http::Request::builder(); request = request.method(info.method()); @@ -338,12 +362,16 @@ impl GlobalState { Ok(request) } - fn to_request_body(&self, info: &mut NxtRequestInfo) -> BoxBody { - // TODO: should convert the body into a form of `Stream` to become an async - // stream of frames. The return value can represent that here but for now - // this slurps up the entire body into memory and puts it all in a single - // `BytesMut` which is then converted to `Bytes`. - let mut body = BytesMut::with_capacity(info.content_length().try_into().unwrap()); + fn to_request_body( + &self, + info: &mut NxtRequestInfo, + ) -> BoxBody { + // TODO: should convert the body into a form of `Stream` to become an + // async stream of frames. The return value can represent that here + // but for now this slurps up the entire body into memory and puts it + // all in a single `BytesMut` which is then converted to `Bytes`. + let mut body = + BytesMut::with_capacity(info.content_length().try_into().unwrap()); // TODO: can this perform a partial read? // TODO: how to make this async at the nxt level? @@ -352,7 +380,11 @@ impl GlobalState { Full::new(body.freeze()).map_err(|e| match e {}).boxed() } - fn send_response(&self, info: &mut NxtRequestInfo, response: http::Response) -> T { + fn send_response( + &self, + info: &mut NxtRequestInfo, + response: http::Response, + ) -> T { info.init_response( response.status().as_u16(), response.headers().len().try_into().unwrap(), @@ -378,9 +410,9 @@ impl GlobalState { mut body: BoxBody, ) -> Result<()> { loop { - // Acquire the next frame, and because nothing is actually async at the - // moment this should never block meaning that the `Pending` case - // should not happen. + // Acquire the next frame, and because nothing is actually async + // at the moment this should never block meaning that the + // `Pending` case should not happen. let frame = match body.frame().await { Some(Ok(frame)) => frame, Some(Err(e)) => break Err(e), @@ -451,8 +483,10 @@ impl NxtRequestInfo { let raw = (*self.info).request; (0..(*raw).fields_count).map(move |i| { let field = (*raw).fields.as_ptr().add(i as usize); - let name = self.get_str(&(*field).name, (*field).name_length.into()); - let value = self.get_str(&(*field).value, (*field).value_length.into()); + let name = + self.get_str(&(*field).name, (*field).name_length.into()); + let value = + self.get_str(&(*field).value, (*field).value_length.into()); (name, value) }) } @@ -461,8 +495,11 @@ impl NxtRequestInfo { fn request_read(&mut self, dst: &mut BytesMut) { unsafe { let rest = dst.spare_capacity_mut(); - let amt = - bindings::nxt_unit_request_read(self.info, rest.as_mut_ptr().cast(), rest.len()); + let amt = bindings::nxt_unit_request_read( + self.info, + rest.as_mut_ptr().cast(), + rest.len(), + ); // TODO: handle failure when `amt` is negative let amt: usize = amt.try_into().unwrap(); dst.set_len(dst.len() + amt); @@ -471,14 +508,23 @@ impl NxtRequestInfo { fn response_write(&mut self, data: &[u8]) { unsafe { - let rc = bindings::nxt_unit_response_write(self.info, data.as_ptr().cast(), data.len()); + let rc = bindings::nxt_unit_response_write( + self.info, + data.as_ptr().cast(), + data.len(), + ); assert_eq!(rc, 0); } } fn init_response(&mut self, status: u16, headers: u32, headers_size: u32) { unsafe { - let rc = bindings::nxt_unit_response_init(self.info, status, headers, headers_size); + let rc = bindings::nxt_unit_response_init( + self.info, + status, + headers, + headers_size, + ); assert_eq!(rc, 0); } } @@ -505,11 +551,18 @@ impl NxtRequestInfo { fn request_done(self) { unsafe { - bindings::nxt_unit_request_done(self.info, bindings::NXT_UNIT_OK as i32); + bindings::nxt_unit_request_done( + self.info, + bindings::NXT_UNIT_OK as i32, + ); } } - unsafe fn get_str(&self, ptr: &bindings::nxt_unit_sptr_t, len: u32) -> &str { + unsafe fn get_str( + &self, + ptr: &bindings::nxt_unit_sptr_t, + len: u32, + ) -> &str { let ptr = bindings::nxt_unit_sptr_get(ptr); let slice = std::slice::from_raw_parts(ptr, len.try_into().unwrap()); std::str::from_utf8(slice).unwrap() -- cgit From ac3a54d67181bb8bfbe6753058941c91dd200c63 Mon Sep 17 00:00:00 2001 From: Andrew Clayton Date: Mon, 29 Jan 2024 11:35:13 +0000 Subject: Wasm-wc: Improve request buffer handling When Unit receives a request, if the body of that request is greater than a certain amount (16KiB by default) then it is written to a temporary file. When a language module goes to read the request body in such situations it will end up using read(2). The wasm-wasi-component language module was failing to properly read request bodies of around 2GiB or more. This is because (on Linux at least) read(2) (and other related system calls) will only read (or write) at most 0x7ffff000 (2,147,479,552) bytes, this is the case for both 32 and 64-bit systems. Regardless, it's probably not a good idea doing IO in such large chunks anyway. This patch changes the wasm-wasi-component language module to read the request buffer in 32MiB chunks (this matches the original 'wasm' language module). We are still limited to a 4GiB address space and can only upload files a little under 4GiB. Signed-off-by: Andrew Clayton --- src/wasm-wasi-component/src/lib.rs | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/wasm-wasi-component/src/lib.rs b/src/wasm-wasi-component/src/lib.rs index da78ea6e..032878f5 100644 --- a/src/wasm-wasi-component/src/lib.rs +++ b/src/wasm-wasi-component/src/lib.rs @@ -495,14 +495,21 @@ impl NxtRequestInfo { fn request_read(&mut self, dst: &mut BytesMut) { unsafe { let rest = dst.spare_capacity_mut(); - let amt = bindings::nxt_unit_request_read( - self.info, - rest.as_mut_ptr().cast(), - rest.len(), - ); + let mut total_bytes_read = 0; + loop { + let amt = bindings::nxt_unit_request_read( + self.info, + rest.as_mut_ptr().wrapping_add(total_bytes_read).cast(), + 32 * 1024 * 1024, + ); + total_bytes_read += amt as usize; + if total_bytes_read >= rest.len() { + break; + } + } // TODO: handle failure when `amt` is negative - let amt: usize = amt.try_into().unwrap(); - dst.set_len(dst.len() + amt); + let total_bytes_read: usize = total_bytes_read.try_into().unwrap(); + dst.set_len(dst.len() + total_bytes_read); } } -- cgit From 98f808af2c23966e49abc7b2556e40adddbb51b9 Mon Sep 17 00:00:00 2001 From: Andrew Clayton Date: Tue, 30 Jan 2024 14:44:11 +0000 Subject: Wasm-wc: Upgrade to wasmtime 17 This brings WASI 0.2.0 support. Link: Signed-off-by: Andrew Clayton --- src/wasm-wasi-component/Cargo.toml | 12 ++++++------ src/wasm-wasi-component/src/lib.rs | 21 +++++++++++---------- 2 files changed, 17 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/wasm-wasi-component/Cargo.toml b/src/wasm-wasi-component/Cargo.toml index 8f42d128..feb7f53c 100644 --- a/src/wasm-wasi-component/Cargo.toml +++ b/src/wasm-wasi-component/Cargo.toml @@ -11,13 +11,13 @@ crate-type = ["cdylib"] anyhow = "1.0.75" bytes = "1.5.0" futures-util = { version = "0.3.29", default-features = false } -http = "0.2.9" -http-body = { version = "1.0.0-rc.2", default-features = false } -http-body-util = "0.1.0-rc.2" +http = "1.0.0" +http-body = { version = "1.0.0", default-features = false } +http-body-util = "0.1.0" tokio = { version = "1.33.0", default-features = false } -wasmtime = "14.0.2" -wasmtime-wasi = "14.0.2" -wasmtime-wasi-http = "14.0.2" +wasmtime = { version = "17.0.0", default-features = false, features = ['component-model', 'cranelift'] } +wasmtime-wasi = "17.0.0" +wasmtime-wasi-http = "17.0.0" [build-dependencies] bindgen = "0.68.1" diff --git a/src/wasm-wasi-component/src/lib.rs b/src/wasm-wasi-component/src/lib.rs index 032878f5..888074ab 100644 --- a/src/wasm-wasi-component/src/lib.rs +++ b/src/wasm-wasi-component/src/lib.rs @@ -7,12 +7,13 @@ use std::mem::MaybeUninit; use std::ptr; use std::sync::OnceLock; use tokio::sync::mpsc; -use wasmtime::component::{Component, InstancePre, Linker}; +use wasmtime::component::{Component, InstancePre, Linker, ResourceTable}; use wasmtime::{Config, Engine, Store}; use wasmtime_wasi::preview2::{ - DirPerms, FilePerms, Table, WasiCtx, WasiCtxBuilder, WasiView, + DirPerms, FilePerms, WasiCtx, WasiCtxBuilder, WasiView, }; use wasmtime_wasi::{ambient_authority, Dir}; +use wasmtime_wasi_http::bindings::http::types::ErrorCode; use wasmtime_wasi_http::{WasiHttpCtx, WasiHttpView}; #[allow( @@ -259,7 +260,7 @@ impl GlobalState { } cx.build() }, - table: Table::default(), + table: ResourceTable::default(), http: WasiHttpCtx, }; let mut store = Store::new(&self.engine, data); @@ -365,7 +366,7 @@ impl GlobalState { fn to_request_body( &self, info: &mut NxtRequestInfo, - ) -> BoxBody { + ) -> BoxBody { // TODO: should convert the body into a form of `Stream` to become an // async stream of frames. The return value can represent that here // but for now this slurps up the entire body into memory and puts it @@ -407,7 +408,7 @@ impl GlobalState { async fn send_response_body( &self, info: &mut NxtRequestInfo, - mut body: BoxBody, + mut body: BoxBody, ) -> Result<()> { loop { // Acquire the next frame, and because nothing is actually async @@ -415,7 +416,7 @@ impl GlobalState { // `Pending` case should not happen. let frame = match body.frame().await { Some(Ok(frame)) => frame, - Some(Err(e)) => break Err(e), + Some(Err(e)) => break Err(e.into()), None => break Ok(()), }; match frame.data_ref() { @@ -579,14 +580,14 @@ impl NxtRequestInfo { struct StoreState { ctx: WasiCtx, http: WasiHttpCtx, - table: Table, + table: ResourceTable, } impl WasiView for StoreState { - fn table(&self) -> &Table { + fn table(&self) -> &ResourceTable { &self.table } - fn table_mut(&mut self) -> &mut Table { + fn table_mut(&mut self) -> &mut ResourceTable { &mut self.table } fn ctx(&self) -> &WasiCtx { @@ -601,7 +602,7 @@ impl WasiHttpView for StoreState { fn ctx(&mut self) -> &mut WasiHttpCtx { &mut self.http } - fn table(&mut self) -> &mut Table { + fn table(&mut self) -> &mut ResourceTable { &mut self.table } } -- cgit From 60eb6c43a71fb0f6cf21759917e0247bdb88b892 Mon Sep 17 00:00:00 2001 From: Andrew Clayton Date: Tue, 13 Feb 2024 16:21:49 +0000 Subject: Wasm-wc: Allow to use the 'reactor' adaptor again With the initial port to wasmtime 17 we could no longer use the 'reactor' adaptor but had to switch to the more restrictive 'proxy' adaptor. This meant amongst other things (probably) we could no longer access the filesystem. Thanks to Joel Dice for pointing out the fix. With this we can go back to using the 'reactor' adaptor again and things are back to working as before. It's worth noting that you can use either the 'proxy' or 'reactor' adaptor depending on your requirements. Cc: Joel Dice Signed-off-by: Andrew Clayton --- src/wasm-wasi-component/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/wasm-wasi-component/src/lib.rs b/src/wasm-wasi-component/src/lib.rs index 888074ab..3ee40c4f 100644 --- a/src/wasm-wasi-component/src/lib.rs +++ b/src/wasm-wasi-component/src/lib.rs @@ -200,7 +200,8 @@ impl GlobalState { let component = Component::from_file(&engine, &global_config.component) .context("failed to compile component")?; let mut linker = Linker::::new(&engine); - wasmtime_wasi_http::proxy::add_to_linker(&mut linker)?; + wasmtime_wasi::preview2::command::add_to_linker(&mut linker)?; + wasmtime_wasi_http::proxy::add_only_http_to_linker(&mut linker)?; let component = linker .instantiate_pre(&component) .context("failed to pre-instantiate the provided component")?; -- cgit From 8d030139a1bde3ee640852d1348eb595cb376d05 Mon Sep 17 00:00:00 2001 From: Andrew Clayton Date: Tue, 20 Feb 2024 14:43:59 +0000 Subject: Wasm-wc: Add Cargo.lock It seems we do want to track this thing. This is just the latest version that cargo had generated for me. Cc: Dan Callahan Signed-off-by: Andrew Clayton --- src/wasm-wasi-component/.gitignore | 2 - src/wasm-wasi-component/Cargo.lock | 2293 ++++++++++++++++++++++++++++++++++++ 2 files changed, 2293 insertions(+), 2 deletions(-) create mode 100644 src/wasm-wasi-component/Cargo.lock (limited to 'src') diff --git a/src/wasm-wasi-component/.gitignore b/src/wasm-wasi-component/.gitignore index 159e3885..eb5a316c 100644 --- a/src/wasm-wasi-component/.gitignore +++ b/src/wasm-wasi-component/.gitignore @@ -1,3 +1 @@ -Cargo.lock - target diff --git a/src/wasm-wasi-component/Cargo.lock b/src/wasm-wasi-component/Cargo.lock new file mode 100644 index 00000000..bc09e96a --- /dev/null +++ b/src/wasm-wasi-component/Cargo.lock @@ -0,0 +1,2293 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "ambient-authority" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9d4ee0d472d1cd2e28c97dfa124b3d8d992e10eb0a035f33f5d12e3a177ba3b" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" + +[[package]] +name = "arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" + +[[package]] +name = "async-trait" +version = "0.1.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bindgen" +version = "0.68.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "726e4313eb6ec35d2730258ad4e15b547ee75d6afaa1361a922e78e59b7d8078" +dependencies = [ + "bitflags 2.4.2", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", + "which", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" + +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "cap-fs-ext" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88e341d15ac1029aadce600be764a1a1edafe40e03cde23285bc1d261b3a4866" +dependencies = [ + "cap-primitives", + "cap-std", + "io-lifetimes", + "windows-sys 0.52.0", +] + +[[package]] +name = "cap-net-ext" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "434168fe6533055f0f4204039abe3ff6d7db338ef46872a5fa39e9d5ad5ab7a9" +dependencies = [ + "cap-primitives", + "cap-std", + "rustix", + "smallvec", +] + +[[package]] +name = "cap-primitives" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe16767ed8eee6d3f1f00d6a7576b81c226ab917eb54b96e5f77a5216ef67abb" +dependencies = [ + "ambient-authority", + "fs-set-times", + "io-extras", + "io-lifetimes", + "ipnet", + "maybe-owned", + "rustix", + "windows-sys 0.52.0", + "winx", +] + +[[package]] +name = "cap-rand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20e5695565f0cd7106bc3c7170323597540e772bb73e0be2cd2c662a0f8fa4ca" +dependencies = [ + "ambient-authority", + "rand", +] + +[[package]] +name = "cap-std" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "593db20e4c51f62d3284bae7ee718849c3214f93a3b94ea1899ad85ba119d330" +dependencies = [ + "cap-primitives", + "io-extras", + "io-lifetimes", + "rustix", +] + +[[package]] +name = "cap-time-ext" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03261630f291f425430a36f38c847828265bc928f517cdd2004c56f4b02f002b" +dependencies = [ + "ambient-authority", + "cap-primitives", + "iana-time-zone", + "once_cell", + "rustix", + "winx", +] + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clang-sys" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "cranelift-bforest" +version = "0.104.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d819feeda4c420a18f1e28236ca0ce1177b22bf7c8a44ddee92dfe40de15bcf0" +dependencies = [ + "cranelift-entity", +] + +[[package]] +name = "cranelift-codegen" +version = "0.104.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9b8d03d5bdbca7e5f72b0e0a0f69933ed1f09e24be6c075aa6fe3f802b0cc0c" +dependencies = [ + "bumpalo", + "cranelift-bforest", + "cranelift-codegen-meta", + "cranelift-codegen-shared", + "cranelift-control", + "cranelift-entity", + "cranelift-isle", + "gimli", + "hashbrown 0.14.3", + "log", + "regalloc2", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-codegen-meta" +version = "0.104.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3fd3664e38e51649b17dc30cfdd561273fe2f590dcd013fb75d9eabc6272dfb" +dependencies = [ + "cranelift-codegen-shared", +] + +[[package]] +name = "cranelift-codegen-shared" +version = "0.104.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b031ec5e605828975952622b5a77d49126f20ffe88d33719a0af66b23a0fc36" + +[[package]] +name = "cranelift-control" +version = "0.104.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fada054d017cf2ed8f7ed2336e0517fc1b19e6825be1790de9eb00c94788362b" +dependencies = [ + "arbitrary", +] + +[[package]] +name = "cranelift-entity" +version = "0.104.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "177b6f94ae8de6348eb45bf977c79ab9e3c40fc3ac8cb7ed8109560ea39bee7d" +dependencies = [ + "serde", + "serde_derive", +] + +[[package]] +name = "cranelift-frontend" +version = "0.104.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebebd23a69a23e3ddea78e98ff3a2de222e88c8e045d81ef4a72f042e0d79dbd" +dependencies = [ + "cranelift-codegen", + "log", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-isle" +version = "0.104.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1571bfc14df8966d12c6121b5325026591a4b4009e22fea0fe3765ab7cd33b96" + +[[package]] +name = "cranelift-native" +version = "0.104.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35a69c37e0c10b46fe5527f2397ac821046efbf5f7ec112c8b84df25712f465b" +dependencies = [ + "cranelift-codegen", + "libc", + "target-lexicon", +] + +[[package]] +name = "cranelift-wasm" +version = "0.104.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b3fef8bbceb8cb56d3f1778b0418d75c5cf12ec571a35fc01eb41abb0227a25" +dependencies = [ + "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", + "itertools", + "log", + "smallvec", + "wasmparser 0.118.1", + "wasmtime-types", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "dirs" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "encoding_rs" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fd-lock" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e5768da2206272c81ef0b5e951a41862938a6070da63bcea197899942d3b947" +dependencies = [ + "cfg-if", + "rustix", + "windows-sys 0.52.0", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fs-set-times" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "033b337d725b97690d86893f9de22b67b80dcc4e9ad815f348254c38119db8fb" +dependencies = [ + "io-lifetimes", + "rustix", + "windows-sys 0.52.0", +] + +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-core", + "futures-sink", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[package]] +name = "getrandom" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +dependencies = [ + "fallible-iterator", + "indexmap", + "stable_deref_trait", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "h2" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31d030e59af851932b72ceebadf4a2b5986dba4c3b99dd2493f8273a0f151943" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +dependencies = [ + "ahash", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0c62115964e08cb8039170eb33c1d0e2388a256930279edca206fff675f82c3" + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "http" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b32afd38673a8016f7c9ae69e5af41a58f81b1d31689040f2f1959594ce194ea" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cb79eb393015dadd30fc252023adb0b2400a0caee0fa2a077e6e21a551e840" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5aa53871fc917b1a9ed87b683a5d86db645e23acb32c2e0785a353e522fb75" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "tokio", + "want", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "id-arena" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824b2ae422412366ba479e8111fd301f7b5faece8149317bb81925979a53f520" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", + "serde", +] + +[[package]] +name = "io-extras" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c301e73fb90e8a29e600a9f402d095765f74310d582916a952f618836a1bd1ed" +dependencies = [ + "io-lifetimes", + "windows-sys 0.52.0", +] + +[[package]] +name = "io-lifetimes" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a611371471e98973dbcab4e0ec66c31a10bc356eeb4d54a0e05eac8158fe38c" + +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "js-sys" +version = "0.3.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "leb128" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "libloading" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "libredox" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +dependencies = [ + "bitflags 2.4.2", + "libc", + "redox_syscall", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "mach" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +dependencies = [ + "libc", +] + +[[package]] +name = "maybe-owned" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4facc753ae494aeb6e3c22f839b158aebd4f9270f55cd3c79906c45476c47ab4" + +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "memfd" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2cffa4ad52c6f791f4f8b15f0c05f9824b2ced1160e88cc393d64fff9a8ac64" +dependencies = [ + "rustix", +] + +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "crc32fast", + "hashbrown 0.14.3", + "indexmap", + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "prettyplease" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "psm" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" +dependencies = [ + "cc", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_users" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + +[[package]] +name = "regalloc2" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad156d539c879b7a24a363a2016d77961786e71f48f2e2fc8302a92abd2429a6" +dependencies = [ + "hashbrown 0.13.2", + "log", + "rustc-hash", + "slice-group-by", + "smallvec", +] + +[[package]] +name = "regex" +version = "1.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "ring" +version = "0.17.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" +dependencies = [ + "cc", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustix" +version = "0.38.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" +dependencies = [ + "bitflags 2.4.2", + "errno", + "itoa", + "libc", + "linux-raw-sys", + "once_cell", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls" +version = "0.21.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" +dependencies = [ + "log", + "ring", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "ryu" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" + +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "semver" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" + +[[package]] +name = "serde" +version = "1.0.196" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.196" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.113" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "shellexpand" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ccc8076840c4da029af4f87e4e8daeb0fca6b87bbb02e10cb60b791450e11e4" +dependencies = [ + "dirs", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "slice-group-by" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" + +[[package]] +name = "smallvec" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" + +[[package]] +name = "socket2" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "sptr" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "syn" +version = "2.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "system-interface" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0682e006dd35771e392a6623ac180999a9a854b1d4a6c12fb2e804941c2b1f58" +dependencies = [ + "bitflags 2.4.2", + "cap-fs-ext", + "cap-std", + "fd-lock", + "io-lifetimes", + "rustix", + "windows-sys 0.52.0", + "winx", +] + +[[package]] +name = "target-lexicon" +version = "0.12.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69758bda2e78f098e4ccb393021a0963bb3442eac05f135c30f61b7370bbafae" + +[[package]] +name = "thiserror" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "pin-project-lite", + "socket2", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasi-cap-std-sync" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db014d2ced91f17d1f1a8f2b76d6ea8d731bc1dbc8c2bbaec689d6a242568e5d" +dependencies = [ + "anyhow", + "async-trait", + "cap-fs-ext", + "cap-rand", + "cap-std", + "cap-time-ext", + "fs-set-times", + "io-extras", + "io-lifetimes", + "once_cell", + "rustix", + "system-interface", + "tracing", + "wasi-common", + "windows-sys 0.52.0", +] + +[[package]] +name = "wasi-common" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "449d17849e3c83a931374442fe2deee4d6bd1ebf469719ef44192e9e82e19c89" +dependencies = [ + "anyhow", + "bitflags 2.4.2", + "cap-rand", + "cap-std", + "io-extras", + "log", + "rustix", + "thiserror", + "tracing", + "wasmtime", + "wiggle", + "windows-sys 0.52.0", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" + +[[package]] +name = "wasm-encoder" +version = "0.38.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ad2b51884de9c7f4fe2fd1043fccb8dcad4b1e29558146ee57a144d15779f3f" +dependencies = [ + "leb128", +] + +[[package]] +name = "wasm-wasi-component" +version = "0.1.0" +dependencies = [ + "anyhow", + "bindgen", + "bytes", + "cc", + "futures-util", + "http", + "http-body", + "http-body-util", + "tokio", + "wasmtime", + "wasmtime-wasi", + "wasmtime-wasi-http", +] + +[[package]] +name = "wasmparser" +version = "0.118.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95ee9723b928e735d53000dec9eae7b07a60e490c85ab54abb66659fc61bfcd9" +dependencies = [ + "indexmap", + "semver", +] + +[[package]] +name = "wasmparser" +version = "0.121.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953cf6a7606ab31382cb1caa5ae403e77ba70c7f8e12eeda167e7040d42bfda8" +dependencies = [ + "bitflags 2.4.2", + "indexmap", + "semver", +] + +[[package]] +name = "wasmprinter" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05e32c13c59fdc64d3f6998a1d52eb1d362b6904a88b754190ccb85661ad577a" +dependencies = [ + "anyhow", + "wasmparser 0.121.0", +] + +[[package]] +name = "wasmtime" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "910fabce77e660f0e0e41cfd5f69fc8bf020a025f059718846e918db7177f469" +dependencies = [ + "anyhow", + "async-trait", + "bincode", + "bumpalo", + "cfg-if", + "encoding_rs", + "indexmap", + "libc", + "log", + "object", + "once_cell", + "paste", + "serde", + "serde_derive", + "serde_json", + "target-lexicon", + "wasmparser 0.118.1", + "wasmtime-component-macro", + "wasmtime-component-util", + "wasmtime-cranelift", + "wasmtime-environ", + "wasmtime-fiber", + "wasmtime-jit", + "wasmtime-runtime", + "wasmtime-winch", + "windows-sys 0.52.0", +] + +[[package]] +name = "wasmtime-asm-macros" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37288142e9b4a61655a3bcbdc7316c2e4bb9e776b10ce3dd758f8186b4469572" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "wasmtime-component-macro" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad63de18eb42e586386b6091f787c82707cbd5ac5e9343216dba1976190cd03a" +dependencies = [ + "anyhow", + "proc-macro2", + "quote", + "syn", + "wasmtime-component-util", + "wasmtime-wit-bindgen", + "wit-parser", +] + +[[package]] +name = "wasmtime-component-util" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e0a160c0c44369aa4bee6d311a8e4366943bab1651040cc8b0fcec2c9eb8906" + +[[package]] +name = "wasmtime-cranelift" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3734cc01b7cd37bc62fdbcd9529ca9547440052d4b3886cfdec3b8081a5d3647" +dependencies = [ + "anyhow", + "cfg-if", + "cranelift-codegen", + "cranelift-control", + "cranelift-entity", + "cranelift-frontend", + "cranelift-native", + "cranelift-wasm", + "gimli", + "log", + "object", + "target-lexicon", + "thiserror", + "wasmparser 0.118.1", + "wasmtime-cranelift-shared", + "wasmtime-environ", + "wasmtime-versioned-export-macros", +] + +[[package]] +name = "wasmtime-cranelift-shared" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0eb33cd30c47844aa228d4d0030587e65c1108343f311fe9f7248b5bd9cb65c" +dependencies = [ + "anyhow", + "cranelift-codegen", + "cranelift-control", + "cranelift-native", + "gimli", + "object", + "target-lexicon", + "wasmtime-environ", +] + +[[package]] +name = "wasmtime-environ" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a056b041fdea604f0972e2fae97958e7748d629a55180228348baefdfc217" +dependencies = [ + "anyhow", + "cranelift-entity", + "gimli", + "indexmap", + "log", + "object", + "serde", + "serde_derive", + "target-lexicon", + "thiserror", + "wasm-encoder", + "wasmparser 0.118.1", + "wasmprinter", + "wasmtime-component-util", + "wasmtime-types", +] + +[[package]] +name = "wasmtime-fiber" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43987d0977c07f15c3608c2f255870c127ffd19e35eeedb1ac1dccedf9932a42" +dependencies = [ + "anyhow", + "cc", + "cfg-if", + "rustix", + "wasmtime-asm-macros", + "wasmtime-versioned-export-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "wasmtime-jit" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b3e48395ac672b386ed588d97a9612aa13a345008f26466f0dfb2a91628aa9f" +dependencies = [ + "anyhow", + "bincode", + "cfg-if", + "gimli", + "log", + "object", + "rustix", + "serde", + "serde_derive", + "target-lexicon", + "wasmtime-environ", + "wasmtime-jit-icache-coherence", + "wasmtime-runtime", + "windows-sys 0.52.0", +] + +[[package]] +name = "wasmtime-jit-icache-coherence" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdc26415bb89e9ccd3bdc498fef63aabf665c4c0dd710c107691deb9694955da" +dependencies = [ + "cfg-if", + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "wasmtime-runtime" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0abddaf17912aabaf39be0802d5eba9a002e956e902d1ebd438a2fe1c88769a2" +dependencies = [ + "anyhow", + "cc", + "cfg-if", + "encoding_rs", + "indexmap", + "libc", + "log", + "mach", + "memfd", + "memoffset", + "paste", + "psm", + "rustix", + "sptr", + "wasm-encoder", + "wasmtime-asm-macros", + "wasmtime-environ", + "wasmtime-fiber", + "wasmtime-versioned-export-macros", + "wasmtime-wmemcheck", + "windows-sys 0.52.0", +] + +[[package]] +name = "wasmtime-types" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b35a95cdc1433729085beab42c0a5c742b431f25b17c40d7718e46df63d5ffc7" +dependencies = [ + "cranelift-entity", + "serde", + "serde_derive", + "thiserror", + "wasmparser 0.118.1", +] + +[[package]] +name = "wasmtime-versioned-export-macros" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fad322733fe67e45743784d8b1df452bcb54f581572a4f1a646a4332deecbcc2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "wasmtime-wasi" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "902cc299b73655c36679b77efdfce4bb5971992f1a4a8a436dd3809a6848ff0e" +dependencies = [ + "anyhow", + "async-trait", + "bitflags 2.4.2", + "bytes", + "cap-fs-ext", + "cap-net-ext", + "cap-rand", + "cap-std", + "cap-time-ext", + "fs-set-times", + "futures", + "io-extras", + "io-lifetimes", + "libc", + "log", + "once_cell", + "rustix", + "system-interface", + "thiserror", + "tokio", + "tracing", + "url", + "wasi-cap-std-sync", + "wasi-common", + "wasmtime", + "wiggle", + "windows-sys 0.52.0", +] + +[[package]] +name = "wasmtime-wasi-http" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "151fc711fad35034b8a6df00a5bcd5a7b1acb89ca12c2407f564a36ebd382e26" +dependencies = [ + "anyhow", + "async-trait", + "bytes", + "futures", + "http", + "http-body", + "http-body-util", + "hyper", + "rustls", + "tokio", + "tokio-rustls", + "tracing", + "wasmtime", + "wasmtime-wasi", + "webpki-roots", +] + +[[package]] +name = "wasmtime-winch" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e63aeca929f84560eec52c5af43bf5d623b92683b0195d9fb06da8ed860e092" +dependencies = [ + "anyhow", + "cranelift-codegen", + "gimli", + "object", + "target-lexicon", + "wasmparser 0.118.1", + "wasmtime-cranelift-shared", + "wasmtime-environ", + "winch-codegen", +] + +[[package]] +name = "wasmtime-wit-bindgen" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41e5675998fdc74495afdd90ad2bd221206a258075b23048af0535a969b07893" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "wit-parser", +] + +[[package]] +name = "wasmtime-wmemcheck" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b20a19e10d8cb50b45412fb21192982b7ce85c0122dc33bb71f1813e25dc6e52" + +[[package]] +name = "wast" +version = "35.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ef140f1b49946586078353a453a1d28ba90adfc54dde75710bc1931de204d68" +dependencies = [ + "leb128", +] + +[[package]] +name = "webpki-roots" +version = "0.25.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + +[[package]] +name = "wiggle" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "737728db69a7657a5f6a7bac445c02d8564d603d62c46c95edf928554e67d072" +dependencies = [ + "anyhow", + "async-trait", + "bitflags 2.4.2", + "thiserror", + "tracing", + "wasmtime", + "wiggle-macro", +] + +[[package]] +name = "wiggle-generate" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2460c7163b79ffefd9a564eaeab0a5b0e84bb91afdfeeb84d36f304ddbe08982" +dependencies = [ + "anyhow", + "heck", + "proc-macro2", + "quote", + "shellexpand", + "syn", + "witx", +] + +[[package]] +name = "wiggle-macro" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa8d8412375ba8325d61fbae56dead51dabfaec85d620ce36427922fb9cece83" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wiggle-generate", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "winch-codegen" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d2b346bad5397b219b4ff0a8fa7230936061ff07c61f05d589d8d81e06fb7b2" +dependencies = [ + "anyhow", + "cranelift-codegen", + "gimli", + "regalloc2", + "smallvec", + "target-lexicon", + "wasmparser 0.118.1", + "wasmtime-environ", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + +[[package]] +name = "winx" +version = "0.36.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9643b83820c0cd246ecabe5fa454dd04ba4fa67996369466d0747472d337346" +dependencies = [ + "bitflags 2.4.2", + "windows-sys 0.52.0", +] + +[[package]] +name = "wit-parser" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df4913a2219096373fd6512adead1fb77ecdaa59d7fc517972a7d30b12f625be" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", +] + +[[package]] +name = "witx" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e366f27a5cabcddb2706a78296a40b8fcc451e1a6aba2fc1d94b4a01bdaaef4b" +dependencies = [ + "anyhow", + "log", + "thiserror", + "wast", +] + +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] -- cgit From 07a0c9a34817d6faedff67505507cd4f54752a22 Mon Sep 17 00:00:00 2001 From: Andrew Clayton Date: Mon, 5 Feb 2024 21:50:52 +0000 Subject: Wasm-wc: Wire up the language module to the config system This exposes the various WebAssembly Component Model language module specific options. The application type is "wasm-wasi-component". There is a "component" option that is required, this specifies the full path to the WebAssembly component to be run. This component should be in binary format, i.e a .wasm file. There is also currently one optional option "access" Due to the sandboxed nature of WebAssembly, by default Wasm modules/components don't have any access to the underlying filesystem. There is however a capabilities based mechanism[0] for allowing such access. This adds a config option to the 'wasm-wasi-component' application type (same as for 'wasm'); 'access.filesystem' which takes an array of directory paths that are then made available to the wasm module/component. This access works recursively, i.e everything under a specific path is allowed access to. Example config might look like "applications": { "my-wasm-component": { "type": "wasm-wasi-component", "component": "/path/to/component.wasm", "access" { "filesystem": [ "/tmp", "/var/tmp" ] } } } The actual mechanism used allows directories to be mapped differently in the guest. But at the moment we don't support that and just map say /tmp to /tmp. This can be revisited if it's something users clamour for. [0]: Signed-off-by: Andrew Clayton --- src/nxt_application.c | 3 +++ src/nxt_conf_validation.c | 17 +++++++++++++++++ src/nxt_main_process.c | 15 +++++++++++++++ 3 files changed, 35 insertions(+) (limited to 'src') diff --git a/src/nxt_application.c b/src/nxt_application.c index 872e387a..e0247bf0 100644 --- a/src/nxt_application.c +++ b/src/nxt_application.c @@ -1100,6 +1100,9 @@ nxt_app_parse_type(u_char *p, size_t length) } else if (nxt_str_eq(&str, "java", 4)) { return NXT_APP_JAVA; + } else if (nxt_str_eq(&str, "wasm-wasi-component", 19)) { + return NXT_APP_WASM_WC; + } else if (nxt_str_eq(&str, "wasm", 4)) { return NXT_APP_WASM; } diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index caa068d2..2099f887 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -1095,6 +1095,22 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_wasm_members[] = { }; +static nxt_conf_vldt_object_t nxt_conf_vldt_wasm_wc_members[] = { + { + .name = nxt_string("component"), + .type = NXT_CONF_VLDT_STRING, + .flags = NXT_CONF_VLDT_REQUIRED, + }, { + .name = nxt_string("access"), + .type = NXT_CONF_VLDT_OBJECT, + .validator = nxt_conf_vldt_object, + .u.members = nxt_conf_vldt_wasm_access_members, + }, + + NXT_CONF_VLDT_NEXT(nxt_conf_vldt_common_members) +}; + + static nxt_conf_vldt_object_t nxt_conf_vldt_wasm_access_members[] = { { .name = nxt_string("filesystem"), @@ -2660,6 +2676,7 @@ nxt_conf_vldt_app(nxt_conf_validation_t *vldt, nxt_str_t *name, { nxt_conf_vldt_object, nxt_conf_vldt_ruby_members }, { nxt_conf_vldt_object, nxt_conf_vldt_java_members }, { nxt_conf_vldt_object, nxt_conf_vldt_wasm_members }, + { nxt_conf_vldt_object, nxt_conf_vldt_wasm_wc_members }, }; ret = nxt_conf_vldt_type(vldt, name, value, NXT_CONF_VLDT_OBJECT); diff --git a/src/nxt_main_process.c b/src/nxt_main_process.c index 3f317d5e..060ead41 100644 --- a/src/nxt_main_process.c +++ b/src/nxt_main_process.c @@ -377,6 +377,20 @@ static nxt_conf_map_t nxt_wasm_app_conf[] = { }; +static nxt_conf_map_t nxt_wasm_wc_app_conf[] = { + { + nxt_string("component"), + NXT_CONF_MAP_CSTRZ, + offsetof(nxt_common_app_conf_t, u.wasm_wc.component), + }, + { + nxt_string("access"), + NXT_CONF_MAP_PTR, + offsetof(nxt_common_app_conf_t, u.wasm_wc.access), + }, +}; + + static nxt_conf_app_map_t nxt_app_maps[] = { { nxt_nitems(nxt_external_app_conf), nxt_external_app_conf }, { nxt_nitems(nxt_python_app_conf), nxt_python_app_conf }, @@ -385,6 +399,7 @@ static nxt_conf_app_map_t nxt_app_maps[] = { { nxt_nitems(nxt_ruby_app_conf), nxt_ruby_app_conf }, { nxt_nitems(nxt_java_app_conf), nxt_java_app_conf }, { nxt_nitems(nxt_wasm_app_conf), nxt_wasm_app_conf }, + { nxt_nitems(nxt_wasm_wc_app_conf), nxt_wasm_wc_app_conf }, }; -- cgit