From ac1a293cbbc1ac5c45203e8e87e47697c88c829a Mon Sep 17 00:00:00 2001 From: u5surf Date: Sun, 31 Dec 2023 20:41:08 +0900 Subject: [PATCH] Support query string encoding for control API #279 --- src/ngx_http_vhost_traffic_status_display.c | 53 +++++++++++- src/ngx_http_vhost_traffic_status_string.c | 1 + t/007.control_status_group.t | 93 +++++++++++++++++++++ t/008.control_status_zone.t | 84 +++++++++++++++++++ 4 files changed, 227 insertions(+), 4 deletions(-) diff --git a/src/ngx_http_vhost_traffic_status_display.c b/src/ngx_http_vhost_traffic_status_display.c index 4f4c01f..59f2da1 100644 --- a/src/ngx_http_vhost_traffic_status_display.c +++ b/src/ngx_http_vhost_traffic_status_display.c @@ -67,7 +67,7 @@ static ngx_int_t ngx_http_vhost_traffic_status_display_handler_control(ngx_http_request_t *r) { ngx_int_t size, rc; - ngx_str_t type, alpha, arg_cmd, arg_group, arg_zone; + ngx_str_t type, alpha, encoded_ch, arg_cmd, arg_group, arg_zone; ngx_buf_t *b; ngx_chain_t out; ngx_slab_pool_t *shpool; @@ -125,7 +125,8 @@ ngx_http_vhost_traffic_status_display_handler_control(ngx_http_request_t *r) if (ngx_http_arg(r, (u_char *) "group", 5, &arg_group) == NGX_OK) { - if (arg_group.len == 1 && ngx_strncmp(arg_group.data, "*", 1) == 0) + if ((arg_group.len == 1 && ngx_strncmp(arg_group.data, "*", 1) == 0) + || (arg_group.len == 3 && ngx_strncasecmp(arg_group.data, (u_char *) "%2A", 3) == 0)) { control->group = -1; } @@ -134,13 +135,17 @@ ngx_http_vhost_traffic_status_display_handler_control(ngx_http_request_t *r) { control->group = NGX_HTTP_VHOST_TRAFFIC_STATUS_UPSTREAM_NO; } - else if (arg_group.len == 14 + else if ((arg_group.len == 14 && ngx_strncasecmp(arg_group.data, (u_char *) "upstream@alone", 14) == 0) + || (arg_group.len == 16 + && ngx_strncasecmp(arg_group.data, (u_char *) "upstream%40alone", 16) == 0)) { control->group = NGX_HTTP_VHOST_TRAFFIC_STATUS_UPSTREAM_UA; } - else if (arg_group.len == 14 + else if ((arg_group.len == 14 && ngx_strncasecmp(arg_group.data, (u_char *) "upstream@group", 14) == 0) + || (arg_group.len == 16 + && ngx_strncasecmp(arg_group.data, (u_char *) "upstream%40group", 16) == 0)) { control->group = NGX_HTTP_VHOST_TRAFFIC_STATUS_UPSTREAM_UG; } @@ -171,6 +176,46 @@ ngx_http_vhost_traffic_status_display_handler_control(ngx_http_request_t *r) "display_handler_control::copy_str() failed"); } + ngx_str_set(&encoded_ch, "%2A"); + + rc = ngx_http_vhost_traffic_status_replace_strc(control->zone, &encoded_ch, '*'); + if (rc != NGX_OK) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "display_handler_control::replace_strc() failed"); + } + + ngx_str_set(&encoded_ch, "%2a"); + + rc = ngx_http_vhost_traffic_status_replace_strc(control->zone, &encoded_ch, '*'); + if (rc != NGX_OK) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "display_handler_control::replace_strc() failed"); + } + + ngx_str_set(&encoded_ch, "%3A"); + + rc = ngx_http_vhost_traffic_status_replace_strc(control->zone, &encoded_ch, ':'); + if (rc != NGX_OK) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "display_handler_control::replace_strc() failed"); + } + + ngx_str_set(&encoded_ch, "%3a"); + + rc = ngx_http_vhost_traffic_status_replace_strc(control->zone, &encoded_ch, ':'); + if (rc != NGX_OK) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "display_handler_control::replace_strc() failed"); + } + + ngx_str_set(&encoded_ch, "%40"); + + rc = ngx_http_vhost_traffic_status_replace_strc(control->zone, &encoded_ch, '@'); + if (rc != NGX_OK) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "display_handler_control::replace_strc() failed"); + } + (void) ngx_http_vhost_traffic_status_replace_chrc(control->zone, '@', NGX_HTTP_VHOST_TRAFFIC_STATUS_KEY_SEPARATOR); diff --git a/src/ngx_http_vhost_traffic_status_string.c b/src/ngx_http_vhost_traffic_status_string.c index cc64e61..3d375e7 100644 --- a/src/ngx_http_vhost_traffic_status_string.c +++ b/src/ngx_http_vhost_traffic_status_string.c @@ -161,6 +161,7 @@ ngx_http_vhost_traffic_status_replace_strc(ngx_str_t *buf, if (n > 0) { buf->len = buf->len - (n * dst->len) + n; } + *(buf->data + buf->len) = '\0'; return NGX_OK; } diff --git a/t/007.control_status_group.t b/t/007.control_status_group.t index f39bfe0..4eec864 100644 --- a/t/007.control_status_group.t +++ b/t/007.control_status_group.t @@ -201,3 +201,96 @@ __DATA__ 'OK', '{"cacheZones.*cache_(one|two)' ] + +=== TEST 7: /status/control?cmd=status&group=upstream%40group&zone=%2A +--- http_config + vhost_traffic_status_zone; + upstream backend { + server localhost; + } + server { + server_name backend; + } +--- config + location /status { + vhost_traffic_status_display; + vhost_traffic_status_display_format json; + access_log off; + } + location /backend { + proxy_set_header Host backend; + proxy_pass http://backend; + } +--- user_files eval +[ + ['backend/file.txt' => 'upstream@group:OK'] +] +--- request eval +[ + 'GET /backend/file.txt', + 'GET /status/control?cmd=status&group=upstream%40group&zone=%2A', +] +--- response_body_like eval +[ + 'OK', + '{"upstreamZones.*backend' +] + +=== TEST 8: /status/control?cmd=status&group=upstream%40group&zone=%2a +--- http_config + vhost_traffic_status_zone; + upstream backend { + server localhost; + } + server { + server_name backend; + } +--- config + location /status { + vhost_traffic_status_display; + vhost_traffic_status_display_format json; + access_log off; + } + location /backend { + proxy_set_header Host backend; + proxy_pass http://backend; + } +--- user_files eval +[ + ['backend/file.txt' => 'upstream@group:OK'] +] +--- request eval +[ + 'GET /backend/file.txt', + 'GET /status/control?cmd=status&group=upstream%40group&zone=%2a', +] +--- response_body_like eval +[ + 'OK', + '{"upstreamZones.*backend' +] + +=== TEST 9: /status/control?cmd=status&group=server&zone=%3A%3Amain +--- http_config + vhost_traffic_status_zone; +--- config + location /status { + vhost_traffic_status_display; + vhost_traffic_status_display_format json; + access_log off; + } +--- user_files eval +[ + ['storage/control/file.txt' => 'server:OK'] +] +--- request eval +[ + 'GET /storage/control/file.txt', + 'GET /status/control?cmd=status&group=server&zone=%3A%3Amain', +] +--- response_body_like eval +[ + 'OK', + 'hostName' +] + diff --git a/t/008.control_status_zone.t b/t/008.control_status_zone.t index fbc8955..ff9fb1f 100644 --- a/t/008.control_status_zone.t +++ b/t/008.control_status_zone.t @@ -175,3 +175,87 @@ __DATA__ 'OK', '{"cache_one"' ] + +=== TEST 6: /status/control?cmd=status&group=filter&zone=storage%3A%3Alocalhost%40vol0 +--- http_config + vhost_traffic_status_zone; +--- config + location /status { + vhost_traffic_status_display; + vhost_traffic_status_display_format json; + access_log off; + } + location ~ ^/storage/(.+)/.*$ { + set $volume $1; + vhost_traffic_status_filter_by_set_key $volume storage::$server_name; + } +--- user_files eval +[ + ['storage/vol0/file.txt' => 'filter:OK'] +] +--- request eval +[ + 'GET /storage/vol0/file.txt', + 'GET /status/control?cmd=status&group=filter&zone=storage%3A%3Alocalhost%40vol0', +] +--- response_body_like eval +[ + 'OK', + '{"vol0"' +] + +=== TEST 7: /status/control?cmd=status&group=upstream%40alone&zone=127.0.0.1%3A1981 +--- http_config + vhost_traffic_status_zone; +--- config + location /status { + vhost_traffic_status_display; + vhost_traffic_status_display_format json; + access_log off; + } + location /backend { + proxy_set_header Host backend; + proxy_pass http://127.0.0.1:1981; + } +--- tcp_listen: 1981 +--- tcp_reply eval +"HTTP/1.1 200 OK\r\n\r\nupstream\@alone:OK" +--- request eval +[ + 'GET /backend/file.txt', + 'GET /status/control?cmd=status&group=upstream%40alone&zone=127.0.0.1%3A1981', +] +--- response_body_like eval +[ + 'OK', + '{"server":"127.0.0.1:1981"' +] + +=== TEST 8: /status/control?cmd=status&group=filter&zone=storage%3a%3alocalhost%40vol0 +--- http_config + vhost_traffic_status_zone; +--- config + location /status { + vhost_traffic_status_display; + vhost_traffic_status_display_format json; + access_log off; + } + location ~ ^/storage/(.+)/.*$ { + set $volume $1; + vhost_traffic_status_filter_by_set_key $volume storage::$server_name; + } +--- user_files eval +[ + ['storage/vol0/file.txt' => 'filter:OK'] +] +--- request eval +[ + 'GET /storage/vol0/file.txt', + 'GET /status/control?cmd=status&group=filter&zone=storage%3A%3Alocalhost%40vol0', +] +--- response_body_like eval +[ + 'OK', + '{"vol0"' +] +