This is an automated email from the ASF dual-hosted git repository.
shreemaanabhishek pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix.git
The following commit(s) were added to refs/heads/master by this push:
new 3ba27f680 chore: header related fixes (#12961)
3ba27f680 is described below
commit 3ba27f680f358384b2bc5b913b990485a7a04315
Author: Shreemaan Abhishek <[email protected]>
AuthorDate: Thu Feb 5 15:11:47 2026 +0545
chore: header related fixes (#12961)
---
apisix/plugins/ai-drivers/openai-base.lua | 30 ++++++-
t/plugin/ai-proxy-multi3.t | 18 ----
t/plugin/ai-proxy.t | 135 ++++++++++++++++++++++++++++--
t/plugin/ai-rate-limiting.t | 16 ----
4 files changed, 158 insertions(+), 41 deletions(-)
diff --git a/apisix/plugins/ai-drivers/openai-base.lua
b/apisix/plugins/ai-drivers/openai-base.lua
index 8688f9eb0..4f279bbc3 100644
--- a/apisix/plugins/ai-drivers/openai-base.lua
+++ b/apisix/plugins/ai-drivers/openai-base.lua
@@ -40,6 +40,7 @@ local math = math
local os = os
local ipairs = ipairs
local setmetatable = setmetatable
+local str_lower = string.lower
local HTTP_INTERNAL_SERVER_ERROR = ngx.HTTP_INTERNAL_SERVER_ERROR
local HTTP_GATEWAY_TIMEOUT = ngx.HTTP_GATEWAY_TIMEOUT
@@ -193,6 +194,7 @@ local function read_response(conf, ctx, res,
response_filter)
end
ctx.var.llm_prompt_tokens = ctx.ai_token_usage.prompt_tokens or 0
ctx.var.llm_completion_tokens = ctx.ai_token_usage.completion_tokens
or 0
+
if type(res_body.choices) == "table" and #res_body.choices > 0 then
local contents = {}
for _, choice in ipairs(res_body.choices) do
@@ -209,6 +211,31 @@ local function read_response(conf, ctx, res,
response_filter)
plugin.lua_response_filter(ctx, headers, raw_res_body)
end
+-- We want to forward all client headers to the LLM upstream by copying
headers from the client
+-- but copying content-length is destructive, similarly some headers like
`host`
+-- should not be forwarded either
+local function construct_forward_headers(ext_opts_headers, ctx)
+ local blacklist = {
+ "host",
+ "content-length"
+ }
+
+ -- make header keys lower case to overwrite downstream headers correctly,
+ -- because downstream headers are lower case
+ local opts_headers_lower = {}
+ for k, v in pairs(ext_opts_headers or {}) do
+ opts_headers_lower[str_lower(k)] = v
+ end
+ local headers = core.table.merge(core.request.headers(ctx),
opts_headers_lower)
+ headers["Content-Type"] = "application/json"
+
+ for _, h in ipairs(blacklist) do
+ headers[h] = nil
+ end
+
+ return headers
+end
+
local gcp_access_token_cache = lrucache.new(1024 * 4)
@@ -297,8 +324,7 @@ function _M.request(self, ctx, conf, request_table,
extra_opts)
local path = (parsed_url and parsed_url.path or self.path)
- local headers = auth.header or {}
- headers["Content-Type"] = "application/json"
+ local headers = construct_forward_headers(auth.header or {}, ctx)
if token then
headers["Authorization"] = "Bearer " .. token
end
diff --git a/t/plugin/ai-proxy-multi3.t b/t/plugin/ai-proxy-multi3.t
index dfb95d8a2..bec93fc4a 100644
--- a/t/plugin/ai-proxy-multi3.t
+++ b/t/plugin/ai-proxy-multi3.t
@@ -70,18 +70,6 @@ add_block_preprocessor(sub {
local body, err = ngx.req.get_body_data()
body, err = json.decode(body)
- local test_type = ngx.req.get_headers()["test-type"]
- if test_type == "options" then
- if body.foo == "bar" then
- ngx.status = 200
- ngx.say("options works")
- else
- ngx.status = 500
- ngx.say("model options feature doesn't work")
- end
- return
- end
-
local header_auth = ngx.req.get_headers()["authorization"]
local query_auth = ngx.req.get_uri_args()["apikey"]
@@ -255,7 +243,6 @@ passed
}]],
nil,
{
- ["test-type"] = "options",
["Content-Type"] = "application/json",
}
)
@@ -419,7 +406,6 @@ passed
}]],
nil,
{
- ["test-type"] = "options",
["Content-Type"] = "application/json",
}
)
@@ -581,7 +567,6 @@ passed
}]],
nil,
{
- ["test-type"] = "options",
["Content-Type"] = "application/json",
}
)
@@ -769,7 +754,6 @@ passed
}]],
nil,
{
- ["test-type"] = "options",
["Content-Type"] = "application/json",
}
)
@@ -1022,7 +1006,6 @@ POST /ai
}]],
nil,
{
- ["test-type"] = "options",
["Content-Type"] = "application/json",
}
)
@@ -1040,7 +1023,6 @@ POST /ai
}]],
nil,
{
- ["test-type"] = "options",
["Content-Type"] = "application/json",
}
)
diff --git a/t/plugin/ai-proxy.t b/t/plugin/ai-proxy.t
index cc86332af..bad63aeaf 100644
--- a/t/plugin/ai-proxy.t
+++ b/t/plugin/ai-proxy.t
@@ -62,7 +62,7 @@ add_block_preprocessor(sub {
if test_type == "options" then
if body.foo == "bar" then
ngx.status = 200
- ngx.say("options works")
+ ngx.print("options works")
else
ngx.status = 500
ngx.say("model options feature doesn't work")
@@ -70,6 +70,11 @@ add_block_preprocessor(sub {
return
end
+ if test_type == "header_forwarding" then
+ ngx.say(json.encode(ngx.req.get_headers()))
+ return
+ end
+
local header_auth = ngx.req.get_headers()["authorization"]
local query_auth = ngx.req.get_uri_args()["apikey"]
@@ -168,7 +173,7 @@ add_block_preprocessor(sub {
location /random {
content_by_lua_block {
- ngx.say("path override works")
+ ngx.print("path override works")
}
}
}
@@ -448,8 +453,8 @@ unsupported content-type:
application/x-www-form-urlencoded, only application/js
}
}
--- error_code: 200
---- response_body_chomp
-options_works
+--- response_body
+options works
@@ -510,7 +515,7 @@ options_works
}
}
---- response_body_chomp
+--- response_body
path override works
@@ -698,3 +703,123 @@ qr/.*text-embedding-ada-002*/
GET /t
--- response_body
ok
+
+
+
+=== TEST 18: set route with right auth header
+--- config
+ location /t {
+ content_by_lua_block {
+ local t = require("lib.test_admin").test
+ local code, body = t('/apisix/admin/routes/1',
+ ngx.HTTP_PUT,
+ [[{
+ "uri": "/anything",
+ "plugins": {
+ "ai-proxy": {
+ "provider": "openai",
+ "auth": {
+ "header": {
+ "Authorization": "Bearer token"
+ }
+ },
+ "options": {
+ "model": "gpt-35-turbo-instruct",
+ "max_tokens": 512,
+ "temperature": 1.0
+ },
+ "override": {
+ "endpoint": "http://localhost:6724"
+ },
+ "ssl_verify": false
+ }
+ }
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.say(body)
+ }
+ }
+--- response_body
+passed
+
+
+
+=== TEST 19: send request
+--- request
+POST /anything
+{ "messages": [ { "role": "system", "content": "You are a mathematician" }, {
"role": "user", "content": "What is 1+1?"} ] }
+--- more_headers
+test-type: header_forwarding
+--- error_code: 200
+--- response_body eval
+qr/"test-type":"header_forwarding"/
+
+
+
+=== TEST 20: set route with right auth header
+--- config
+ location /t {
+ content_by_lua_block {
+ local t = require("lib.test_admin").test
+ local code, body = t('/apisix/admin/routes/1',
+ ngx.HTTP_PUT,
+ [[{
+ "uri": "/anything",
+ "plugins": {
+ "ai-proxy": {
+ "provider": "openai",
+ "auth": {
+ "header": {
+ "Authorization": "Bearer token"
+ }
+ },
+ "options": {
+ "model": "gpt-35-turbo-instruct",
+ "max_tokens": 512,
+ "temperature": 1.0
+ },
+ "override": {
+ "endpoint": "http://localhost:6724"
+ },
+ "ssl_verify": false
+ },
+ "request-id": {
+ }
+ }
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.say(body)
+ }
+ }
+--- response_body
+passed
+
+
+
+=== TEST 21: send request
+--- request
+POST /anything
+{ "messages": [ { "role": "system", "content": "You are a mathematician" }, {
"role": "user", "content": "What is 1+1?"} ] }
+--- more_headers
+test-type: header_forwarding
+--- error_code: 200
+--- response_body eval
+qr/"x-request-id":"[\d\w-]+"/
+
+
+
+=== TEST 22: send request with Authorization header
+--- request
+POST /anything
+{ "messages": [ { "role": "system", "content": "You are a mathematician" }, {
"role": "user", "content": "What is 1+1?"} ] }
+--- more_headers
+Authorization: Bearer wrong token
+--- error_code: 200
diff --git a/t/plugin/ai-rate-limiting.t b/t/plugin/ai-rate-limiting.t
index c6112b9ab..48c71d04f 100644
--- a/t/plugin/ai-rate-limiting.t
+++ b/t/plugin/ai-rate-limiting.t
@@ -78,18 +78,6 @@ add_block_preprocessor(sub {
local body, err = ngx.req.get_body_data()
body, err = json.decode(body)
- local test_type = ngx.req.get_headers()["test-type"]
- if test_type == "options" then
- if body.foo == "bar" then
- ngx.status = 200
- ngx.say("options works")
- else
- ngx.status = 500
- ngx.say("model options feature doesn't work")
- end
- return
- end
-
local header_auth = ngx.req.get_headers()["authorization"]
local query_auth = ngx.req.get_uri_args()["apikey"]
@@ -656,7 +644,6 @@ passed
}]],
nil,
{
- ["test-type"] = "options",
["Content-Type"] = "application/json",
}
)
@@ -675,7 +662,6 @@ passed
}]],
nil,
{
- ["test-type"] = "options",
["Content-Type"] = "application/json",
}
)
@@ -694,7 +680,6 @@ passed
}]],
nil,
{
- ["test-type"] = "options",
["Content-Type"] = "application/json",
}
)
@@ -793,7 +778,6 @@ passed
}]],
nil,
{
- ["test-type"] = "options",
["Content-Type"] = "application/json",
}
)