249 lines
7.1 KiB
Lua
249 lines
7.1 KiB
Lua
local cjson = require "cjson"
|
|
local helpers = require "spec.helpers"
|
|
|
|
-- returns nth byte (0: LSB, 3: MSB if 32-bit)
|
|
local function nbyt(x, n)
|
|
return bit.band(bit.rshift(x, 8*n), 0xff)
|
|
end
|
|
|
|
local function be_bytes(x)
|
|
return nbyt(x, 3), nbyt(x, 2), nbyt(x, 1), nbyt(x, 0)
|
|
end
|
|
|
|
for _, strategy in helpers.each_strategy() do
|
|
|
|
describe("gRPC-Web Proxying [#" .. strategy .. "]", function()
|
|
local proxy_client
|
|
local proxy_client_ssl
|
|
|
|
local HELLO_REQUEST_TEXT_BODY = "AAAAAAYKBGhleWE="
|
|
local HELLO_REQUEST_BODY = ngx.decode_base64(HELLO_REQUEST_TEXT_BODY)
|
|
local HELLO_RESPONSE_TEXT_BODY = "AAAAAAwKCmhlbGxvIGhleWE=" ..
|
|
"gAAAAB5ncnBjLXN0YXR1czowDQpncnBjLW1lc3NhZ2U6DQo="
|
|
local HELLO_RESPONSE_BODY = ngx.decode_base64("AAAAAAwKCmhlbGxvIGhleWE=") ..
|
|
ngx.decode_base64("gAAAAB5ncnBjLXN0YXR1czowDQpncnBjLW1lc3NhZ2U6DQo=")
|
|
|
|
lazy_setup(function()
|
|
local bp = helpers.get_db_utils(strategy, {
|
|
"routes",
|
|
"services",
|
|
"plugins",
|
|
}, {
|
|
"grpc-web",
|
|
})
|
|
|
|
local service1 = assert(bp.services:insert {
|
|
name = "grpc",
|
|
url = helpers.grpcbin_url,
|
|
})
|
|
|
|
local route1 = assert(bp.routes:insert {
|
|
protocols = { "http", "https" },
|
|
paths = { "/" },
|
|
service = service1,
|
|
})
|
|
|
|
local route2 = assert(bp.routes:insert {
|
|
protocols = { "http", "https" },
|
|
paths = { "/prefix" },
|
|
service = service1,
|
|
})
|
|
|
|
local mock_grpc_service = assert(bp.services:insert {
|
|
name = "mock_grpc_service",
|
|
url = "http://localhost:8765",
|
|
})
|
|
|
|
local mock_grpc_route = assert(bp.routes:insert {
|
|
protocols = { "http" },
|
|
hosts = { "grpc_mock.example" },
|
|
service = mock_grpc_service,
|
|
preserve_host = true,
|
|
})
|
|
|
|
assert(bp.plugins:insert {
|
|
route = mock_grpc_route,
|
|
name = "grpc-web",
|
|
config = {
|
|
},
|
|
})
|
|
|
|
assert(bp.plugins:insert {
|
|
route = route1,
|
|
name = "grpc-web",
|
|
config = {
|
|
proto = "spec/fixtures/grpc/hello.proto",
|
|
},
|
|
})
|
|
|
|
assert(bp.plugins:insert {
|
|
route = route2,
|
|
name = "grpc-web",
|
|
config = {
|
|
proto = "spec/fixtures/grpc/hello.proto",
|
|
pass_stripped_path = true,
|
|
},
|
|
})
|
|
|
|
local fixtures = {
|
|
http_mock = {}
|
|
}
|
|
fixtures.http_mock.my_server_block = [[
|
|
server {
|
|
server_name myserver;
|
|
listen 8765;
|
|
location ~ / {
|
|
content_by_lua_block {
|
|
local headers = ngx.req.get_headers()
|
|
ngx.header.content_type = "application/grpc"
|
|
ngx.header.received_host = headers["Host"]
|
|
ngx.header.received_te = headers["te"]
|
|
}
|
|
}
|
|
}
|
|
]]
|
|
|
|
assert(helpers.start_kong({
|
|
database = strategy,
|
|
plugins = "bundled,grpc-web",
|
|
nginx_conf = "spec/fixtures/custom_nginx.template",
|
|
}, nil, nil, fixtures))
|
|
end)
|
|
|
|
before_each(function()
|
|
proxy_client = helpers.proxy_client(1000)
|
|
proxy_client_ssl = helpers.proxy_ssl_client(1000)
|
|
end)
|
|
|
|
lazy_teardown(function()
|
|
helpers.stop_kong()
|
|
end)
|
|
|
|
|
|
test("Sets 'TE: trailers'", function()
|
|
local res, err = proxy_client:post("/", {
|
|
headers = {
|
|
["Host"] = "grpc_mock.example",
|
|
["Content-Type"] = "application/grpc-web-text",
|
|
},
|
|
})
|
|
|
|
assert.equal("trailers", res.headers["received-te"])
|
|
assert.is_nil(err)
|
|
end)
|
|
|
|
test("Ignores user-agent TE", function()
|
|
-- in grpc-web, kong acts as a grpc client on behalf of the client
|
|
-- (which generally is a web-browser); as such, the Te header must be
|
|
-- set by kong, which will append trailers to the response body
|
|
local res, err = proxy_client:post("/", {
|
|
headers = {
|
|
["Host"] = "grpc_mock.example",
|
|
["Content-Type"] = "application/grpc-web-text",
|
|
["TE"] = "chunked",
|
|
},
|
|
})
|
|
|
|
assert.equal("trailers", res.headers["received-te"])
|
|
assert.is_nil(err)
|
|
end)
|
|
|
|
test("Call gRCP-base64 via HTTP", function()
|
|
local res, err = proxy_client:post("/hello.HelloService/SayHello", {
|
|
headers = {
|
|
["Content-Type"] = "application/grpc-web-text",
|
|
["Content-Length"] = tostring(#HELLO_REQUEST_TEXT_BODY),
|
|
},
|
|
body = HELLO_REQUEST_TEXT_BODY,
|
|
})
|
|
|
|
assert.equal(HELLO_RESPONSE_TEXT_BODY, res:read_body())
|
|
assert.is_nil(err)
|
|
end)
|
|
|
|
test("Call gRCP-base64 via HTTPS", function()
|
|
local res, err = proxy_client_ssl:post("/hello.HelloService/SayHello", {
|
|
headers = {
|
|
["Content-Type"] = "application/grpc-web-text",
|
|
["Content-Length"] = tostring(#HELLO_REQUEST_TEXT_BODY),
|
|
},
|
|
body = HELLO_REQUEST_TEXT_BODY,
|
|
})
|
|
|
|
assert.equal(HELLO_RESPONSE_TEXT_BODY, res:read_body())
|
|
assert.is_nil(err)
|
|
end)
|
|
|
|
test("Call binary gRCP via HTTP", function()
|
|
local res, err = proxy_client:post("/hello.HelloService/SayHello", {
|
|
headers = {
|
|
["Content-Type"] = "application/grpc-web+proto",
|
|
["Content-Length"] = tostring(#HELLO_REQUEST_BODY),
|
|
},
|
|
body = HELLO_REQUEST_BODY,
|
|
})
|
|
|
|
assert.equal(HELLO_RESPONSE_BODY, res:read_body())
|
|
assert.is_nil(err)
|
|
end)
|
|
|
|
test("Call binary gRCP via HTTPS", function()
|
|
local res, err = proxy_client_ssl:post("/hello.HelloService/SayHello", {
|
|
headers = {
|
|
["Content-Type"] = "application/grpc-web+proto",
|
|
["Content-Length"] = tostring(#HELLO_REQUEST_BODY),
|
|
},
|
|
body = HELLO_REQUEST_BODY,
|
|
})
|
|
|
|
assert.equal(HELLO_RESPONSE_BODY, res:read_body())
|
|
assert.is_nil(err)
|
|
end)
|
|
|
|
test("Call gRPC-Web JSON via HTTP", function()
|
|
local req = cjson.encode{ greeting = "heya" }
|
|
req = string.char(0, be_bytes(#req)) .. req
|
|
local res, err = proxy_client:post("/hello.HelloService/SayHello", {
|
|
headers = {
|
|
["Content-Type"] = "application/grpc-web+json",
|
|
["Content-Length"] = tostring(#req)
|
|
},
|
|
body = req,
|
|
})
|
|
|
|
local resp = cjson.encode{ reply = "hello heya" }
|
|
resp = string.char(0, be_bytes(#resp)) .. resp
|
|
|
|
local trailer = "grpc-status:0\r\ngrpc-message:\r\n"
|
|
trailer = string.char(0x80, be_bytes(#trailer)) .. trailer
|
|
|
|
assert.equal(resp .. trailer, res:read_body())
|
|
assert.is_nil(err)
|
|
end)
|
|
|
|
test("Call plain JSON via HTTP", function()
|
|
local res, err = proxy_client:post("/hello.HelloService/SayHello", {
|
|
headers = {
|
|
["Content-Type"] = "application/json",
|
|
},
|
|
body = cjson.encode{ greeting = "heya" },
|
|
})
|
|
|
|
assert.same({ reply = "hello heya" }, cjson.decode((res:read_body())))
|
|
assert.is_nil(err)
|
|
end)
|
|
|
|
test("Pass stripped URI", function()
|
|
local res, err = proxy_client:post("/prefix/hello.HelloService/SayHello", {
|
|
headers = {
|
|
["Content-Type"] = "application/json",
|
|
},
|
|
body = cjson.encode{ greeting = "heya" },
|
|
})
|
|
|
|
assert.same({ reply = "hello heya" }, cjson.decode((res:read_body())))
|
|
assert.is_nil(err)
|
|
end)
|
|
end)
|
|
end
|