From 2ca1366f8ef416823e04aae9385f1be4319da905 Mon Sep 17 00:00:00 2001 From: vicanso Date: Mon, 13 Jan 2025 21:23:50 +0800 Subject: [PATCH] feat: support set response header if not exists --- examples/grpc-web.toml | 6 +----- examples/proxy-upstream.toml | 1 - examples/static-serve.toml | 1 - src/plugin/response_headers.rs | 36 +++++++++++++++++++++++++++++++++- web/src/i18n/en.ts | 3 +++ web/src/i18n/zh.ts | 3 +++ web/src/pages/Plugins.tsx | 8 ++++++++ 7 files changed, 50 insertions(+), 8 deletions(-) diff --git a/examples/grpc-web.toml b/examples/grpc-web.toml index bcffd26..04412d0 100644 --- a/examples/grpc-web.toml +++ b/examples/grpc-web.toml @@ -70,16 +70,12 @@ plugins = ["grpc-web-static"] [plugins.grpc-web-static] category = "directory" path = "~/tmp/grpc/commonjs-example" -step = "request" [servers.grpc-web] addr = "127.0.0.1:3000" enabled_h2 = true global_certificates = true -locations = [ - "grpc-web", - "grpc-server", -] +locations = ["grpc-web", "grpc-server"] modules = ["grpc-web"] [upstreams.grpc-server] diff --git a/examples/proxy-upstream.toml b/examples/proxy-upstream.toml index 6f9b5e5..c9522db 100644 --- a/examples/proxy-upstream.toml +++ b/examples/proxy-upstream.toml @@ -13,7 +13,6 @@ category = "cache" headers = ["Accept-Encoding"] max_ttl = "1h" namespace = "charts" -step = "request" [servers.charts] addr = "127.0.0.1:3000" diff --git a/examples/static-serve.toml b/examples/static-serve.toml index 168a487..f41cdfb 100644 --- a/examples/static-serve.toml +++ b/examples/static-serve.toml @@ -8,7 +8,6 @@ plugins = [ [plugins.staticServe] category = "directory" path = "~/github/pingap/dist" -step = "request" [servers.staticServe] addr = "127.0.0.1:3000" diff --git a/src/plugin/response_headers.rs b/src/plugin/response_headers.rs index 8b6e401..08759c1 100644 --- a/src/plugin/response_headers.rs +++ b/src/plugin/response_headers.rs @@ -59,6 +59,13 @@ pub struct ResponseHeaders { /// Example: [("x-old-header", "x-new-header")] rename_headers: Vec<(HeaderName, HeaderName)>, + /// Headers to be set only if they don't already exist in the response + /// - Only sets the header if it's not present + /// - Does not modify existing header values + /// - Format: Vec of (header_name, header_value) pairs + /// Example: [("x-default-header", "default-value")] + set_headers_not_exists: Vec, + /// Unique identifier for this plugin instance /// Generated from the plugin configuration to track changes hash_value: String, @@ -139,6 +146,17 @@ impl TryFrom<&PluginConf> for ResponseHeaders { rename_headers.push((original_name, new_name)); } } + let mut set_headers_not_exists = vec![]; + for item in get_str_slice_conf(value, "set_headers_not_exists").iter() { + let header = convert_header(item).map_err(|e| Error::Invalid { + category: PluginCategory::ResponseHeaders.to_string(), + message: e.to_string(), + })?; + if let Some(item) = header { + set_headers_not_exists.push(item); + } + } + let params = Self { hash_value, plugin_step: step, @@ -146,6 +164,7 @@ impl TryFrom<&PluginConf> for ResponseHeaders { set_headers, remove_headers, rename_headers, + set_headers_not_exists, }; if params.plugin_step != PluginStep::Response { @@ -252,6 +271,17 @@ impl Plugin for ResponseHeaders { } } + // Set headers that don't exist (conditional set) + for (name, value) in &self.set_headers_not_exists { + if !upstream_response.headers.contains_key(name) { + if let Some(value) = convert_header_value(value, session, ctx) { + let _ = upstream_response.insert_header(name, value); + } else { + let _ = upstream_response.insert_header(name, value); + } + } + } + // Rename headers (move values to new name) for (original_name, new_name) in &self.rename_headers { if let Some(value) = upstream_response.remove_header(original_name) @@ -337,6 +367,10 @@ set_headers = [ ] remove_headers = [ "Content-Type" +] +set_headers_not_exists = [ + "X-Response-Id:abc", + "X-Tag:userTag", ] "###, ) @@ -369,7 +403,7 @@ remove_headers = [ .unwrap(); assert_eq!( - r###"ResponseHeader { base: Parts { status: 200, version: HTTP/1.1, headers: {"x-service": "1", "x-service": "2", "x-response-id": "123"} }, header_name_map: None, reason_phrase: None }"###, + r###"ResponseHeader { base: Parts { status: 200, version: HTTP/1.1, headers: {"x-service": "1", "x-service": "2", "x-response-id": "123", "x-tag": "userTag"} }, header_name_map: None, reason_phrase: None }"###, format!("{upstream_response:?}") ) } diff --git a/web/src/i18n/en.ts b/web/src/i18n/en.ts index 27452a9..2a53202 100644 --- a/web/src/i18n/en.ts +++ b/web/src/i18n/en.ts @@ -422,6 +422,9 @@ export default { responseHeadersRenameHeader: "Rename Header", responseHeadersRenamePlaceholder: "Input the original header name : Input the new header name", + responseHeadersSetHeaderNotExists: "Set Header If Not Exists", + responseHeadersSetHeaderNotExistsPlaceholder: + "Input the header name : Input the header value", responseHeadersRemoveHeader: "Remove Header", responseHeadersRemoveHeaderPlaceholder: "Input the header name", combinedAuthAuthorizations: "Authorizations", diff --git a/web/src/i18n/zh.ts b/web/src/i18n/zh.ts index dc5da3e..dbc5e92 100644 --- a/web/src/i18n/zh.ts +++ b/web/src/i18n/zh.ts @@ -400,6 +400,9 @@ export default { responseHeadersRenameHeader: "修改响应头", responseHeadersRenamePlaceholder: "输入原来的响应头名称 : 输入新的响应头名称", + responseHeadersSetHeaderNotExists: "设置响应头(不存在时)", + responseHeadersSetHeaderNotExistsPlaceholder: + "输入设置响应头的名称 : 输入设置响应头的值", responseHeadersRemoveHeader: "删除响应头", responseHeadersRemoveHeaderPlaceholder: "输入响应头名称", combinedAuthAuthorizations: "认证配置列表", diff --git a/web/src/pages/Plugins.tsx b/web/src/pages/Plugins.tsx index e90a781..bb5213b 100644 --- a/web/src/pages/Plugins.tsx +++ b/web/src/pages/Plugins.tsx @@ -954,6 +954,14 @@ export default function Plugins() { span: 6, category: ExFormItemCategory.KV_LIST, }, + { + name: "set_headers_not_exists", + label: pluginI18n("responseHeadersSetHeaderNotExists"), + placeholder: pluginI18n("responseHeadersSetHeaderNotExistsPlaceholder"), + defaultValue: pluginConfig.set_headers_not_exists as string[], + span: 6, + category: ExFormItemCategory.KV_LIST, + }, ); break; }