-
Notifications
You must be signed in to change notification settings - Fork 2.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: support lua proxy upstream #12086
base: master
Are you sure you want to change the base?
Conversation
@@ -85,6 +90,10 @@ function _M.access(conf, ctx) | |||
core.log.warn("plugin access phase, conf: ", core.json.encode(conf)) | |||
-- return 200, {message = "hit example plugin"} | |||
|
|||
if conf.lua_proxy_upstream then |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a feature PR that has added plugin configuration and requires updating schema.
common_phase("before_proxy") | ||
return | ||
end | ||
|
||
-- run the before_proxy method in access phase first to avoid always reinit request | ||
common_phase("before_proxy") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
common_phase("before_proxy")
if api_ctx.lua_proxy_upstream then
return
end
@@ -550,6 +550,12 @@ function _M.handle_upstream(api_ctx, route, enable_websocket) | |||
|
|||
set_upstream_headers(api_ctx, server) | |||
|
|||
-- lua proxy the request to upstream | |||
if api_ctx.lua_proxy_upstream then |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought about the naming of the existing variables bypass_nginx_upstream
and lua_proxy_upstream
, and felt that their meanings are unclear. I think we can design them as two:
bypass_nginx_upstream
to indicate bypassing nginx's upstream modulebypass_balancer
to indicate bypassing the existing balancer logic
cc @membphis What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with this
end | ||
|
||
local headers = core.request.headers(ctx) | ||
-- When content-length is cleared, the HTTP server will automatically calculate and |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-- When content-length is cleared, the HTTP server will automatically calculate and | |
-- When content-length is cleared, the HTTP client will automatically calculate and |
-- TODO: support event stream | ||
if content_type and core.string.find(content_type, "text/event-stream") then | ||
core.log.error("event stream is not supported") | ||
return HTTP_INTERNAL_SERVER_ERROR | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This already has example code in ai-proxy.
"example-plugin": { | ||
"i": 0, | ||
"lua_proxy_upstream": true, | ||
"request_uri": "http://httpbin.org/get", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rename this request_uri
to indicate it's used to change the rseponse body, it make me confused with upstream.nodes
core.log.warn("plugin lua_body_filter phase, conf: ", core.json.encode(conf)) | ||
core.log.warn("plugin lua_body_filter phase, body: ", core.json.encode(body)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can assert those log in test cases to confirm the body come from upstream is passed to lua_body_filter
as expect.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto
|
||
|
||
|
||
=== TEST 14: lua body filter |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to add more test cases that combine example-plugin with other plugins like proxy-rewrite, which modify downstream requests. This will help verify that the new Lua HTTP client way works with existing plugins in the rewrite/access phase.
headers = headers, | ||
ssl_verify = conf.ssl_verify, | ||
keepalive = conf.keepalive, | ||
keepalive_timeout = conf.timeout, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This timeout
is unclear.
The meanings of connection timeout and idle connection timeout are different.
I suggest using keepalive_timeout
-- Get request body | ||
local body, err = core.request.get_body() | ||
if err then | ||
core.log.error("failed to get request body: ", err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why isn't there a return for this error?
@@ -107,6 +116,49 @@ function _M.access(conf, ctx) | |||
return | |||
end | |||
|
|||
|
|||
function _M.before_proxy(conf, ctx) | |||
core.log.warn("plugin before_proxy phase, conf: ", core.json.encode(conf)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not a warm level log, please use core.json.delay_encode
local code, body = read_response(ctx, res) | ||
|
||
-- Set keepalive for connection reuse | ||
if opts.keepalive then |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do I not need to close the connection when the keepalive is false?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the mean of another complex mechanism introduced by this PR? It's introducing a new plugin phase again, and continuing to introduce new learning costs, and I can't accept it without significant justification.
Now, even a professional APISIX developer would be hard pressed to say what APISIX phases are and how they correspond to NGINX phases.
How does it differ from performing HTTP requests at rewrite/access using lua-resty-http
?
Has the solution been properly evaluated for performance differences with access phase + subrequests? This would also allow you to implement running external requests in the header_filter
and body_filter
phases, and there would be no need to introduce new request phases, just a new core API is enough.
Description
Currently, APISIX cannot support making HTTP requests during the body_filter phase.
This PR adds a
lua_body_filter
phase, which uses Lua to make HTTP requests to upstream services, bypassing Nginx's default upstream request behavior.This allows for more flexible request operations in the
lua_body_filter
phase.The
lua_body_filter
phase executes beforeheader_filter
andbody_filter
phases.Checklist