blob: 502b528689eba165f24364c641a090ae1dc72a8f [file] [log] [blame]
--
-- Licensed to the Apache Software Foundation (ASF) under one or more
-- contributor license agreements. See the NOTICE file distributed with
-- this work for additional information regarding copyright ownership.
-- The ASF licenses this file to You under the Apache License, Version 2.0
-- (the "License"); you may not use this file except in compliance with
-- the License. You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
local ngx = ngx
local ngx_arg = ngx.arg
local core = require("apisix.core")
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 ALLOW_METHOD_OPTIONS = "OPTIONS"
local ALLOW_METHOD_POST = "POST"
local CONTENT_ENCODING_BASE64 = "base64"
local CONTENT_ENCODING_BINARY = "binary"
local DEFAULT_CORS_ALLOW_ORIGIN = "*"
local DEFAULT_CORS_ALLOW_METHODS = ALLOW_METHOD_POST
local DEFAULT_CORS_ALLOW_HEADERS = "content-type,x-grpc-web,x-user-agent"
local DEFAULT_PROXY_CONTENT_TYPE = "application/grpc"
local plugin_name = "grpc-web"
local schema = {
type = "object",
properties = {},
}
local grpc_web_content_encoding = {
["application/grpc-web"] = CONTENT_ENCODING_BINARY,
["application/grpc-web-text"] = CONTENT_ENCODING_BASE64,
["application/grpc-web+proto"] = CONTENT_ENCODING_BINARY,
["application/grpc-web-text+proto"] = CONTENT_ENCODING_BASE64,
}
local _M = {
version = 0.1,
priority = 505,
name = plugin_name,
schema = schema,
}
function _M.check_schema(conf)
return core.schema.check(schema, conf)
end
function _M.access(conf, ctx)
-- set context variable mime
-- When processing non gRPC Web requests, `mime` can be obtained in the context
-- and set to the `Content-Type` of the response
ctx.grpc_web_mime = core.request.header(ctx, "Content-Type")
local method = core.request.get_method()
if method == ALLOW_METHOD_OPTIONS then
return 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
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
end
-- set context variable encoding method
ctx.grpc_web_encoding = encoding
-- 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")
return 400
end
local path = ctx.curr_req_matched[":ext"]
if path:byte(1) ~= core.string.byte("/") then
path = "/" .. path
end
req_set_uri(path)
-- set grpc body
local body, err = core.request.get_body()
if err then
core.log.error("failed to read request body, err: ", err)
return 400
end
if encoding == CONTENT_ENCODING_BASE64 then
body = decode_base64(body)
if not body then
core.log.error("failed to decode request body")
return 400
end
end
-- set grpc content-type
core.request.set_header(ctx, "Content-Type", DEFAULT_PROXY_CONTENT_TYPE)
-- set grpc body
req_set_body_data(body)
end
function _M.header_filter(conf, ctx)
local method = core.request.get_method()
if method == ALLOW_METHOD_OPTIONS then
core.response.set_header("Access-Control-Allow-Methods", DEFAULT_CORS_ALLOW_METHODS)
core.response.set_header("Access-Control-Allow-Headers", DEFAULT_CORS_ALLOW_HEADERS)
end
core.response.set_header("Access-Control-Allow-Origin", DEFAULT_CORS_ALLOW_ORIGIN)
core.response.set_header("Content-Type", ctx.grpc_web_mime)
end
function _M.body_filter(conf, ctx)
-- 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
-- https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-WEB.md
-- https://github.com/grpc/grpc-web/blob/master/doc/browser-features.md#cors-support
if not ctx.grpc_web_mime then
return
end
if ctx.grpc_web_encoding == CONTENT_ENCODING_BASE64 then
local chunk = ngx_arg[1]
chunk = encode_base64(chunk)
ngx_arg[1] = chunk
end
end
return _M