Skip to content
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 trailers #10851

Merged
merged 8 commits into from
Jan 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 48 additions & 1 deletion apisix/plugins/grpc-web.lua
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ local req_set_uri = ngx.req.set_uri
local req_set_body_data = ngx.req.set_body_data
local decode_base64 = ngx.decode_base64
local encode_base64 = ngx.encode_base64
local bit = require("bit")
local string = string


local ALLOW_METHOD_OPTIONS = "OPTIONS"
Expand Down Expand Up @@ -87,7 +89,7 @@ function _M.access(conf, ctx)
-- set grpc path
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")
"`prefix matching` pattern routing")
return 400
end

Expand Down Expand Up @@ -130,6 +132,7 @@ function _M.header_filter(conf, ctx)
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", "grpc-message,grpc-status")
end

function _M.body_filter(conf, ctx)
Expand All @@ -147,6 +150,50 @@ function _M.body_filter(conf, ctx)
chunk = encode_base64(chunk)
ngx_arg[1] = chunk
end

--[[
upstream_trailer_* available since NGINX version 1.13.10 :
https://nginx.org/en/docs/http/ngx_http_upstream_module.html#var_upstream_trailer_

grpc-web trailer format reference:
envoyproxy/envoy/source/extensions/filters/http/grpc_web/grpc_web_filter.cc
shreemaan-abhishek marked this conversation as resolved.
Show resolved Hide resolved

Format for grpc-web trailer
1 byte: 0x80
4 bytes: length of the trailer
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)
shreemaan-abhishek marked this conversation as resolved.
Show resolved Hide resolved
)
-- n bytes: trailer
trailer_buf = trailer_buf .. grpc_web_trailer

if ctx.grpc_web_encoding == CONTENT_ENCODING_BINARY then
shreemaan-abhishek marked this conversation as resolved.
Show resolved Hide resolved
ngx_arg[1] = ngx_arg[1] .. trailer_buf
else
ngx_arg[1] = ngx_arg[1] .. encode_base64(trailer_buf)
end

-- clear trailer
ctx.var.upstream_trailer_grpc_status = nil
ctx.var.upstream_trailer_grpc_message = nil
end
end

return _M
37 changes: 35 additions & 2 deletions t/plugin/grpc-web.t
Original file line number Diff line number Diff line change
Expand Up @@ -68,25 +68,33 @@ passed



=== TEST 2: Proxy unary request using APISIX gRPC-Web plugin
=== TEST 2: Proxy unary request using APISIX with trailers gRPC-Web plugin
--- exec
node ./t/plugin/grpc-web/client.js BIN UNARY
node ./t/plugin/grpc-web/client.js TEXT UNARY
--- response_body
Status: { code: 0, details: '', metadata: {} }
Status: { code: 0, details: '', metadata: {} }
{"name":"hello","path":"/hello"}
Status: { code: 0, details: '', metadata: {} }
Status: { code: 0, details: '', metadata: {} }
{"name":"hello","path":"/hello"}



=== TEST 3: Proxy server-side streaming request using APISIX gRPC-Web plugin
=== TEST 3: Proxy server-side streaming request using APISIX with trailers gRPC-Web plugin
shreemaan-abhishek marked this conversation as resolved.
Show resolved Hide resolved
--- exec
node ./t/plugin/grpc-web/client.js BIN STREAM
node ./t/plugin/grpc-web/client.js TEXT STREAM
--- response_body
{"name":"hello","path":"/hello"}
{"name":"world","path":"/world"}
Status: { code: 0, details: '', metadata: {} }
Status: { code: 0, details: '', metadata: {} }
{"name":"hello","path":"/hello"}
{"name":"world","path":"/world"}
Status: { code: 0, details: '', metadata: {} }
Status: { code: 0, details: '', metadata: {} }



Expand Down Expand Up @@ -227,3 +235,28 @@ Content-Type: application/grpc-web
--- response_headers
Access-Control-Allow-Origin: http://test.com
Content-Type: application/grpc-web



=== TEST 11: check for Access-Control-Expose-Headers header in response
--- request
POST /grpc/web/a6.RouteService/GetRoute
{}
--- more_headers
Origin: http://test.com
Content-Type: application/grpc-web
--- response_headers
Access-Control-Allow-Origin: http://test.com
Access-Control-Expose-Headers: grpc-message,grpc-status
Content-Type: application/grpc-web



=== TEST 12: verify trailers in response
--- exec
curl -iv --location 'http://127.0.0.1:1984/grpc/web/a6.RouteService/GetRoute' \
--header 'Content-Type: application/grpc-web+proto' \
--header 'X-Grpc-Web: 1' \
--data-binary '@./t/plugin/grpc-web/req.bin'
--- response_body eval
qr/grpc-status:0\x0d\x0agrpc-message:/
6 changes: 6 additions & 0 deletions t/plugin/grpc-web/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ class gRPCWebClient {
return
}
console.log(JSON.stringify(response.toObject()));
}).on("status", function (status) {
console.log("Status:", status);
});
}

Expand All @@ -62,6 +64,10 @@ class gRPCWebClient {
stream.on('end', function(end) {
stream.cancel();
});

stream.on("status", function (status) {
console.log("Status:", status);
});
}
}

Expand Down
Binary file added t/plugin/grpc-web/req.bin
Binary file not shown.
Loading