From 254cd3b068766f5d3cbe20b8f3a6fa26c68ce7a0 Mon Sep 17 00:00:00 2001 From: Caio Ramos Casimiro Date: Fri, 4 Aug 2023 11:53:51 +0100 Subject: [PATCH] feat(*) wasm_socket unix domain socket support --- src/common/ngx_wasm_socket_tcp.c | 55 ++++++- src/common/ngx_wasm_socket_tcp.h | 3 +- .../proxy_wasm/ngx_http_proxy_wasm_dispatch.c | 24 +-- .../hfuncs/130-proxy_dispatch_http.t | 142 ++++++++++++++---- .../hostcalls/src/types/test_http.rs | 24 ++- 5 files changed, 180 insertions(+), 68 deletions(-) diff --git a/src/common/ngx_wasm_socket_tcp.c b/src/common/ngx_wasm_socket_tcp.c index 77449d9e0..30c62838c 100644 --- a/src/common/ngx_wasm_socket_tcp.c +++ b/src/common/ngx_wasm_socket_tcp.c @@ -119,9 +119,11 @@ ngx_wasm_socket_tcp_resume(ngx_wasm_socket_tcp_t *sock) ngx_int_t ngx_wasm_socket_tcp_init(ngx_wasm_socket_tcp_t *sock, - ngx_str_t *host, in_port_t port, unsigned tls, - ngx_wasm_subsys_env_t *env) + ngx_str_t *host, unsigned tls, ngx_wasm_subsys_env_t *env) { + u_char *p, *last; + static ngx_str_t uds_prefix = ngx_string("unix:"); + ngx_memzero(sock, sizeof(ngx_wasm_socket_tcp_t)); ngx_memcpy(&sock->env, env, sizeof(ngx_wasm_subsys_env_t)); @@ -165,7 +167,40 @@ ngx_wasm_socket_tcp_init(ngx_wasm_socket_tcp_t *sock, sock->url.default_port = 80; #endif sock->url.url = sock->host; - sock->url.port = port; + sock->url.port = 0; + + if (host->len > uds_prefix.len + && ngx_memcmp(host->data, uds_prefix.data, uds_prefix.len) == 0) + { + +#if (!NGX_HAVE_UNIX_DOMAIN) + ngx_wasm_log_error(NGX_LOG_ERR, sock->log, 0, + "host \"%V\" requires unix domain socket support", + host); + return NGX_ERROR; +#endif + + } else { + /* extract port */ + + p = host->data; + last = p + host->len; + + if (*p == '[') { + /* IPv6 */ + p = ngx_strlchr(p, last, ']'); + if (p == NULL) { + p = host->data; + } + } + + p = ngx_strlchr(p, last, ':'); + + if (p) { + sock->url.port = ngx_atoi(p + 1, last - p); + } + } + sock->url.no_resolve = 1; if (ngx_parse_url(sock->pool, &sock->url) != NGX_OK) { @@ -208,6 +243,8 @@ ngx_wasm_socket_tcp_connect(ngx_wasm_socket_tcp_t *sock) ngx_stream_session_t *s; #endif + dd("enter"); + if (sock->errlen) { return NGX_ERROR; } @@ -284,7 +321,15 @@ ngx_wasm_socket_tcp_connect(ngx_wasm_socket_tcp_t *sock) "wasm tcp socket no resolving: %V", &sock->resolved.host); - return ngx_wasm_socket_tcp_connect_peer(sock); + rc = ngx_wasm_socket_tcp_connect_peer(sock); + if (sock->connected) { +#if (NGX_SSL) + if (sock->ssl_conf) { + return ngx_wasm_socket_tcp_ssl_handshake(sock); + } +#endif + } + return rc; } ngx_log_debug1(NGX_LOG_DEBUG_WASM, sock->log, 0, @@ -1484,7 +1529,7 @@ ngx_wasm_socket_tcp_init_addr_text(ngx_peer_connection_t *pc) break; #endif -#if (NGX_HAVE_UNIX_DOMAIN && 0) +#if (NGX_HAVE_UNIX_DOMAIN) case AF_UNIX: addr_text_max_len = NGX_UNIX_ADDRSTRLEN; break; diff --git a/src/common/ngx_wasm_socket_tcp.h b/src/common/ngx_wasm_socket_tcp.h index 4b4689c3f..bd9f0fcbe 100644 --- a/src/common/ngx_wasm_socket_tcp.h +++ b/src/common/ngx_wasm_socket_tcp.h @@ -94,8 +94,7 @@ struct ngx_wasm_socket_tcp_s { ngx_int_t ngx_wasm_socket_tcp_init(ngx_wasm_socket_tcp_t *sock, - ngx_str_t *host, in_port_t port, unsigned tls, - ngx_wasm_subsys_env_t *env); + ngx_str_t *host, unsigned tls, ngx_wasm_subsys_env_t *env); ngx_int_t ngx_wasm_socket_tcp_connect(ngx_wasm_socket_tcp_t *sock); ngx_int_t ngx_wasm_socket_tcp_send(ngx_wasm_socket_tcp_t *sock, ngx_chain_t *cl); diff --git a/src/http/proxy_wasm/ngx_http_proxy_wasm_dispatch.c b/src/http/proxy_wasm/ngx_http_proxy_wasm_dispatch.c index 97d9d3100..85b951ea0 100644 --- a/src/http/proxy_wasm/ngx_http_proxy_wasm_dispatch.c +++ b/src/http/proxy_wasm/ngx_http_proxy_wasm_dispatch.c @@ -136,8 +136,6 @@ ngx_http_proxy_wasm_dispatch(ngx_proxy_wasm_exec_t *pwexec, { static uint32_t callout_ids = 0; size_t i; - in_port_t port = 0; - u_char *p, *last; ngx_buf_t *buf; ngx_event_t *ev; ngx_table_elt_t *elts, *elt; @@ -227,25 +225,6 @@ ngx_http_proxy_wasm_dispatch(ngx_proxy_wasm_exec_t *pwexec, ngx_memcpy(call->host.data, host->data, host->len); call->host.data[call->host.len] = '\0'; - /* port */ - - p = host->data; - last = p + host->len; - - if (*p == '[') { - /* IPv6 */ - p = ngx_strlchr(p, last, ']'); - if (p == NULL) { - p = host->data; - } - } - - p = ngx_strlchr(p, last, ':'); - - if (p) { - port = ngx_atoi(p + 1, last - p); - } - /* headers/trailers */ if (ngx_proxy_wasm_pairs_unmarshal(pwexec, &call->headers, headers) @@ -354,8 +333,7 @@ ngx_http_proxy_wasm_dispatch(ngx_proxy_wasm_exec_t *pwexec, ngx_wasm_assert(rctx); - if (ngx_wasm_socket_tcp_init(sock, &call->host, port, enable_ssl, - &rctx->env) + if (ngx_wasm_socket_tcp_init(sock, &call->host, enable_ssl, &rctx->env) != NGX_OK) { dd("tcp init error"); diff --git a/t/03-proxy_wasm/hfuncs/130-proxy_dispatch_http.t b/t/03-proxy_wasm/hfuncs/130-proxy_dispatch_http.t index f9a4f7ec1..bfa7ef8d9 100644 --- a/t/03-proxy_wasm/hfuncs/130-proxy_dispatch_http.t +++ b/t/03-proxy_wasm/hfuncs/130-proxy_dispatch_http.t @@ -184,7 +184,53 @@ qr/(\[error\]|Uncaught RuntimeError|\s+).*?dispatch failed: no resolver defined -=== TEST 11: proxy_wasm - dispatch_http_call() sanity (default resolver) +=== TEST 11: proxy_wasm - dispatch_http_call() unix domain socket, missing socket file +--- wasm_modules: hostcalls +--- config eval +qq{ + location /t { + proxy_wasm hostcalls 'test=/t/dispatch_http_call \ + host=unix:/tmp/inexistent_file.sock'; + } +} +--- error_code: 500 +--- response_body_like: 500 Internal Server Error +--- error_log eval +qr/\[crit\] .*? connect\(\) to unix\:\/tmp\/inexistent_file\.sock failed .*? No such file or directory/ +--- no_error_log +stub + + + +=== TEST 12: proxy_wasm - dispatch_http_call() unix domain socket, missing :authority +--- load_nginx_modules: ngx_http_echo_module +--- wasm_modules: hostcalls +--- http_config +server { + listen unix:/tmp/nginx.sock; + + location /headers { + echo $echo_client_request_headers; + } +} +--- config eval +qq{ + location /t { + proxy_wasm hostcalls 'test=/t/dispatch_http_call \ + host=unix:/tmp/nginx.sock \ + no_authority=true'; + } +} +--- error_code: 500 +--- response_body_like: 500 Internal Server Error +--- error_log eval +qr/(\[error\]|Uncaught RuntimeError|\s+).*?dispatch failed: no :authority/ +--- no_error_log +stub + + + +=== TEST 13: proxy_wasm - dispatch_http_call() sanity (default resolver) --- timeout eval: $::ExtTimeout --- load_nginx_modules: ngx_http_echo_module --- main_config eval @@ -208,7 +254,7 @@ ok -=== TEST 12: proxy_wasm - dispatch_http_call() on_request_body, no output +=== TEST 14: proxy_wasm - dispatch_http_call() on_request_body, no output --- load_nginx_modules: ngx_http_echo_module --- wasm_modules: hostcalls --- config @@ -235,7 +281,7 @@ ok -=== TEST 13: proxy_wasm - dispatch_http_call() on_request_body, with output +=== TEST 15: proxy_wasm - dispatch_http_call() on_request_body, with output --- load_nginx_modules: ngx_http_echo_module --- wasm_modules: hostcalls --- config @@ -263,7 +309,41 @@ Hello back -=== TEST 14: proxy_wasm - dispatch_http_call() resolver + hostname (IPv4) +=== TEST 16: proxy_wasm - dispatch_http_call() unix domain socket +--- load_nginx_modules: ngx_http_echo_module +--- wasm_modules: hostcalls +--- http_config +server { + listen unix:/tmp/nginx.sock; + + location /headers { + echo $echo_client_request_headers; + } +} +--- config eval +qq{ + location /t { + proxy_wasm hostcalls 'test=/t/dispatch_http_call \ + host=unix:/tmp/nginx.sock \ + authority=a_localhost \ + headers=X-Thing:foo|X-Thing:bar|Hello:world + path=/headers \ + on_http_call_response=echo_response_body'; + echo fail; + } +} +--- response_body_like +.*? +\s*X-Thing: foo\s* +\s*X-Thing: bar\s* +\s*Hello: world\s* +--- no_error_log +[error] +[crit] + + + +=== TEST 17: proxy_wasm - dispatch_http_call() resolver + hostname (IPv4) Needs IPv4 resolution + external I/O to succeed. Succeeds on: - HTTP 200 (httpbin.org/headers success) @@ -297,7 +377,7 @@ qq{ -=== TEST 15: proxy_wasm - dispatch_http_call() resolver + hostname (IPv6), default port +=== TEST 18: proxy_wasm - dispatch_http_call() resolver + hostname (IPv6), default port Disabled on GitHub Actions due to IPv6 constraint. --- skip_eval: 4: system("ping6 -c 1 ::1 >/dev/null 2>&1") ne 0 || defined $ENV{GITHUB_ACTIONS} --- timeout eval: $::ExtTimeout @@ -324,7 +404,7 @@ qq{ -=== TEST 16: proxy_wasm - dispatch_http_call() resolver + hostname (IPv4) + port +=== TEST 19: proxy_wasm - dispatch_http_call() resolver + hostname (IPv4) + port --- load_nginx_modules: ngx_http_echo_module --- wasm_modules: hostcalls --- http_config @@ -361,7 +441,7 @@ qq{ -=== TEST 17: proxy_wasm - dispatch_http_call() resolver error (host not found) +=== TEST 20: proxy_wasm - dispatch_http_call() resolver error (host not found) --- timeout eval: $::ExtTimeout --- load_nginx_modules: ngx_http_echo_module --- wasm_modules: hostcalls @@ -385,7 +465,7 @@ qr/(\[error\]|Uncaught RuntimeError|\s+).*?dispatch failed: tcp socket - resolve -=== TEST 18: proxy_wasm - dispatch_http_call() IP + port (IPv4) +=== TEST 21: proxy_wasm - dispatch_http_call() IP + port (IPv4) --- load_nginx_modules: ngx_http_echo_module --- wasm_modules: hostcalls --- config @@ -408,7 +488,7 @@ ok -=== TEST 19: proxy_wasm - dispatch_http_call() IP + port (IPv6) +=== TEST 22: proxy_wasm - dispatch_http_call() IP + port (IPv6) --- load_nginx_modules: ngx_http_echo_module --- wasm_modules: hostcalls --- config @@ -433,7 +513,7 @@ ok -=== TEST 20: proxy_wasm - dispatch_http_call() many request headers (> 10) +=== TEST 23: proxy_wasm - dispatch_http_call() many request headers (> 10) --- load_nginx_modules: ngx_http_echo_module --- wasm_modules: hostcalls --- config @@ -471,7 +551,7 @@ K: 11.* -=== TEST 21: proxy_wasm - dispatch_http_call() empty response body (HTTP 204) +=== TEST 24: proxy_wasm - dispatch_http_call() empty response body (HTTP 204) --- skip_no_debug: 4 --- load_nginx_modules: ngx_http_echo_module --- wasm_modules: hostcalls @@ -500,7 +580,7 @@ tcp socket closing -=== TEST 22: proxy_wasm - dispatch_http_call() empty response body (Content-Length: 0) +=== TEST 25: proxy_wasm - dispatch_http_call() empty response body (Content-Length: 0) --- skip_no_debug: 4 --- load_nginx_modules: ngx_http_echo_module ngx_http_headers_more_filter_module --- wasm_modules: hostcalls @@ -528,7 +608,7 @@ tcp socket closing -=== TEST 23: proxy_wasm - dispatch_http_call() no response body (HEAD) +=== TEST 26: proxy_wasm - dispatch_http_call() no response body (HEAD) --- skip_no_debug: 4 --- load_nginx_modules: ngx_http_echo_module --- wasm_modules: hostcalls @@ -558,7 +638,7 @@ tcp socket closing -=== TEST 24: proxy_wasm - dispatch_http_call() "Content-Length" response body +=== TEST 27: proxy_wasm - dispatch_http_call() "Content-Length" response body --- load_nginx_modules: ngx_http_echo_module --- wasm_modules: hostcalls --- config @@ -581,7 +661,7 @@ Hello world -=== TEST 25: proxy_wasm - dispatch_http_call() "Transfer-Encoding: chunked" response body +=== TEST 28: proxy_wasm - dispatch_http_call() "Transfer-Encoding: chunked" response body --- load_nginx_modules: ngx_http_echo_module --- wasm_modules: hostcalls --- config @@ -606,7 +686,7 @@ Content-Length: 0.* -=== TEST 26: proxy_wasm - dispatch_http_call() large response +=== TEST 29: proxy_wasm - dispatch_http_call() large response --- load_nginx_modules: ngx_http_echo_module --- wasm_modules: hostcalls --- config @@ -632,7 +712,7 @@ Content-Length: 0.* -=== TEST 27: proxy_wasm - dispatch_http_call() small wasm_socket_buffer_size +=== TEST 30: proxy_wasm - dispatch_http_call() small wasm_socket_buffer_size --- skip_no_debug: 4 --- load_nginx_modules: ngx_http_echo_module --- wasm_modules: hostcalls @@ -660,7 +740,7 @@ tcp socket trying to receive data (max: 1) -=== TEST 28: proxy_wasm - dispatch_http_call() small wasm_socket_large_buffers +=== TEST 31: proxy_wasm - dispatch_http_call() small wasm_socket_large_buffers --- load_nginx_modules: ngx_http_echo_module --- wasm_modules: hostcalls --- config @@ -687,7 +767,7 @@ Hello world -=== TEST 29: proxy_wasm - dispatch_http_call() too small wasm_socket_large_buffers +=== TEST 32: proxy_wasm - dispatch_http_call() too small wasm_socket_large_buffers --- skip_no_debug: 4 --- load_nginx_modules: ngx_http_echo_module --- wasm_modules: hostcalls @@ -715,7 +795,7 @@ tcp socket - upstream response headers too large, increase wasm_socket_large_buf -=== TEST 30: proxy_wasm - dispatch_http_call() too small wasm_socket_large_buffers size +=== TEST 33: proxy_wasm - dispatch_http_call() too small wasm_socket_large_buffers size --- skip_no_debug: 4 --- load_nginx_modules: ngx_http_echo_module --- wasm_modules: hostcalls @@ -743,7 +823,7 @@ tcp socket - upstream response headers too large, increase wasm_socket_large_buf -=== TEST 31: proxy_wasm - dispatch_http_call() not enough wasm_socket_large_buffers +=== TEST 34: proxy_wasm - dispatch_http_call() not enough wasm_socket_large_buffers --- load_nginx_modules: ngx_http_echo_module --- wasm_modules: hostcalls --- tcp_listen: 12345 @@ -774,7 +854,7 @@ sub { -=== TEST 32: proxy_wasm - dispatch_http_call() scarce wasm_socket_large_buffers +=== TEST 35: proxy_wasm - dispatch_http_call() scarce wasm_socket_large_buffers --- skip_no_debug: 4 --- load_nginx_modules: ngx_http_echo_module --- wasm_modules: hostcalls @@ -802,7 +882,7 @@ tcp socket trying to receive data (max: 1023) -=== TEST 33: proxy_wasm - dispatch_http_call() TCP reader error +=== TEST 36: proxy_wasm - dispatch_http_call() TCP reader error --- load_nginx_modules: ngx_http_echo_module --- wasm_modules: hostcalls --- tcp_listen: 12345 @@ -826,7 +906,7 @@ qr/(\[error\]|Uncaught RuntimeError|\s+).*?dispatch failed: tcp socket - parser -=== TEST 34: proxy_wasm - dispatch_http_call() re-entrant after status line +=== TEST 37: proxy_wasm - dispatch_http_call() re-entrant after status line --- skip_no_debug: 4 --- load_nginx_modules: ngx_http_echo_module --- wasm_modules: hostcalls @@ -854,7 +934,7 @@ tcp socket trying to receive data (max: 1017) -=== TEST 35: proxy_wasm - dispatch_http_call() trap in dispatch handler +=== TEST 38: proxy_wasm - dispatch_http_call() trap in dispatch handler --- load_nginx_modules: ngx_http_echo_module --- wasm_modules: hostcalls --- config @@ -879,7 +959,7 @@ tcp socket trying to receive data (max: 1017) -=== TEST 36: proxy_wasm - dispatch_http_call() invalid response headers +=== TEST 39: proxy_wasm - dispatch_http_call() invalid response headers --- load_nginx_modules: ngx_http_echo_module --- wasm_modules: hostcalls --- tcp_listen: 12345 @@ -905,7 +985,7 @@ qr/(\[error\]|Uncaught RuntimeError|\s+).*?dispatch failed: tcp socket - parser -=== TEST 37: proxy_wasm - dispatch_http_call() async dispatch x2 +=== TEST 40: proxy_wasm - dispatch_http_call() async dispatch x2 --- load_nginx_modules: ngx_http_echo_module --- wasm_modules: hostcalls --- config @@ -953,7 +1033,7 @@ qr/(\[error\]|Uncaught RuntimeError|\s+).*?dispatch failed: tcp socket - parser -=== TEST 38: proxy_wasm - dispatch_http_call() can be chained by different filters +=== TEST 41: proxy_wasm - dispatch_http_call() can be chained by different filters So long as these filters do not produce content. --- load_nginx_modules: ngx_http_echo_module --- wasm_modules: hostcalls @@ -986,7 +1066,7 @@ qr/^\*\d+ .*? on_http_call_response \(id: \d+[^*]* -=== TEST 39: proxy_wasm - dispatch_http_call() on log step +=== TEST 42: proxy_wasm - dispatch_http_call() on log step --- load_nginx_modules: ngx_http_echo_module --- wasm_modules: hostcalls --- config @@ -1010,7 +1090,7 @@ qr/(\[error\]|Uncaught RuntimeError|\s+).*?dispatch failed: bad step/ -=== TEST 40: proxy_wasm - dispatch_http_call() supports get_http_call_response_headers() +=== TEST 43: proxy_wasm - dispatch_http_call() supports get_http_call_response_headers() --- load_nginx_modules: ngx_http_echo_module --- wasm_modules: hostcalls --- config @@ -1034,7 +1114,7 @@ X-Callout-Header: callout-header-value -=== TEST 41: proxy_wasm - dispatch_http_call() get :status dispatch response header +=== TEST 44: proxy_wasm - dispatch_http_call() get :status dispatch response header --- load_nginx_modules: ngx_http_echo_module --- wasm_modules: hostcalls --- config diff --git a/t/lib/proxy-wasm-tests/hostcalls/src/types/test_http.rs b/t/lib/proxy-wasm-tests/hostcalls/src/types/test_http.rs index e1495dbce..b9d5a0ea0 100644 --- a/t/lib/proxy-wasm-tests/hostcalls/src/types/test_http.rs +++ b/t/lib/proxy-wasm-tests/hostcalls/src/types/test_http.rs @@ -178,13 +178,23 @@ impl TestHttp { } if self.get_config("no_authority").is_none() { - headers.push(( - ":authority", - self.config - .get("host") - .map(|v| v.as_str()) - .unwrap_or("default"), - )); + if self.get_config("authority").is_none() { + headers.push(( + ":authority", + self.config + .get("host") + .map(|v| v.as_str()) + .unwrap_or("default"), + )); + } else { + headers.push(( + ":authority", + self.config + .get("authority") + .map(|v| v.as_str()) + .unwrap_or("default"), + )); + } } if self.get_config("https") == Some("yes") {