-
Notifications
You must be signed in to change notification settings - Fork 2.5k
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
fix(grpc-web): response contains two trailer chunks #11988
Changes from 4 commits
0ec8eea
30e60f2
26fc40b
1e945ca
08b163b
abb1f92
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -68,6 +68,32 @@ function _M.check_schema(conf) | |
return core.schema.check(schema, conf) | ||
end | ||
|
||
local function exit(ctx, status) | ||
ctx.grpc_web_skip_body_filter = true | ||
return status | ||
end | ||
|
||
local build_trailer = function (grpc_status, grpc_message) | ||
local status_str = "grpc-status:" .. grpc_status | ||
local status_msg = "grpc-message:" .. ( grpc_message or "") | ||
local grpc_web_trailer = status_str .. "\r\n" .. status_msg .. "\r\n" | ||
local len = #grpc_web_trailer | ||
|
||
-- 1 byte: 0x80 | ||
local trailer_buf = string.char(0x80) | ||
-- 4 bytes: length of the trailer | ||
trailer_buf = trailer_buf .. string.char( | ||
bit.band(bit.rshift(len, 24), 0xff), | ||
bit.band(bit.rshift(len, 16), 0xff), | ||
bit.band(bit.rshift(len, 8), 0xff), | ||
bit.band(len, 0xff) | ||
) | ||
-- n bytes: trailer | ||
trailer_buf = trailer_buf .. grpc_web_trailer | ||
|
||
return trailer_buf | ||
end | ||
|
||
function _M.access(conf, ctx) | ||
-- set context variable mime | ||
-- When processing non gRPC Web requests, `mime` can be obtained in the context | ||
|
@@ -76,19 +102,19 @@ function _M.access(conf, ctx) | |
|
||
local method = core.request.get_method() | ||
if method == ALLOW_METHOD_OPTIONS then | ||
return 204 | ||
return exit(ctx, 204) | ||
end | ||
|
||
if method ~= ALLOW_METHOD_POST then | ||
-- https://github.com/grpc/grpc-web/blob/master/doc/browser-features.md#cors-support | ||
core.log.error("request method: `", method, "` invalid") | ||
return 400 | ||
return exit(ctx, 405) | ||
end | ||
|
||
local encoding = grpc_web_content_encoding[ctx.grpc_web_mime] | ||
if not encoding then | ||
core.log.error("request Content-Type: `", ctx.grpc_web_mime, "` invalid") | ||
return 400 | ||
return exit(ctx, 400) | ||
end | ||
|
||
-- set context variable encoding method | ||
|
@@ -98,7 +124,7 @@ function _M.access(conf, ctx) | |
if not (ctx.curr_req_matched and ctx.curr_req_matched[":ext"]) then | ||
core.log.error("routing configuration error, grpc-web plugin only supports ", | ||
"`prefix matching` pattern routing") | ||
return 400 | ||
return exit(ctx, 400) | ||
end | ||
|
||
local path = ctx.curr_req_matched[":ext"] | ||
|
@@ -110,16 +136,17 @@ function _M.access(conf, ctx) | |
|
||
-- set grpc body | ||
local body, err = core.request.get_body() | ||
if err then | ||
if err or not body then | ||
core.log.error("failed to read request body, err: ", err) | ||
return 400 | ||
return exit(ctx, 400) | ||
end | ||
|
||
if encoding == CONTENT_ENCODING_BASE64 then | ||
body = decode_base64(body) | ||
ngx.log(ngx.WARN, "DECODE BODY: ", body) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Obviously here leaves out some unneeded code, which I will remove in another PR. 🫡 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is probably code for testing and debugging. |
||
if not body then | ||
core.log.error("failed to decode request body") | ||
return 400 | ||
return exit(ctx, 400) | ||
end | ||
end | ||
|
||
|
@@ -139,11 +166,19 @@ function _M.header_filter(conf, ctx) | |
if not ctx.cors_allow_origins then | ||
core.response.set_header("Access-Control-Allow-Origin", DEFAULT_CORS_ALLOW_ORIGIN) | ||
end | ||
core.response.set_header("Content-Type", ctx.grpc_web_mime) | ||
core.response.set_header("Access-Control-Expose-Headers", DEFAULT_CORS_EXPOSE_HEADERS) | ||
|
||
if not ctx.grpc_web_skip_body_filter then | ||
core.response.set_header("Content-Type", ctx.grpc_web_mime) | ||
core.response.set_header("Content-Length", nil) | ||
end | ||
end | ||
|
||
function _M.body_filter(conf, ctx) | ||
if ctx.grpc_web_skip_body_filter then | ||
return | ||
end | ||
|
||
-- If the MIME extension type description of the gRPC-Web standard is not obtained, | ||
-- indicating that the request is not based on the gRPC Web specification, | ||
-- the processing of the request body will be ignored | ||
|
@@ -172,35 +207,21 @@ function _M.body_filter(conf, ctx) | |
n bytes: trailer | ||
|
||
--]] | ||
local status = ctx.var.upstream_trailer_grpc_status | ||
local message = ctx.var.upstream_trailer_grpc_message | ||
if status ~= "" and status ~= nil then | ||
local status_str = "grpc-status:" .. status | ||
local status_msg = "grpc-message:" .. ( message or "") | ||
local grpc_web_trailer = status_str .. "\r\n" .. status_msg .. "\r\n" | ||
local len = #grpc_web_trailer | ||
|
||
-- 1 byte: 0x80 | ||
local trailer_buf = string.char(0x80) | ||
-- 4 bytes: length of the trailer | ||
trailer_buf = trailer_buf .. string.char( | ||
bit.band(bit.rshift(len, 24), 0xff), | ||
bit.band(bit.rshift(len, 16), 0xff), | ||
bit.band(bit.rshift(len, 8), 0xff), | ||
bit.band(len, 0xff) | ||
if ngx_arg[2] then -- if eof | ||
local status = ctx.var.upstream_trailer_grpc_status | ||
local message = ctx.var.upstream_trailer_grpc_message | ||
|
||
-- When the response body completes and still does not receive the grpc status | ||
local resp_ok = status ~= nil and status ~= "" | ||
local trailer_buf = build_trailer( | ||
resp_ok and status or 2, | ||
resp_ok and message or "upstream grpc status not received" | ||
) | ||
-- n bytes: trailer | ||
trailer_buf = trailer_buf .. grpc_web_trailer | ||
|
||
if ctx.grpc_web_encoding == CONTENT_ENCODING_BINARY then | ||
ngx_arg[1] = ngx_arg[1] .. trailer_buf | ||
else | ||
ngx_arg[1] = ngx_arg[1] .. encode_base64(trailer_buf) | ||
if ctx.grpc_web_encoding == CONTENT_ENCODING_BASE64 then | ||
trailer_buf = encode_base64(trailer_buf) | ||
end | ||
|
||
-- clear trailer | ||
ctx.var.upstream_trailer_grpc_status = nil | ||
ctx.var.upstream_trailer_grpc_message = nil | ||
ngx_arg[1] = ngx_arg[1] .. trailer_buf | ||
end | ||
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.
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.
Good catch
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 good first issue, hopefully someone from the community can fix this.