From 270d563633a363866ab37ef10eb215d203e0d9fa Mon Sep 17 00:00:00 2001 From: nic-chen Date: Mon, 12 Aug 2019 13:56:57 +0800 Subject: [PATCH 01/50] feature: grpc proxy --test --- conf/config.yaml | 1 + conf/nginx.conf | 24 ++++--- lua/apisix.lua | 3 + lua/apisix/plugins/grpc-proxy.lua | 69 ++++++++++++++++++ lua/apisix/plugins/grpc-proxy/proto.lua | 43 +++++++++++ lua/apisix/plugins/grpc-proxy/request.lua | 38 ++++++++++ lua/apisix/plugins/grpc-proxy/response.lua | 42 +++++++++++ lua/apisix/plugins/grpc-proxy/util.lua | 83 ++++++++++++++++++++++ rockspec/apisix-dev-1.0-0.rockspec | 1 + 9 files changed, 296 insertions(+), 8 deletions(-) create mode 100644 lua/apisix/plugins/grpc-proxy.lua create mode 100644 lua/apisix/plugins/grpc-proxy/proto.lua create mode 100644 lua/apisix/plugins/grpc-proxy/request.lua create mode 100644 lua/apisix/plugins/grpc-proxy/response.lua create mode 100644 lua/apisix/plugins/grpc-proxy/util.lua diff --git a/conf/config.yaml b/conf/config.yaml index 888a61a02a66..75ca77c198e2 100644 --- a/conf/config.yaml +++ b/conf/config.yaml @@ -35,3 +35,4 @@ plugins: # plugin list - jwt-auth - zipkin - ip-restriction + - grpc-proxy diff --git a/conf/nginx.conf b/conf/nginx.conf index 1c536a465fad..cc2f38de352f 100644 --- a/conf/nginx.conf +++ b/conf/nginx.conf @@ -128,19 +128,27 @@ http { apisix.http_access_phase() } - proxy_http_version 1.1; - proxy_set_header Host $upstream_host; - proxy_set_header Upgrade $upstream_upgrade; - proxy_set_header Connection $upstream_connection; - proxy_set_header X-Real-IP $remote_addr; - proxy_pass_header Server; - proxy_pass_header Date; - proxy_pass $upstream_scheme://apisix_backend$upstream_uri; + #proxy_http_version 1.1; + #proxy_set_header Host $upstream_host; + #proxy_set_header Upgrade $upstream_upgrade; + #proxy_set_header Connection $upstream_connection; + #proxy_set_header X-Real-IP $remote_addr; + #proxy_pass_header Server; + #proxy_pass_header Date; + #proxy_pass $upstream_scheme://apisix_backend$upstream_uri; + + grpc_set_header Content-Type application/grpc; + grpc_pass apisix_backend; header_filter_by_lua_block { apisix.http_header_filter_phase() } + body_filter_by_lua_block { + apisix.http_body_filter_phase() + } + + log_by_lua_block { apisix.http_log_phase() } diff --git a/lua/apisix.lua b/lua/apisix.lua index ecb6533b5d03..b0c92e0986bc 100644 --- a/lua/apisix.lua +++ b/lua/apisix.lua @@ -202,6 +202,9 @@ function _M.http_header_filter_phase() run_plugin("header_filter") end +function _M.http_body_filter_phase() + run_plugin("body_filter") +end function _M.http_log_phase() local api_ctx = run_plugin("log") diff --git a/lua/apisix/plugins/grpc-proxy.lua b/lua/apisix/plugins/grpc-proxy.lua new file mode 100644 index 000000000000..aceb2d6e7ef8 --- /dev/null +++ b/lua/apisix/plugins/grpc-proxy.lua @@ -0,0 +1,69 @@ +local core = require("apisix.core") +local plugin_name = "grpc-proxy" +local proto = require("apisix.plugins.grpc-proxy.proto") +local request = require("apisix.plugins.grpc-proxy.request") +local response = require("apisix.plugins.grpc-proxy.response") + +local schema = { + type = "object", + additionalProperties = false +} + + +local _M = { + version = 0.1, + priority = 500, + name = plugin_name, + schema = schema, +} + + +function _M.check_schema(conf) + local ok, err = core.schema.check(schema, conf) + if not ok then + return false, err + end + + return true +end + + +function _M.access() + local proto_id = 1 + local p, err = proto.new(proto_id) + if err then + ngx.log(ngx.ERR, ("proto load error: %s"):format(err)) + return + end + local req = request.new(p) + err = req:transform("helloworld.Greeter", "SayHello") + if err then + ngx.log(ngx.ERR, ("trasnform request error: %s"):format(err)) + return + end + +end + + +function _M.header_filter(conf, ctx) + ngx.header["Content-Type"] = "application/json" +end + + +function _M.body_filter(conf, ctx) + local proto_id = 1 + local p, err = proto.new(proto_id) + if err then + ngx.log(ngx.ERR, ("proto load error: %s"):format(err)) + return + end + local resp = response.new(p) + err = resp:transform("helloworld.Greeter", "SayHello") + if err then + ngx.log(ngx.ERR, ("trasnform response error: %s"):format(err)) + return + end +end + + +return _M diff --git a/lua/apisix/plugins/grpc-proxy/proto.lua b/lua/apisix/plugins/grpc-proxy/proto.lua new file mode 100644 index 000000000000..d25ca4124e32 --- /dev/null +++ b/lua/apisix/plugins/grpc-proxy/proto.lua @@ -0,0 +1,43 @@ +local protoc = require("protoc") +local util = require("apisix.plugins.grpc-proxy.util") + +local _M = {} + +_M.new = function(proto_id) + local _p = protoc.new() + --todo read proto content from etcd by id + local ppp = [[ + syntax = "proto3"; + + option java_multiple_files = true; + option java_package = "io.grpc.examples.helloworld"; + option java_outer_classname = "HelloWorldProto"; + + package helloworld; + + // The greeting service definition. + service Greeter { + // Sends a greeting + rpc SayHello (HelloRequest) returns (HelloReply) {} + } + + // The request message containing the user's name. + message HelloRequest { + string name = 1; + } + + // The response message containing the greetings + message HelloReply { + string message = 1; + } ]] + + _p:load(ppp) + + local instance = {} + instance.get_loaded_proto = function() + return _p.loaded + end + return instance +end + +return _M diff --git a/lua/apisix/plugins/grpc-proxy/request.lua b/lua/apisix/plugins/grpc-proxy/request.lua new file mode 100644 index 000000000000..594012a6e832 --- /dev/null +++ b/lua/apisix/plugins/grpc-proxy/request.lua @@ -0,0 +1,38 @@ +local pb = require("pb") +local bit = require("bit") +local util = require("apisix.plugins.grpc-proxy.util") + +local _M = {} + +_M.new = function(proto) + local instance = {} + instance.transform = function(self, service, method, default_values) + local m = util.find_method(proto, service, method) + if not m then + return ("1.Undefined service method: %s/%s end."):format(service, method) + end + + ngx.req.read_body() + local encoded = pb.encode(m.input_type, util.map_message(m.input_type, default_values or {})) + local size = string.len(encoded) + local prefix = { + string.char(0), + string.char(bit.band(bit.rshift(size, 24), 0xFF)), + string.char(bit.band(bit.rshift(size, 16), 0xFF)), + string.char(bit.band(bit.rshift(size, 8), 0xFF)), + string.char(bit.band(size, 0xFF)) + } + local message = table.concat(prefix, "") .. encoded + + ngx.req.set_method(ngx.HTTP_POST) + ngx.req.set_uri(("/%s/%s"):format(service, method), false) + ngx.req.set_uri_args({}) + ngx.req.init_body(string.len(message)) + ngx.req.set_body_data(message) + return nil + end + + return instance +end + +return _M diff --git a/lua/apisix/plugins/grpc-proxy/response.lua b/lua/apisix/plugins/grpc-proxy/response.lua new file mode 100644 index 000000000000..1d5f7372f9b0 --- /dev/null +++ b/lua/apisix/plugins/grpc-proxy/response.lua @@ -0,0 +1,42 @@ +local pb = require("pb") +local json = require("cjson") +local util = require("apisix.plugins.grpc-proxy.util") + +local _M = {} + +_M.new = function(proto) + local instance = {} + instance.transform = function(self, service, method) + local m = util.find_method(proto, service, method) + if not m then + return ("2.Undefined service method: %s/%s end."):format(service, method) + end + + local chunk, eof = ngx.arg[1], ngx.arg[2] + local buffered = ngx.ctx.buffered + if not buffered then + buffered = {} + ngx.ctx.buffered = buffered + end + if chunk ~= "" then + buffered[#buffered + 1] = chunk + ngx.arg[1] = nil + end + + if eof then + ngx.ctx.buffered = nil + local buffer = table.concat(buffered) + if not ngx.req.get_headers()["X-Grpc-Web"] then + buffer = string.sub(buffer, 6) + end + + local decoded = pb.decode(m.output_type, buffer) + local response = json.encode(decoded) + ngx.arg[1] = response + end + end + + return instance +end + +return _M diff --git a/lua/apisix/plugins/grpc-proxy/util.lua b/lua/apisix/plugins/grpc-proxy/util.lua new file mode 100644 index 000000000000..810280180555 --- /dev/null +++ b/lua/apisix/plugins/grpc-proxy/util.lua @@ -0,0 +1,83 @@ +local pb = require("pb") +local json +if not os.getenv("LUAUNIT") then + json = require("cjson") +end + +local _M = {} + +_M.file_exists = function(file) + local fp = io.open(file, "r") + if fp then + fp:close() + return true + end + return false +end + +_M.find_method = function(proto, service, method) + local protos = proto.get_loaded_proto() + for k, loaded in pairs(protos) do + if type(loaded) == "boolean" then + ngx.log(ngx.ERR, k) + end + local package = loaded.package + for _, s in ipairs(loaded.service or {}) do + if ("%s.%s"):format(package, s.name) == service then + for _, m in ipairs(s.method) do + if m.name == method then + return m + end + end + end + end + end + + return nil +end + +local function get_from_request(name, kind) + local request_table + if ngx.req.get_method() == "POST" then + if string.find(ngx.req.get_headers()["Content-Type"] or "", "application/json") then + request_table = json.decode(ngx.req.get_body_data()) + else + request_table = ngx.req.get_post_args() + end + else + request_table = ngx.req.get_uri_args() + end + local prefix = kind:sub(1, 3) + if prefix == "str" then + return request_table[name] or nul + elseif prefix == "int" then + if request_table[name] then + return tonumber(request_table[name]) + else + return nil + end + end + return nil +end + +_M.map_message = function(field, default_values) + if not pb.type(field) then + return nil, ("Field %s is not defined"):format(field) + end + + local request = {} + for name, _, field_type in pb.fields(field) do + if field_type:sub(1, 1) == "." then + sub, err = _M.map_message(field_type, default_values) + if err then + return nil, err + end + request[name] = sub + else + request[name] = get_from_request(name, field_type) or default_values[name] or nil + end + end + return request, nil +end + +return _M diff --git a/rockspec/apisix-dev-1.0-0.rockspec b/rockspec/apisix-dev-1.0-0.rockspec index 7cda3a8f3c47..488374e9709b 100644 --- a/rockspec/apisix-dev-1.0-0.rockspec +++ b/rockspec/apisix-dev-1.0-0.rockspec @@ -28,6 +28,7 @@ dependencies = { "opentracing-openresty = 0.1", "lua-resty-radixtree = 0.4", "lua-resty-iputils = 0.3.0-1", + "lua-protobuf = 0.3.1", } build = { From 2184dc6f3035f8287e7f2c414bf7196031a8ba35 Mon Sep 17 00:00:00 2001 From: nic-chen Date: Tue, 13 Aug 2019 22:26:59 +0800 Subject: [PATCH 02/50] feat compat grpc pass --- conf/nginx.conf | 41 ++++++++++++++++-------- lua/apisix.lua | 65 ++++++++++++++++++++++++++++++++++++++ lua/apisix/core/schema.lua | 3 ++ 3 files changed, 96 insertions(+), 13 deletions(-) diff --git a/conf/nginx.conf b/conf/nginx.conf index cc2f38de352f..3e4c59ba1c6c 100644 --- a/conf/nginx.conf +++ b/conf/nginx.conf @@ -128,30 +128,45 @@ http { apisix.http_access_phase() } - #proxy_http_version 1.1; - #proxy_set_header Host $upstream_host; - #proxy_set_header Upgrade $upstream_upgrade; - #proxy_set_header Connection $upstream_connection; - #proxy_set_header X-Real-IP $remote_addr; - #proxy_pass_header Server; - #proxy_pass_header Date; - #proxy_pass $upstream_scheme://apisix_backend$upstream_uri; - - grpc_set_header Content-Type application/grpc; - grpc_pass apisix_backend; + proxy_http_version 1.1; + proxy_set_header Host $upstream_host; + proxy_set_header Upgrade $upstream_upgrade; + proxy_set_header Connection $upstream_connection; + proxy_set_header X-Real-IP $remote_addr; + proxy_pass_header Server; + proxy_pass_header Date; + proxy_pass $upstream_scheme://apisix_backend$upstream_uri; header_filter_by_lua_block { apisix.http_header_filter_phase() } - body_filter_by_lua_block { - apisix.http_body_filter_phase() + log_by_lua_block { + apisix.http_log_phase() + } + } + + location @grpc_pass { + + access_by_lua_block { + apisix.grpc_access_phase() } + grpc_set_header Content-Type application/grpc; + grpc_pass apisix_backend; + + header_filter_by_lua_block { + apisix.http_header_filter_phase() + } + + body_filter_by_lua_block { + apisix.http_body_filter_phase() + } log_by_lua_block { apisix.http_log_phase() } } + } } diff --git a/lua/apisix.lua b/lua/apisix.lua index b0c92e0986bc..9c91fff37c5a 100644 --- a/lua/apisix.lua +++ b/lua/apisix.lua @@ -159,6 +159,11 @@ function _M.http_access_phase() return core.response.exit(404) end + -- + if route.value.backend_protocol=="gprc" then + return ngx.exec("@grpc_pass") + end + if route.value.service_id then -- core.log.info("matched route: ", core.json.delay_encode(route.value)) local service = service_fetch(route.value.service_id) @@ -197,6 +202,66 @@ function _M.http_access_phase() run_plugin("access", plugins, api_ctx) end +function _M.grpc_access_phase() + local ngx_ctx = ngx.ctx + local api_ctx = ngx_ctx.api_ctx + + if api_ctx == nil then + api_ctx = core.tablepool.fetch("api_ctx", 0, 32) + ngx_ctx.api_ctx = api_ctx + end + + core.ctx.set_vars_meta(api_ctx) + + router.router_http.match(api_ctx) + + core.log.info("route: ", + core.json.delay_encode(api_ctx.matched_route, true)) + + local route = api_ctx.matched_route + if not route then + return core.response.exit(404) + end + + if route.value.service_id then + -- core.log.info("matched route: ", core.json.delay_encode(route.value)) + local service = service_fetch(route.value.service_id) + if not service then + core.log.error("failed to fetch service configuration by ", + "id: ", route.value.service_id) + return core.response.exit(404) + end + + local changed + route, changed = plugin.merge_service_route(service, route) + api_ctx.matched_route = route + + if changed then + api_ctx.conf_type = "route&service" + api_ctx.conf_version = route.modifiedIndex .. "&" + .. service.modifiedIndex + api_ctx.conf_id = route.value.id .. "&" + .. service.value.id + else + api_ctx.conf_type = "service" + api_ctx.conf_version = service.modifiedIndex + api_ctx.conf_id = service.value.id + end + + else + api_ctx.conf_type = "route" + api_ctx.conf_version = route.modifiedIndex + api_ctx.conf_id = route.value.id + end + + local plugins = core.tablepool.fetch("plugins", 32, 0) + api_ctx.plugins = plugin.filter(route, plugins) + + run_plugin("rewrite", plugins, api_ctx) + run_plugin("access", plugins, api_ctx) +end + + function _M.http_header_filter_phase() run_plugin("header_filter") diff --git a/lua/apisix/core/schema.lua b/lua/apisix/core/schema.lua index efcea4c764af..1fd663c2da74 100644 --- a/lua/apisix/core/schema.lua +++ b/lua/apisix/core/schema.lua @@ -257,6 +257,9 @@ _M.route = [[{ }, "uniqueItems": true }, + "backend_protocol": { + "type": "string" + }, "desc": {"type": "string", "maxLength": 256}, "plugins": ]] .. json.encode(plugins_schema) .. [[, "upstream": ]] .. json.encode(upstream_schema) .. [[, From ac69ad2a4cd9b459a3c274dafe13891e0e0ec7f5 Mon Sep 17 00:00:00 2001 From: nic-chen Date: Wed, 14 Aug 2019 09:27:15 +0800 Subject: [PATCH 03/50] feat update nginx conf template --- bin/apisix | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/bin/apisix b/bin/apisix index c8920860a438..f5dca22fd70f 100755 --- a/bin/apisix +++ b/bin/apisix @@ -231,10 +231,37 @@ http { apisix.http_header_filter_phase() } + body_filter_by_lua_block { + apisix.http_body_filter_phase() + } + + log_by_lua_block { + apisix.http_log_phase() + } + } + + location @grpc_pass { + + access_by_lua_block { + apisix.grpc_access_phase() + } + + grpc_set_header Content-Type application/grpc; + grpc_pass apisix_backend; + + header_filter_by_lua_block { + apisix.http_header_filter_phase() + } + + body_filter_by_lua_block { + apisix.http_body_filter_phase() + } + log_by_lua_block { apisix.http_log_phase() } } + } } ]=] From 0901ba1aa5424c6c42eb25baf84df64caf2272fd Mon Sep 17 00:00:00 2001 From: nic-chen Date: Thu, 15 Aug 2019 17:23:27 +0800 Subject: [PATCH 04/50] feat grpc proto admin && fetch proto from etcd --- lua/apisix.lua | 4 +- lua/apisix/admin/init.lua | 1 + lua/apisix/admin/proto.lua | 107 ++++++++++++++++++++++++ lua/apisix/core/schema.lua | 17 +++- lua/apisix/plugins/grpc-proxy.lua | 22 +++-- lua/apisix/plugins/grpc-proxy/proto.lua | 40 ++++----- lua/apisix/plugins/grpc-proxy/util.lua | 7 +- lua/apisix/proto/helloworld.proto | 37 ++++++++ 8 files changed, 200 insertions(+), 35 deletions(-) create mode 100644 lua/apisix/admin/proto.lua create mode 100644 lua/apisix/proto/helloworld.proto diff --git a/lua/apisix.lua b/lua/apisix.lua index 9c91fff37c5a..22f4f406eaa9 100644 --- a/lua/apisix.lua +++ b/lua/apisix.lua @@ -159,8 +159,10 @@ function _M.http_access_phase() return core.response.exit(404) end + core.log.error("service_protocol:", route.value.service_protocol) + -- - if route.value.backend_protocol=="gprc" then + if route.value.service_protocol=="gprc" then return ngx.exec("@grpc_pass") end diff --git a/lua/apisix/admin/init.lua b/lua/apisix/admin/init.lua index da8e82cd3fad..c83168b2f0d8 100644 --- a/lua/apisix/admin/init.lua +++ b/lua/apisix/admin/init.lua @@ -17,6 +17,7 @@ local resources = { schema = require("apisix.admin.schema"), ssl = require("apisix.admin.ssl"), plugins = require("apisix.admin.plugins"), + proto = require("apisix.admin.proto"), } diff --git a/lua/apisix/admin/proto.lua b/lua/apisix/admin/proto.lua new file mode 100644 index 000000000000..7eadc51ab9c0 --- /dev/null +++ b/lua/apisix/admin/proto.lua @@ -0,0 +1,107 @@ +local core = require("apisix.core") +local tostring = tostring + + +local _M = { + version = 0.1, +} + + +local function check_conf(id, conf, need_id) + if not conf then + return nil, {error_msg = "missing configurations"} + end + + id = id or conf.id + if need_id and not id then + return nil, {error_msg = "missing proto id"} + end + + if not need_id and id then + return nil, {error_msg = "wrong proto id, do not need it"} + end + + if need_id and conf.id and tostring(conf.id) ~= tostring(id) then + return nil, {error_msg = "wrong proto id"} + end + + core.log.info("schema: ", core.json.delay_encode(core.schema.proto)) + core.log.info("conf : ", core.json.delay_encode(conf)) + local ok, err = core.schema.check(core.schema.proto, conf) + if not ok then + return nil, {error_msg = "invalid configuration: " .. err} + end + + return need_id and id or true +end + + +function _M.put(id, conf) + local id, err = check_conf(id, conf, true) + if not id then + return 400, err + end + + local key = "/proto/" .. id + local res, err = core.etcd.set(key, conf) + if not res then + core.log.error("failed to put proto[", key, "]: ", err) + return 500, {error_msg = err} + end + + return res.status, res.body +end + + +function _M.get(id) + local key = "/proto" + if id then + key = key .. "/" .. id + end + + local res, err = core.etcd.get(key) + if not res then + core.log.error("failed to get proto[", key, "]: ", err) + return 500, {error_msg = err} + end + + return res.status, res.body +end + + +function _M.post(id, conf) + local id, err = check_conf(id, conf, false) + if not id then + return 400, err + end + + local key = "/proto" + -- core.log.info("key: ", key) + local res, err = core.etcd.push("/proto", conf) + if not res then + core.log.error("failed to post proto[", key, "]: ", err) + return 500, {error_msg = err} + end + + return res.status, res.body +end + + +function _M.delete(id) + if not id then + return 400, {error_msg = "missing proto id"} + end + + local key = "/proto/" .. id + -- core.log.info("key: ", key) + local res, err = core.etcd.delete(key) + if not res then + core.log.error("failed to delete proto[", key, "]: ", err) + return 500, {error_msg = err} + end + + return res.status, res.body +end + + +return _M diff --git a/lua/apisix/core/schema.lua b/lua/apisix/core/schema.lua index 1fd663c2da74..0ccc5ec2364d 100644 --- a/lua/apisix/core/schema.lua +++ b/lua/apisix/core/schema.lua @@ -257,8 +257,9 @@ _M.route = [[{ }, "uniqueItems": true }, - "backend_protocol": { - "type": "string" + "service_protocol": { + "type": "string", + "maxLength": 20, }, "desc": {"type": "string", "maxLength": 256}, "plugins": ]] .. json.encode(plugins_schema) .. [[, @@ -348,4 +349,16 @@ _M.ssl = { } +_M.proto = { + type = "object", + properties = { + content = { + type = "string", minLength = 1, maxLength = 4096 + } + }, + required = {"content"}, + additionalProperties = false, +} + + return _M diff --git a/lua/apisix/plugins/grpc-proxy.lua b/lua/apisix/plugins/grpc-proxy.lua index aceb2d6e7ef8..c56bf5d3b988 100644 --- a/lua/apisix/plugins/grpc-proxy.lua +++ b/lua/apisix/plugins/grpc-proxy.lua @@ -6,7 +6,7 @@ local response = require("apisix.plugins.grpc-proxy.response") local schema = { type = "object", - additionalProperties = false + additionalProperties = true } @@ -28,15 +28,20 @@ function _M.check_schema(conf) end -function _M.access() - local proto_id = 1 +function _M.access(conf, ctx) + local proto_id = conf.proto_id + if not proto_id then + ngx.log(ngx.ERR, ("proto id miss: %s"):format(err)) + return + end + local p, err = proto.new(proto_id) if err then ngx.log(ngx.ERR, ("proto load error: %s"):format(err)) return end local req = request.new(p) - err = req:transform("helloworld.Greeter", "SayHello") + err = req:transform(conf.service, conf.method) if err then ngx.log(ngx.ERR, ("trasnform request error: %s"):format(err)) return @@ -51,14 +56,19 @@ end function _M.body_filter(conf, ctx) - local proto_id = 1 + local proto_id = conf.proto_id + if not proto_id then + ngx.log(ngx.ERR, ("proto id miss: %s"):format(err)) + return + end + local p, err = proto.new(proto_id) if err then ngx.log(ngx.ERR, ("proto load error: %s"):format(err)) return end local resp = response.new(p) - err = resp:transform("helloworld.Greeter", "SayHello") + err = resp:transform(conf.service, conf.method) if err then ngx.log(ngx.ERR, ("trasnform response error: %s"):format(err)) return diff --git a/lua/apisix/plugins/grpc-proxy/proto.lua b/lua/apisix/plugins/grpc-proxy/proto.lua index d25ca4124e32..fcbfc64820fe 100644 --- a/lua/apisix/plugins/grpc-proxy/proto.lua +++ b/lua/apisix/plugins/grpc-proxy/proto.lua @@ -1,3 +1,4 @@ +local core = require("apisix.core") local protoc = require("protoc") local util = require("apisix.plugins.grpc-proxy.util") @@ -5,33 +6,26 @@ local _M = {} _M.new = function(proto_id) local _p = protoc.new() - --todo read proto content from etcd by id - local ppp = [[ - syntax = "proto3"; - option java_multiple_files = true; - option java_package = "io.grpc.examples.helloworld"; - option java_outer_classname = "HelloWorldProto"; - - package helloworld; - - // The greeting service definition. - service Greeter { - // Sends a greeting - rpc SayHello (HelloRequest) returns (HelloReply) {} - } + local err + proto_etcd, err = core.config.new("/proto", { + automatic = true, + item_schema = core.schema.proto + }) + if not proto_etcd then + ngx.log(ngx.ERR, "failed to create etcd instance for fetching proto:" .. err) + return + end - // The request message containing the user's name. - message HelloRequest { - string name = 1; - } + local proto_obj = proto_etcd:get(tostring(proto_id)) + if not proto_obj then + ngx.log(ngx.ERR, "failed to find proto by id: " .. proto_id) + return + end - // The response message containing the greetings - message HelloReply { - string message = 1; - } ]] + ngx.log(ngx.ERR, "proto content:" .. proto_obj.value.content) - _p:load(ppp) + _p:load(proto_obj.value.content) local instance = {} instance.get_loaded_proto = function() diff --git a/lua/apisix/plugins/grpc-proxy/util.lua b/lua/apisix/plugins/grpc-proxy/util.lua index 810280180555..a6062bd1817c 100644 --- a/lua/apisix/plugins/grpc-proxy/util.lua +++ b/lua/apisix/plugins/grpc-proxy/util.lua @@ -18,11 +18,12 @@ end _M.find_method = function(proto, service, method) local protos = proto.get_loaded_proto() for k, loaded in pairs(protos) do - if type(loaded) == "boolean" then - ngx.log(ngx.ERR, k) - end + + ngx.log(ngx.ERR, "key:" .. k) + local package = loaded.package for _, s in ipairs(loaded.service or {}) do + ngx.log(ngx.ERR, "pkg:" .. package .. "name:"..s.name) if ("%s.%s"):format(package, s.name) == service then for _, m in ipairs(s.method) do if m.name == method then diff --git a/lua/apisix/proto/helloworld.proto b/lua/apisix/proto/helloworld.proto new file mode 100644 index 000000000000..8de5d08ef452 --- /dev/null +++ b/lua/apisix/proto/helloworld.proto @@ -0,0 +1,37 @@ +// Copyright 2015 gRPC authors. +// +// Licensed 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. + +syntax = "proto3"; + +option java_multiple_files = true; +option java_package = "io.grpc.examples.helloworld"; +option java_outer_classname = "HelloWorldProto"; + +package helloworld; + +// The greeting service definition. +service Greeter { + // Sends a greeting + rpc SayHello (HelloRequest) returns (HelloReply) {} +} + +// The request message containing the user's name. +message HelloRequest { + string name = 1; +} + +// The response message containing the greetings +message HelloReply { + string message = 1; +} \ No newline at end of file From 19a880c736eb8b99a0675afba532f93f1c2aa10a Mon Sep 17 00:00:00 2001 From: nic-chen Date: Thu, 15 Aug 2019 18:12:29 +0800 Subject: [PATCH 05/50] feat fetch proto --- lua/apisix/plugins/grpc-proxy/proto.lua | 30 +++++++++++++++---------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/lua/apisix/plugins/grpc-proxy/proto.lua b/lua/apisix/plugins/grpc-proxy/proto.lua index fcbfc64820fe..8c2c82c97e1d 100644 --- a/lua/apisix/plugins/grpc-proxy/proto.lua +++ b/lua/apisix/plugins/grpc-proxy/proto.lua @@ -2,30 +2,36 @@ local core = require("apisix.core") local protoc = require("protoc") local util = require("apisix.plugins.grpc-proxy.util") + local _M = {} _M.new = function(proto_id) local _p = protoc.new() - local err - proto_etcd, err = core.config.new("/proto", { - automatic = true, - item_schema = core.schema.proto - }) - if not proto_etcd then - ngx.log(ngx.ERR, "failed to create etcd instance for fetching proto:" .. err) - return - end + local key = "/proto/" .. proto_id + local res, err = core.etcd.get(key) + + local proto_obj = res.body.node.value + + -- local err + -- proto_etcd, err = core.config.new("/proto", { + -- automatic = true, + -- item_schema = core.schema.proto + -- }) + -- if not proto_etcd then + -- ngx.log(ngx.ERR, "failed to create etcd instance for fetching proto:" .. err) + -- return + -- end - local proto_obj = proto_etcd:get(tostring(proto_id)) + --local proto_obj = proto_etcd:get(tostring(proto_id)) if not proto_obj then ngx.log(ngx.ERR, "failed to find proto by id: " .. proto_id) return end - ngx.log(ngx.ERR, "proto content:" .. proto_obj.value.content) + ngx.log(ngx.ERR, "proto content:" .. proto_obj.content) - _p:load(proto_obj.value.content) + _p:load(proto_obj.content) local instance = {} instance.get_loaded_proto = function() From 87b08ed3afd95d6db1bbbfb80f3f65ce90143598 Mon Sep 17 00:00:00 2001 From: nic-chen Date: Fri, 16 Aug 2019 13:49:47 +0800 Subject: [PATCH 06/50] fix remove debug log --- lua/apisix.lua | 4 +--- lua/apisix/plugins/grpc-proxy/proto.lua | 1 - lua/apisix/plugins/grpc-proxy/util.lua | 4 ---- 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/lua/apisix.lua b/lua/apisix.lua index 22f4f406eaa9..69b1607ed881 100644 --- a/lua/apisix.lua +++ b/lua/apisix.lua @@ -159,10 +159,8 @@ function _M.http_access_phase() return core.response.exit(404) end - core.log.error("service_protocol:", route.value.service_protocol) - -- - if route.value.service_protocol=="gprc" then + if route.value.service_protocol=="grpc" then return ngx.exec("@grpc_pass") end diff --git a/lua/apisix/plugins/grpc-proxy/proto.lua b/lua/apisix/plugins/grpc-proxy/proto.lua index 8c2c82c97e1d..8308cbd6ec7d 100644 --- a/lua/apisix/plugins/grpc-proxy/proto.lua +++ b/lua/apisix/plugins/grpc-proxy/proto.lua @@ -29,7 +29,6 @@ _M.new = function(proto_id) return end - ngx.log(ngx.ERR, "proto content:" .. proto_obj.content) _p:load(proto_obj.content) diff --git a/lua/apisix/plugins/grpc-proxy/util.lua b/lua/apisix/plugins/grpc-proxy/util.lua index a6062bd1817c..e955a9540d9b 100644 --- a/lua/apisix/plugins/grpc-proxy/util.lua +++ b/lua/apisix/plugins/grpc-proxy/util.lua @@ -18,12 +18,8 @@ end _M.find_method = function(proto, service, method) local protos = proto.get_loaded_proto() for k, loaded in pairs(protos) do - - ngx.log(ngx.ERR, "key:" .. k) - local package = loaded.package for _, s in ipairs(loaded.service or {}) do - ngx.log(ngx.ERR, "pkg:" .. package .. "name:"..s.name) if ("%s.%s"):format(package, s.name) == service then for _, m in ipairs(s.method) do if m.name == method then From ab6141d029a1c106b51a764bfa92d11849ddf45c Mon Sep 17 00:00:00 2001 From: nic-chen Date: Fri, 16 Aug 2019 17:18:02 +0800 Subject: [PATCH 07/50] feat fetch proto from etcd --- lua/apisix/http/router.lua | 4 + lua/apisix/plugins/grpc-proxy.lua | 38 ++++---- lua/apisix/plugins/grpc-proxy/proto.lua | 71 +++++++++----- lua/apisix/plugins/grpc-proxy/request.lua | 56 +++++------ lua/apisix/plugins/grpc-proxy/response.lua | 54 +++++------ lua/apisix/plugins/grpc-proxy/util.lua | 106 ++++++++++----------- lua/apisix/proto/helloworld.proto | 37 ------- 7 files changed, 177 insertions(+), 189 deletions(-) delete mode 100644 lua/apisix/proto/helloworld.proto diff --git a/lua/apisix/http/router.lua b/lua/apisix/http/router.lua index f6316099527d..e8732eac19d1 100644 --- a/lua/apisix/http/router.lua +++ b/lua/apisix/http/router.lua @@ -22,6 +22,10 @@ function _M.init_worker() local router_ssl = require("apisix.http.router." .. router_ssl_name) router_ssl:init_worker() _M.router_ssl = router_ssl + + local proto = require("apisix.plugins.grpc-proxy.proto") + proto:init_worker() + end diff --git a/lua/apisix/plugins/grpc-proxy.lua b/lua/apisix/plugins/grpc-proxy.lua index c56bf5d3b988..6cbe1f538a59 100644 --- a/lua/apisix/plugins/grpc-proxy.lua +++ b/lua/apisix/plugins/grpc-proxy.lua @@ -29,23 +29,23 @@ end function _M.access(conf, ctx) - local proto_id = conf.proto_id - if not proto_id then - ngx.log(ngx.ERR, ("proto id miss: %s"):format(err)) - return - end - - local p, err = proto.new(proto_id) - if err then - ngx.log(ngx.ERR, ("proto load error: %s"):format(err)) - return - end - local req = request.new(p) - err = req:transform(conf.service, conf.method) - if err then - ngx.log(ngx.ERR, ("trasnform request error: %s"):format(err)) - return - end + local proto_id = conf.proto_id + if not proto_id then + ngx.log(ngx.ERR, ("proto id miss: %s"):format(err)) + return + end + + local p, err = proto.new(proto_id) + if err then + ngx.log(ngx.ERR, ("proto load error: %s"):format(err)) + return + end + local req = request.new(p) + err = req:transform(conf.service, conf.method) + if err then + ngx.log(ngx.ERR, ("trasnform request error: %s"):format(err)) + return + end end @@ -58,8 +58,8 @@ end function _M.body_filter(conf, ctx) local proto_id = conf.proto_id if not proto_id then - ngx.log(ngx.ERR, ("proto id miss: %s"):format(err)) - return + ngx.log(ngx.ERR, ("proto id miss: %s"):format(err)) + return end local p, err = proto.new(proto_id) diff --git a/lua/apisix/plugins/grpc-proxy/proto.lua b/lua/apisix/plugins/grpc-proxy/proto.lua index 8308cbd6ec7d..a028965efde3 100644 --- a/lua/apisix/plugins/grpc-proxy/proto.lua +++ b/lua/apisix/plugins/grpc-proxy/proto.lua @@ -1,42 +1,61 @@ local core = require("apisix.core") local protoc = require("protoc") local util = require("apisix.plugins.grpc-proxy.util") +local lrucache = require("apisix.core.lrucache") +local config = require("apisix.core.config_etcd") +local schema = require("apisix.core.schema") +local protos -local _M = {} +local function protos_arrange() + local result = {} -_M.new = function(proto_id) - local _p = protoc.new() + if protos.values == nil then + return result + end - local key = "/proto/" .. proto_id - local res, err = core.etcd.get(key) + for _, proto in ipairs(protos.values) do + local id = proto.value.id + result[id] = proto.value.content + end - local proto_obj = res.body.node.value + return result +end - -- local err - -- proto_etcd, err = core.config.new("/proto", { - -- automatic = true, - -- item_schema = core.schema.proto - -- }) - -- if not proto_etcd then - -- ngx.log(ngx.ERR, "failed to create etcd instance for fetching proto:" .. err) - -- return - -- end - --local proto_obj = proto_etcd:get(tostring(proto_id)) - if not proto_obj then - ngx.log(ngx.ERR, "failed to find proto by id: " .. proto_id) - return - end +local _M = {} +_M.new = function(proto_id) + local cache = lrucache.global("/proto", protos.conf_version, protos_arrange) + local content = cache[proto_id] + + if not content then + ngx.log(ngx.ERR, "failed to find proto by id: " .. proto_id) + return + end + + local _p = protoc.new() + _p:load(content) + + local instance = {} + instance.get_loaded_proto = function() + return _p.loaded + end + return instance +end - _p:load(proto_obj.content) - local instance = {} - instance.get_loaded_proto = function() - return _p.loaded - end - return instance +_M.init_worker = function() + local err + protos, err = config.new("/proto", + { + automatic = true, + item_schema = schema.proto + }) + if not protos then + ngx.log(ngx.ERR, "failed to create etcd instance for fetching protos: " .. err) + return + end end return _M diff --git a/lua/apisix/plugins/grpc-proxy/request.lua b/lua/apisix/plugins/grpc-proxy/request.lua index 594012a6e832..af1feddff644 100644 --- a/lua/apisix/plugins/grpc-proxy/request.lua +++ b/lua/apisix/plugins/grpc-proxy/request.lua @@ -5,34 +5,36 @@ local util = require("apisix.plugins.grpc-proxy.util") local _M = {} _M.new = function(proto) - local instance = {} - instance.transform = function(self, service, method, default_values) - local m = util.find_method(proto, service, method) - if not m then - return ("1.Undefined service method: %s/%s end."):format(service, method) + local instance = {} + + instance.transform = function(self, service, method, default_values) + local m = util.find_method(proto, service, method) + if not m then + return ("1.Undefined service method: %s/%s end."):format(service, method) + end + + ngx.req.read_body() + local encoded = pb.encode(m.input_type, util.map_message(m.input_type, default_values or {})) + local size = string.len(encoded) + local prefix = { + string.char(0), + string.char(bit.band(bit.rshift(size, 24), 0xFF)), + string.char(bit.band(bit.rshift(size, 16), 0xFF)), + string.char(bit.band(bit.rshift(size, 8), 0xFF)), + string.char(bit.band(size, 0xFF)) + } + + local message = table.concat(prefix, "") .. encoded + + ngx.req.set_method(ngx.HTTP_POST) + ngx.req.set_uri(("/%s/%s"):format(service, method), false) + ngx.req.set_uri_args({}) + ngx.req.init_body(string.len(message)) + ngx.req.set_body_data(message) + return nil end - ngx.req.read_body() - local encoded = pb.encode(m.input_type, util.map_message(m.input_type, default_values or {})) - local size = string.len(encoded) - local prefix = { - string.char(0), - string.char(bit.band(bit.rshift(size, 24), 0xFF)), - string.char(bit.band(bit.rshift(size, 16), 0xFF)), - string.char(bit.band(bit.rshift(size, 8), 0xFF)), - string.char(bit.band(size, 0xFF)) - } - local message = table.concat(prefix, "") .. encoded - - ngx.req.set_method(ngx.HTTP_POST) - ngx.req.set_uri(("/%s/%s"):format(service, method), false) - ngx.req.set_uri_args({}) - ngx.req.init_body(string.len(message)) - ngx.req.set_body_data(message) - return nil - end - - return instance + return instance end -return _M +return _M \ No newline at end of file diff --git a/lua/apisix/plugins/grpc-proxy/response.lua b/lua/apisix/plugins/grpc-proxy/response.lua index 1d5f7372f9b0..aa69ddfcf69a 100644 --- a/lua/apisix/plugins/grpc-proxy/response.lua +++ b/lua/apisix/plugins/grpc-proxy/response.lua @@ -5,38 +5,38 @@ local util = require("apisix.plugins.grpc-proxy.util") local _M = {} _M.new = function(proto) - local instance = {} - instance.transform = function(self, service, method) - local m = util.find_method(proto, service, method) - if not m then - return ("2.Undefined service method: %s/%s end."):format(service, method) - end + local instance = {} + instance.transform = function(self, service, method) + local m = util.find_method(proto, service, method) + if not m then + return ("2.Undefined service method: %s/%s end."):format(service, method) + end - local chunk, eof = ngx.arg[1], ngx.arg[2] - local buffered = ngx.ctx.buffered - if not buffered then - buffered = {} - ngx.ctx.buffered = buffered - end - if chunk ~= "" then - buffered[#buffered + 1] = chunk - ngx.arg[1] = nil - end + local chunk, eof = ngx.arg[1], ngx.arg[2] + local buffered = ngx.ctx.buffered + if not buffered then + buffered = {} + ngx.ctx.buffered = buffered + end + if chunk ~= "" then + buffered[#buffered + 1] = chunk + ngx.arg[1] = nil + end - if eof then - ngx.ctx.buffered = nil - local buffer = table.concat(buffered) - if not ngx.req.get_headers()["X-Grpc-Web"] then - buffer = string.sub(buffer, 6) - end + if eof then + ngx.ctx.buffered = nil + local buffer = table.concat(buffered) + if not ngx.req.get_headers()["X-Grpc-Web"] then + buffer = string.sub(buffer, 6) + end - local decoded = pb.decode(m.output_type, buffer) - local response = json.encode(decoded) - ngx.arg[1] = response + local decoded = pb.decode(m.output_type, buffer) + local response = json.encode(decoded) + ngx.arg[1] = response + end end - end - return instance + return instance end return _M diff --git a/lua/apisix/plugins/grpc-proxy/util.lua b/lua/apisix/plugins/grpc-proxy/util.lua index e955a9540d9b..118bccef81e3 100644 --- a/lua/apisix/plugins/grpc-proxy/util.lua +++ b/lua/apisix/plugins/grpc-proxy/util.lua @@ -1,80 +1,80 @@ local pb = require("pb") local json if not os.getenv("LUAUNIT") then - json = require("cjson") + json = require("cjson") end local _M = {} _M.file_exists = function(file) - local fp = io.open(file, "r") - if fp then - fp:close() - return true - end - return false + local fp = io.open(file, "r") + if fp then + fp:close() + return true + end + return false end _M.find_method = function(proto, service, method) - local protos = proto.get_loaded_proto() - for k, loaded in pairs(protos) do - local package = loaded.package - for _, s in ipairs(loaded.service or {}) do - if ("%s.%s"):format(package, s.name) == service then - for _, m in ipairs(s.method) do - if m.name == method then - return m - end + local protos = proto.get_loaded_proto() + for k, loaded in pairs(protos) do + local package = loaded.package + for _, s in ipairs(loaded.service or {}) do + if ("%s.%s"):format(package, s.name) == service then + for _, m in ipairs(s.method) do + if m.name == method then + return m + end + end + end end - end end - end - return nil + return nil end local function get_from_request(name, kind) - local request_table - if ngx.req.get_method() == "POST" then - if string.find(ngx.req.get_headers()["Content-Type"] or "", "application/json") then - request_table = json.decode(ngx.req.get_body_data()) + local request_table + if ngx.req.get_method() == "POST" then + if string.find(ngx.req.get_headers()["Content-Type"] or "", "application/json") then + request_table = json.decode(ngx.req.get_body_data()) + else + request_table = ngx.req.get_post_args() + end else - request_table = ngx.req.get_post_args() + request_table = ngx.req.get_uri_args() end - else - request_table = ngx.req.get_uri_args() - end - local prefix = kind:sub(1, 3) - if prefix == "str" then - return request_table[name] or nul - elseif prefix == "int" then - if request_table[name] then - return tonumber(request_table[name]) - else - return nil + local prefix = kind:sub(1, 3) + if prefix == "str" then + return request_table[name] or nul + elseif prefix == "int" then + if request_table[name] then + return tonumber(request_table[name]) + else + return nil + end end - end - return nil + return nil end _M.map_message = function(field, default_values) - if not pb.type(field) then - return nil, ("Field %s is not defined"):format(field) - end + if not pb.type(field) then + return nil, ("Field %s is not defined"):format(field) + end - local request = {} - for name, _, field_type in pb.fields(field) do - if field_type:sub(1, 1) == "." then - sub, err = _M.map_message(field_type, default_values) - if err then - return nil, err - end - request[name] = sub - else - request[name] = get_from_request(name, field_type) or default_values[name] or nil + local request = {} + for name, _, field_type in pb.fields(field) do + if field_type:sub(1, 1) == "." then + sub, err = _M.map_message(field_type, default_values) + if err then + return nil, err + end + request[name] = sub + else + request[name] = get_from_request(name, field_type) or default_values[name] or nil + end end - end - return request, nil + return request, nil end -return _M +return _M \ No newline at end of file diff --git a/lua/apisix/proto/helloworld.proto b/lua/apisix/proto/helloworld.proto deleted file mode 100644 index 8de5d08ef452..000000000000 --- a/lua/apisix/proto/helloworld.proto +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2015 gRPC authors. -// -// Licensed 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. - -syntax = "proto3"; - -option java_multiple_files = true; -option java_package = "io.grpc.examples.helloworld"; -option java_outer_classname = "HelloWorldProto"; - -package helloworld; - -// The greeting service definition. -service Greeter { - // Sends a greeting - rpc SayHello (HelloRequest) returns (HelloReply) {} -} - -// The request message containing the user's name. -message HelloRequest { - string name = 1; -} - -// The response message containing the greetings -message HelloReply { - string message = 1; -} \ No newline at end of file From c6886f700b3189dfce0bb41d01508c152ddc18ec Mon Sep 17 00:00:00 2001 From: nic-chen Date: Fri, 16 Aug 2019 17:58:25 +0800 Subject: [PATCH 08/50] fix warning error --- lua/apisix/plugins/grpc-proxy.lua | 4 ++-- lua/apisix/plugins/grpc-proxy/proto.lua | 4 +--- lua/apisix/plugins/grpc-proxy/util.lua | 3 ++- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/lua/apisix/plugins/grpc-proxy.lua b/lua/apisix/plugins/grpc-proxy.lua index 6cbe1f538a59..bc4c9eba3177 100644 --- a/lua/apisix/plugins/grpc-proxy.lua +++ b/lua/apisix/plugins/grpc-proxy.lua @@ -31,7 +31,7 @@ end function _M.access(conf, ctx) local proto_id = conf.proto_id if not proto_id then - ngx.log(ngx.ERR, ("proto id miss: %s"):format(err)) + ngx.log(ngx.ERR, ("proto id miss: %s"):format(proto_id)) return end @@ -58,7 +58,7 @@ end function _M.body_filter(conf, ctx) local proto_id = conf.proto_id if not proto_id then - ngx.log(ngx.ERR, ("proto id miss: %s"):format(err)) + ngx.log(ngx.ERR, ("proto id miss: %s"):format(proto_id)) return end diff --git a/lua/apisix/plugins/grpc-proxy/proto.lua b/lua/apisix/plugins/grpc-proxy/proto.lua index a028965efde3..743f040559ba 100644 --- a/lua/apisix/plugins/grpc-proxy/proto.lua +++ b/lua/apisix/plugins/grpc-proxy/proto.lua @@ -1,6 +1,4 @@ -local core = require("apisix.core") -local protoc = require("protoc") -local util = require("apisix.plugins.grpc-proxy.util") +local protoc = require("protoc") local lrucache = require("apisix.core.lrucache") local config = require("apisix.core.config_etcd") local schema = require("apisix.core.schema") diff --git a/lua/apisix/plugins/grpc-proxy/util.lua b/lua/apisix/plugins/grpc-proxy/util.lua index 118bccef81e3..03046c34bd1c 100644 --- a/lua/apisix/plugins/grpc-proxy/util.lua +++ b/lua/apisix/plugins/grpc-proxy/util.lua @@ -46,7 +46,7 @@ local function get_from_request(name, kind) end local prefix = kind:sub(1, 3) if prefix == "str" then - return request_table[name] or nul + return request_table[name] or nil elseif prefix == "int" then if request_table[name] then return tonumber(request_table[name]) @@ -63,6 +63,7 @@ _M.map_message = function(field, default_values) end local request = {} + local sub, err for name, _, field_type in pb.fields(field) do if field_type:sub(1, 1) == "." then sub, err = _M.map_message(field_type, default_values) From d4932e406458c5f5b86e50f7e5451b0b23204e55 Mon Sep 17 00:00:00 2001 From: nic-chen Date: Fri, 16 Aug 2019 22:08:39 +0800 Subject: [PATCH 09/50] feat doc --- README.md | 1 + doc/plugins/grpc-proxy-cn.md | 91 ++++++++++++++++++++++++++++++++++++ doc/plugins/grpc-proxy.md | 90 +++++++++++++++++++++++++++++++++++ 3 files changed, 182 insertions(+) create mode 100644 doc/plugins/grpc-proxy-cn.md create mode 100644 doc/plugins/grpc-proxy.md diff --git a/README.md b/README.md index 8ab43d2c8ed9..1ea830341aa3 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,7 @@ For more detailed information, see the [White Paper](https://www.iresty.com/down - **[Limit-concurrency](doc/plugins/limit-conn.md)** - **OpenTracing: [Zipkin](doc/plugins/zipkin.md)** - **Monitoring and Metrics**: [Prometheus](doc/plugins/prometheus.md) +- **[Grpc Proxy](doc/plugins/grpc-proxy.md)**:REST <-> gRPC proxying. - **Custom plugins**: Allows hooking of common phases, such as `rewrite`, `access`, `header filer`, `body filter` and `log`, also allows to hook the `balancer` stage. - **Dashboard**: Built-in dashboard to control APISIX. - **CLI**: start\stop\reload APISIX through the command line. diff --git a/doc/plugins/grpc-proxy-cn.md b/doc/plugins/grpc-proxy-cn.md new file mode 100644 index 000000000000..613bff827641 --- /dev/null +++ b/doc/plugins/grpc-proxy-cn.md @@ -0,0 +1,91 @@ +[中文](grpc-proxy-cn.md) +# grpc-proxy + + +### Proto + +#### 参数 +* `content`: `.proto`文件的内容 + +#### 添加proto + +路径中最后的数字,会被用作proto的id 做唯一标识,比如下面示例的proto `id` 是 `1` : + +```shell +curl http://127.0.0.1:9080/apisix/admin/proto/1 -X PUT -d ' +{ + "content" : "syntax = \"proto3\"; + package helloworld; + service Greeter { + rpc SayHello (HelloRequest) returns (HelloReply) {} + } + message HelloRequest { + string name = 1; + } + message HelloReply { + string message = 1; + }" +}' +``` + +### 参数 +* `proto_id`: `.proto`内容的id. +* `service`: grpc服务名. +* `method`: grpc服务中要调用的方法名. + + + +### 举个例子 + +#### 使用grpc-proxy插件 + +在指定route中,代理grpc服务接口: + +* 注意: 这个route的属性`service_protocal` 必须设置为 `grpc` +* 例子所代理的grpc服务可参考:[grpc_server_example](https://github.com/nic-chen/grpc_server_example) + +```shell +curl http://127.0.0.1:9080/apisix/admin/routes/111 -X PUT -d ' +{ + "methods": ["GET"], + "uri": "/grpctest", + "service_protocol": "grpc", + "plugins": { + "grpc-proxy": { + "proto_id": "1", + "service": "helloworld.Greeter", + "method": "SayHello" + } + }, + "upstream": { + "type": "roundrobin", + "nodes": { + "127.0.0.1:50051": 1 + } + } +}' +``` + + +#### 测试 + +访问上面配置的route: +```shell +curl -i http://127.0.0.1:9080/grpctest +``` + +response: +``` +HTTP/1.1 200 OK +Date: Fri, 16 Aug 2019 11:55:36 GMT +Content-Type: application/json +Transfer-Encoding: chunked +Connection: keep-alive +Server: APISIX web server +Proxy-Connection: keep-alive + +{"message":"Hello world"} +``` + +这意味着代理成功 + diff --git a/doc/plugins/grpc-proxy.md b/doc/plugins/grpc-proxy.md new file mode 100644 index 000000000000..1d7a1a5b9b46 --- /dev/null +++ b/doc/plugins/grpc-proxy.md @@ -0,0 +1,90 @@ +[中文](grpc-proxy-cn.md) +# grpc-proxy + + +### Proto + +#### Parameters +* `content`: `.proto` file's content. + +#### Add a proto + +Here's an example, adding a proto which `id` is `1`: + +```shell +curl http://127.0.0.1:9080/apisix/admin/proto/1 -X PUT -d ' +{ + "content" : "syntax = \"proto3\"; + package helloworld; + service Greeter { + rpc SayHello (HelloRequest) returns (HelloReply) {} + } + message HelloRequest { + string name = 1; + } + message HelloReply { + string message = 1; + }" +}' +``` + +### Parameters +* `proto_id`: `.proto` content id. +* `service`: the grpc service name. +* `method`: the method name of grpc service. + + + +### example + +#### enable plugin +Here's an example, enable the grpc-proxy plugin on the specified route: + +* attention: the route's option `service_protocal` must be `grpc` +* the grpc server example:[grpc_server_example](https://github.com/nic-chen/grpc_server_example) + +```shell +curl http://127.0.0.1:9080/apisix/admin/routes/111 -X PUT -d ' +{ + "methods": ["GET"], + "uri": "/grpctest", + "service_protocol": "grpc", + "plugins": { + "grpc-proxy": { + "proto_id": "1", + "service": "helloworld.Greeter", + "method": "SayHello" + } + }, + "upstream": { + "type": "roundrobin", + "nodes": { + "127.0.0.1:50051": 1 + } + } +}' +``` + + +#### test plugin + +The above configuration proxy : +```shell +curl -i http://127.0.0.1:9080/grpctest +``` + +response: +``` +HTTP/1.1 200 OK +Date: Fri, 16 Aug 2019 11:55:36 GMT +Content-Type: application/json +Transfer-Encoding: chunked +Connection: keep-alive +Server: APISIX web server +Proxy-Connection: keep-alive + +{"message":"Hello world"} +``` + +This means that the proxying is working. + From 6d9f9f234278989e13048b13dab6dff3a1d1f934 Mon Sep 17 00:00:00 2001 From: nic-chen Date: Fri, 16 Aug 2019 22:09:54 +0800 Subject: [PATCH 10/50] fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1ea830341aa3..962a15869c1a 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ For more detailed information, see the [White Paper](https://www.iresty.com/down - **[Limit-concurrency](doc/plugins/limit-conn.md)** - **OpenTracing: [Zipkin](doc/plugins/zipkin.md)** - **Monitoring and Metrics**: [Prometheus](doc/plugins/prometheus.md) -- **[Grpc Proxy](doc/plugins/grpc-proxy.md)**:REST <-> gRPC proxying. +- **[Grpc-Proxy](doc/plugins/grpc-proxy.md)**:REST <-> gRPC proxying. - **Custom plugins**: Allows hooking of common phases, such as `rewrite`, `access`, `header filer`, `body filter` and `log`, also allows to hook the `balancer` stage. - **Dashboard**: Built-in dashboard to control APISIX. - **CLI**: start\stop\reload APISIX through the command line. From 360dc43c5db1b7fdcc1a8089f80ee0ae380f5fae Mon Sep 17 00:00:00 2001 From: nic-chen Date: Sun, 18 Aug 2019 09:45:16 +0800 Subject: [PATCH 11/50] fix Hash Sum mismatch --- .travis/linux_runner.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis/linux_runner.sh b/.travis/linux_runner.sh index 8c0d1e6b518c..a8a7c602ef25 100755 --- a/.travis/linux_runner.sh +++ b/.travis/linux_runner.sh @@ -23,6 +23,7 @@ before_install() { do_install() { wget -qO - https://openresty.org/package/pubkey.gpg | sudo apt-key add - + sudo apt-get -y update --fix-missing sudo apt-get -y install software-properties-common sudo add-apt-repository -y "deb http://openresty.org/package/ubuntu $(lsb_release -sc) main" sudo apt-get update From e8f3d4449afe789e12791d08b09bb8e393d78613 Mon Sep 17 00:00:00 2001 From: nic-chen Date: Sun, 18 Aug 2019 10:50:48 +0800 Subject: [PATCH 12/50] fix local ngx --- lua/apisix/plugins/grpc-proxy.lua | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lua/apisix/plugins/grpc-proxy.lua b/lua/apisix/plugins/grpc-proxy.lua index bc4c9eba3177..112fa2829ae9 100644 --- a/lua/apisix/plugins/grpc-proxy.lua +++ b/lua/apisix/plugins/grpc-proxy.lua @@ -1,8 +1,9 @@ -local core = require("apisix.core") +local ngx = ngx +local core = require("apisix.core") local plugin_name = "grpc-proxy" -local proto = require("apisix.plugins.grpc-proxy.proto") -local request = require("apisix.plugins.grpc-proxy.request") -local response = require("apisix.plugins.grpc-proxy.response") +local proto = require("apisix.plugins.grpc-proxy.proto") +local request = require("apisix.plugins.grpc-proxy.request") +local response = require("apisix.plugins.grpc-proxy.response") local schema = { type = "object", From 496075f51c34808e3fb81783aaf8df608f7c8088 Mon Sep 17 00:00:00 2001 From: nic-chen Date: Mon, 19 Aug 2019 16:17:33 +0800 Subject: [PATCH 13/50] =?UTF-8?q?feat=20=E6=80=A7=E8=83=BD=E4=BC=98?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lua/apisix/plugins/grpc-proxy/proto.lua | 37 ++++++++++++------------- lua/apisix/plugins/grpc-proxy/util.lua | 17 ++++++------ 2 files changed, 26 insertions(+), 28 deletions(-) diff --git a/lua/apisix/plugins/grpc-proxy/proto.lua b/lua/apisix/plugins/grpc-proxy/proto.lua index 743f040559ba..5644699acb19 100644 --- a/lua/apisix/plugins/grpc-proxy/proto.lua +++ b/lua/apisix/plugins/grpc-proxy/proto.lua @@ -5,28 +5,22 @@ local schema = require("apisix.core.schema") local protos -local function protos_arrange() - local result = {} - +local function protos_arrange(proto_id) if protos.values == nil then - return result + return nil end + ngx.log(ngx.ERR, "load proto") + + local content for _, proto in ipairs(protos.values) do local id = proto.value.id - result[id] = proto.value.content + if proto_id==proto.value.id then + content = proto.value.content + break + end end - return result -end - - -local _M = {} - -_M.new = function(proto_id) - local cache = lrucache.global("/proto", protos.conf_version, protos_arrange) - local content = cache[proto_id] - if not content then ngx.log(ngx.ERR, "failed to find proto by id: " .. proto_id) return @@ -35,11 +29,14 @@ _M.new = function(proto_id) local _p = protoc.new() _p:load(content) - local instance = {} - instance.get_loaded_proto = function() - return _p.loaded - end - return instance + return _p.loaded +end + + +local _M = {} + +_M.new = function(proto_id) + return lrucache.global("/protoc", protos.conf_version, protos_arrange, proto_id) end diff --git a/lua/apisix/plugins/grpc-proxy/util.lua b/lua/apisix/plugins/grpc-proxy/util.lua index 03046c34bd1c..4bd8b3f0bde8 100644 --- a/lua/apisix/plugins/grpc-proxy/util.lua +++ b/lua/apisix/plugins/grpc-proxy/util.lua @@ -15,15 +15,16 @@ _M.file_exists = function(file) return false end -_M.find_method = function(proto, service, method) - local protos = proto.get_loaded_proto() +_M.find_method = function(protos, service, method) for k, loaded in pairs(protos) do - local package = loaded.package - for _, s in ipairs(loaded.service or {}) do - if ("%s.%s"):format(package, s.name) == service then - for _, m in ipairs(s.method) do - if m.name == method then - return m + if type(loaded) == 'table' then + local package = loaded.package + for _, s in ipairs(loaded.service or {}) do + if ("%s.%s"):format(package, s.name) == service then + for _, m in ipairs(s.method) do + if m.name == method then + return m + end end end end From 26fbf17ab7a2f9eaa1f25356ea7a2eeced0885ca Mon Sep 17 00:00:00 2001 From: Yuansheng Date: Mon, 19 Aug 2019 16:26:39 +0800 Subject: [PATCH 14/50] bugfix: avoided invalid json schema. --- lua/apisix/core/schema.lua | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lua/apisix/core/schema.lua b/lua/apisix/core/schema.lua index 0ccc5ec2364d..2aeca5df67ee 100644 --- a/lua/apisix/core/schema.lua +++ b/lua/apisix/core/schema.lua @@ -1,4 +1,5 @@ local json = require('rapidjson') +local cjson = require('cjson.safe') local schema_validator = json.SchemaValidator local schema_doc = json.SchemaDocument local json_doc = json.Document @@ -244,7 +245,7 @@ local upstream_schema = { } -_M.route = [[{ +local route = [[{ "type": "object", "properties": { "methods": { @@ -259,7 +260,7 @@ _M.route = [[{ }, "service_protocol": { "type": "string", - "maxLength": 20, + "maxLength": 20 }, "desc": {"type": "string", "maxLength": 256}, "plugins": ]] .. json.encode(plugins_schema) .. [[, @@ -292,6 +293,13 @@ _M.route = [[{ ], "additionalProperties": false }]] +do + local route_t, err = cjson.decode(route) + if err then + error("invalid route: " .. route) + end + _M.route = cjson.encode(route_t) +end _M.service = { From fae43fc7d63056f757675773395060ac01db06c8 Mon Sep 17 00:00:00 2001 From: nic-chen Date: Mon, 19 Aug 2019 16:49:30 +0800 Subject: [PATCH 15/50] =?UTF-8?q?feat=20=E4=B8=8D=E4=BD=BF=E7=94=A8lrucach?= =?UTF-8?q?e=20global?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lua/apisix/plugins/grpc-proxy/proto.lua | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/lua/apisix/plugins/grpc-proxy/proto.lua b/lua/apisix/plugins/grpc-proxy/proto.lua index 5644699acb19..5045fe2e92b6 100644 --- a/lua/apisix/plugins/grpc-proxy/proto.lua +++ b/lua/apisix/plugins/grpc-proxy/proto.lua @@ -1,10 +1,14 @@ -local protoc = require("protoc") -local lrucache = require("apisix.core.lrucache") -local config = require("apisix.core.config_etcd") -local schema = require("apisix.core.schema") +local protoc = require("protoc") +local core = require("apisix.core") +local config = require("apisix.core.config_etcd") +local schema = require("apisix.core.schema") local protos +local lrucache_proto = core.lrucache.new({ + ttl = 300, count = 256 +}) + local function protos_arrange(proto_id) if protos.values == nil then return nil @@ -36,7 +40,8 @@ end local _M = {} _M.new = function(proto_id) - return lrucache.global("/protoc", protos.conf_version, protos_arrange, proto_id) + local key = "/proto"..proto_id + return lrucache_proto(key, protos.conf_version, protos_arrange, proto_id) end From a57c128f478fe3bbf7e59d109c6ecfa0ad7dfa8f Mon Sep 17 00:00:00 2001 From: Yuansheng Date: Mon, 19 Aug 2019 16:39:15 +0800 Subject: [PATCH 16/50] bugfix: plugin initialization is done in `init`. --- lua/apisix/admin/routes.lua | 2 +- lua/apisix/http/router.lua | 5 +--- lua/apisix/plugins/grpc-proxy.lua | 9 +++++-- lua/apisix/plugins/grpc-proxy/proto.lua | 32 ++++++++++++------------- lua/apisix/plugins/grpc-proxy/util.lua | 4 ++-- 5 files changed, 27 insertions(+), 25 deletions(-) diff --git a/lua/apisix/admin/routes.lua b/lua/apisix/admin/routes.lua index 56df1e1fd952..630feaec0d23 100644 --- a/lua/apisix/admin/routes.lua +++ b/lua/apisix/admin/routes.lua @@ -27,7 +27,7 @@ local function check_conf(id, conf, need_id) return nil, {error_msg = "wrong route id"} end - core.log.info("schema: ", core.json.delay_encode(core.schema.route)) + core.log.info("schema: ", core.schema.route) core.log.info("conf : ", core.json.delay_encode(conf)) local ok, err = core.schema.check(core.schema.route, conf) if not ok then diff --git a/lua/apisix/http/router.lua b/lua/apisix/http/router.lua index e8732eac19d1..8e3ef0328f35 100644 --- a/lua/apisix/http/router.lua +++ b/lua/apisix/http/router.lua @@ -10,6 +10,7 @@ function _M.init_worker() local conf = local_conf() local router_http_name = "r3_uri" local router_ssl_name = "r3_sni" + if conf and conf.apisix and conf.apisix.router then router_http_name = conf.apisix.router.http or router_http_name router_ssl_name = conf.apisix.router.ssl or router_ssl_name @@ -22,10 +23,6 @@ function _M.init_worker() local router_ssl = require("apisix.http.router." .. router_ssl_name) router_ssl:init_worker() _M.router_ssl = router_ssl - - local proto = require("apisix.plugins.grpc-proxy.proto") - proto:init_worker() - end diff --git a/lua/apisix/plugins/grpc-proxy.lua b/lua/apisix/plugins/grpc-proxy.lua index 112fa2829ae9..b1ea88485883 100644 --- a/lua/apisix/plugins/grpc-proxy.lua +++ b/lua/apisix/plugins/grpc-proxy.lua @@ -19,6 +19,11 @@ local _M = { } +function _M.init() + proto.init() +end + + function _M.check_schema(conf) local ok, err = core.schema.check(schema, conf) if not ok then @@ -33,7 +38,7 @@ function _M.access(conf, ctx) local proto_id = conf.proto_id if not proto_id then ngx.log(ngx.ERR, ("proto id miss: %s"):format(proto_id)) - return + return end local p, err = proto.new(proto_id) @@ -60,7 +65,7 @@ function _M.body_filter(conf, ctx) local proto_id = conf.proto_id if not proto_id then ngx.log(ngx.ERR, ("proto id miss: %s"):format(proto_id)) - return + return end local p, err = proto.new(proto_id) diff --git a/lua/apisix/plugins/grpc-proxy/proto.lua b/lua/apisix/plugins/grpc-proxy/proto.lua index 5045fe2e92b6..cde27e1bb866 100644 --- a/lua/apisix/plugins/grpc-proxy/proto.lua +++ b/lua/apisix/plugins/grpc-proxy/proto.lua @@ -1,7 +1,7 @@ -local protoc = require("protoc") -local core = require("apisix.core") -local config = require("apisix.core.config_etcd") -local schema = require("apisix.core.schema") +local core = require("apisix.core") +local protoc = require("protoc") +local config = require("apisix.core.config_etcd") +local schema = require("apisix.core.schema") local protos @@ -18,8 +18,7 @@ local function protos_arrange(proto_id) local content for _, proto in ipairs(protos.values) do - local id = proto.value.id - if proto_id==proto.value.id then + if proto_id == proto.value.id then content = proto.value.content break end @@ -27,7 +26,7 @@ local function protos_arrange(proto_id) if not content then ngx.log(ngx.ERR, "failed to find proto by id: " .. proto_id) - return + return end local _p = protoc.new() @@ -37,25 +36,26 @@ local function protos_arrange(proto_id) end -local _M = {} +local _M = {version = 0.1} + _M.new = function(proto_id) - local key = "/proto"..proto_id + local key = "/proto" .. proto_id return lrucache_proto(key, protos.conf_version, protos_arrange, proto_id) end -_M.init_worker = function() +function _M.init() local err - protos, err = config.new("/proto", - { - automatic = true, - item_schema = schema.proto - }) + protos, err = config.new("/proto", { + automatic = true, + item_schema = schema.proto + }) if not protos then - ngx.log(ngx.ERR, "failed to create etcd instance for fetching protos: " .. err) + core.log.error("failed to create etcd instance for fetching protos: " .. err) return end end + return _M diff --git a/lua/apisix/plugins/grpc-proxy/util.lua b/lua/apisix/plugins/grpc-proxy/util.lua index 4bd8b3f0bde8..edb04275a302 100644 --- a/lua/apisix/plugins/grpc-proxy/util.lua +++ b/lua/apisix/plugins/grpc-proxy/util.lua @@ -64,7 +64,7 @@ _M.map_message = function(field, default_values) end local request = {} - local sub, err + local sub, err for name, _, field_type in pb.fields(field) do if field_type:sub(1, 1) == "." then sub, err = _M.map_message(field_type, default_values) @@ -79,4 +79,4 @@ _M.map_message = function(field, default_values) return request, nil end -return _M \ No newline at end of file +return _M From 5e69c16d50ecdbda96fd461839430af968f7cb35 Mon Sep 17 00:00:00 2001 From: Yuansheng Date: Mon, 19 Aug 2019 16:46:21 +0800 Subject: [PATCH 17/50] test: updated test cases after added new plugin `grpc-proxy`. --- lua/apisix/plugins/grpc-proxy.lua | 2 +- t/admin/plugins.t | 2 +- t/admin/schema.t | 2 +- t/debug-mode.t | 1 + 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lua/apisix/plugins/grpc-proxy.lua b/lua/apisix/plugins/grpc-proxy.lua index b1ea88485883..4851139301b7 100644 --- a/lua/apisix/plugins/grpc-proxy.lua +++ b/lua/apisix/plugins/grpc-proxy.lua @@ -13,7 +13,7 @@ local schema = { local _M = { version = 0.1, - priority = 500, + priority = 506, name = plugin_name, schema = schema, } diff --git a/t/admin/plugins.t b/t/admin/plugins.t index 45f1cc046b00..62a414f53a38 100644 --- a/t/admin/plugins.t +++ b/t/admin/plugins.t @@ -14,6 +14,6 @@ __DATA__ --- request GET /apisix/admin/plugins/list --- response_body_like eval -qr/\["limit-req","limit-count","limit-conn","key-auth","prometheus","node-status","jwt-auth","zipkin","ip-restriction"\]/ +qr/\["limit-req","limit-count","limit-conn","key-auth","prometheus","node-status","jwt-auth","zipkin","ip-restriction","grpc-proxy"\]/ --- no_error_log [error] diff --git a/t/admin/schema.t b/t/admin/schema.t index 4a017b03d340..3f50b5385dfb 100644 --- a/t/admin/schema.t +++ b/t/admin/schema.t @@ -14,7 +14,7 @@ __DATA__ --- request GET /apisix/admin/schema/route --- response_body eval -qr/"plugins": \{"type":"object"}/ +qr/"plugins":\{"type":"object"}/ --- no_error_log [error] diff --git a/t/debug-mode.t b/t/debug-mode.t index 16c48720a55c..1d0ca71735b0 100644 --- a/t/debug-mode.t +++ b/t/debug-mode.t @@ -46,6 +46,7 @@ loaded plugin and sort by priority: 1003 name: limit-conn loaded plugin and sort by priority: 1002 name: limit-count loaded plugin and sort by priority: 1001 name: limit-req loaded plugin and sort by priority: 1000 name: node-status +loaded plugin and sort by priority: 506 name: grpc-proxy loaded plugin and sort by priority: 500 name: prometheus loaded plugin and sort by priority: 0 name: example-plugin loaded plugin and sort by priority: -1000 name: zipkin From 8061c1e7f153be8b654afd7524380913d45573db Mon Sep 17 00:00:00 2001 From: nic-chen Date: Mon, 19 Aug 2019 16:58:32 +0800 Subject: [PATCH 18/50] fix error --- lua/apisix/plugins/grpc-proxy/proto.lua | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/lua/apisix/plugins/grpc-proxy/proto.lua b/lua/apisix/plugins/grpc-proxy/proto.lua index cde27e1bb866..9c0c9a05b743 100644 --- a/lua/apisix/plugins/grpc-proxy/proto.lua +++ b/lua/apisix/plugins/grpc-proxy/proto.lua @@ -1,14 +1,11 @@ local core = require("apisix.core") local protoc = require("protoc") +local lrucache = require("apisix.core.lrucache") local config = require("apisix.core.config_etcd") local schema = require("apisix.core.schema") local protos -local lrucache_proto = core.lrucache.new({ - ttl = 300, count = 256 -}) - local function protos_arrange(proto_id) if protos.values == nil then return nil @@ -39,9 +36,8 @@ end local _M = {version = 0.1} -_M.new = function(proto_id) - local key = "/proto" .. proto_id - return lrucache_proto(key, protos.conf_version, protos_arrange, proto_id) +function _M.new(proto_id) + return lrucache.global("/protoc", protos.conf_version, protos_arrange, proto_id) end From 6e1432133f03013c7e378a0678caa4a88bf19d2f Mon Sep 17 00:00:00 2001 From: nic-chen Date: Mon, 19 Aug 2019 17:36:24 +0800 Subject: [PATCH 19/50] =?UTF-8?q?feat=20=E4=B8=8D=E4=BD=BF=E7=94=A8lrucach?= =?UTF-8?q?e=20global?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lua/apisix/plugins/grpc-proxy/proto.lua | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lua/apisix/plugins/grpc-proxy/proto.lua b/lua/apisix/plugins/grpc-proxy/proto.lua index 9c0c9a05b743..6280ee5aae78 100644 --- a/lua/apisix/plugins/grpc-proxy/proto.lua +++ b/lua/apisix/plugins/grpc-proxy/proto.lua @@ -6,6 +6,10 @@ local schema = require("apisix.core.schema") local protos +local lrucache_proto = core.lrucache.new({ + ttl = 300, count = 100 +}) + local function protos_arrange(proto_id) if protos.values == nil then return nil @@ -37,7 +41,8 @@ local _M = {version = 0.1} function _M.new(proto_id) - return lrucache.global("/protoc", protos.conf_version, protos_arrange, proto_id) + local key = "/proto"..proto_id + return lrucache_proto(key, protos.conf_version, protos_arrange, proto_id) end From 73cadb12c395857caba459315844746acd8995dc Mon Sep 17 00:00:00 2001 From: nic-chen Date: Mon, 19 Aug 2019 17:49:09 +0800 Subject: [PATCH 20/50] remove log --- lua/apisix/plugins/grpc-proxy/proto.lua | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/lua/apisix/plugins/grpc-proxy/proto.lua b/lua/apisix/plugins/grpc-proxy/proto.lua index 6280ee5aae78..7a3a3b30e44a 100644 --- a/lua/apisix/plugins/grpc-proxy/proto.lua +++ b/lua/apisix/plugins/grpc-proxy/proto.lua @@ -1,8 +1,7 @@ -local core = require("apisix.core") -local protoc = require("protoc") -local lrucache = require("apisix.core.lrucache") -local config = require("apisix.core.config_etcd") -local schema = require("apisix.core.schema") +local core = require("apisix.core") +local protoc = require("protoc") +local config = require("apisix.core.config_etcd") +local schema = require("apisix.core.schema") local protos @@ -15,8 +14,6 @@ local function protos_arrange(proto_id) return nil end - ngx.log(ngx.ERR, "load proto") - local content for _, proto in ipairs(protos.values) do if proto_id == proto.value.id then From 9fc8e41457d88578815bf0bc2be29034df70cc4f Mon Sep 17 00:00:00 2001 From: Yuansheng Date: Mon, 19 Aug 2019 20:27:40 +0800 Subject: [PATCH 21/50] test: use 128 worker connections as default value. --- t/APISix.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/t/APISix.pm b/t/APISix.pm index 71093a234286..836b4675fb97 100644 --- a/t/APISix.pm +++ b/t/APISix.pm @@ -8,6 +8,7 @@ repeat_each(1); log_level('info'); no_long_string(); no_shuffle(); +worker_connections(128); my $pwd = cwd(); From fdc0f1ace55b600ce021b45bb8ffefd36d31f792 Mon Sep 17 00:00:00 2001 From: Yuansheng Date: Mon, 19 Aug 2019 20:36:42 +0800 Subject: [PATCH 22/50] doc --- README.md | 2 +- README_CN.md | 1 + doc/plugins/grpc-proxy-cn.md | 29 ++++++++++++++--------------- doc/plugins/grpc-proxy.md | 7 ++++--- 4 files changed, 20 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 962a15869c1a..2e21dee9ebe4 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ For more detailed information, see the [White Paper](https://www.iresty.com/down - **[Limit-concurrency](doc/plugins/limit-conn.md)** - **OpenTracing: [Zipkin](doc/plugins/zipkin.md)** - **Monitoring and Metrics**: [Prometheus](doc/plugins/prometheus.md) -- **[Grpc-Proxy](doc/plugins/grpc-proxy.md)**:REST <-> gRPC proxying. +- **[gRPC-Proxy](doc/plugins/grpc-proxy.md)**:REST <-> gRPC proxying. - **Custom plugins**: Allows hooking of common phases, such as `rewrite`, `access`, `header filer`, `body filter` and `log`, also allows to hook the `balancer` stage. - **Dashboard**: Built-in dashboard to control APISIX. - **CLI**: start\stop\reload APISIX through the command line. diff --git a/README_CN.md b/README_CN.md index e815dfbffe7c..55b6679f42f2 100644 --- a/README_CN.md +++ b/README_CN.md @@ -41,6 +41,7 @@ APISIX 通过插件机制,提供动态负载平衡、身份验证、限流限 - **[限制并发](doc/plugins/limit-conn-cn.md)** - **OpenTracing: [Zipkin](doc/plugins/zipkin.md)** - **监控和指标**: [Prometheus](doc/plugins/prometheus-cn.md) +- **[gRPC-Proxy](doc/plugins/grpc-proxy-cn.md)**:REST <-> gRPC proxying. - **自定义插件**: 允许挂载常见阶段,例如`rewrite`,`access`,`header filer`,`body filter`和`log`,还允许挂载 `balancer` 阶段。 - **控制台**: 内置控制台来操作 APISIX 集群。 - **CLI**: 使用命令行来启动、关闭和重启 APISIX。 diff --git a/doc/plugins/grpc-proxy-cn.md b/doc/plugins/grpc-proxy-cn.md index 613bff827641..921354ddf05d 100644 --- a/doc/plugins/grpc-proxy-cn.md +++ b/doc/plugins/grpc-proxy-cn.md @@ -1,15 +1,16 @@ -[中文](grpc-proxy-cn.md) +[English](grpc-proxy.md) # grpc-proxy +HTTP(s) -> APISIX -> gRPC server ### Proto #### 参数 -* `content`: `.proto`文件的内容 +* `content`: `.proto` 文件的内容 #### 添加proto -路径中最后的数字,会被用作proto的id 做唯一标识,比如下面示例的proto `id` 是 `1` : +路径中最后的数字,会被用作 proto 的 id 做唯一标识,比如下面示例的 proto `id` 是 `1` : ```shell curl http://127.0.0.1:9080/apisix/admin/proto/1 -X PUT -d ' @@ -29,20 +30,21 @@ curl http://127.0.0.1:9080/apisix/admin/proto/1 -X PUT -d ' ``` ### 参数 + * `proto_id`: `.proto`内容的id. * `service`: grpc服务名. * `method`: grpc服务中要调用的方法名. -### 举个例子 +### 示例 -#### 使用grpc-proxy插件 +#### 使用 grpc-proxy 插件 -在指定route中,代理grpc服务接口: +在指定 route 中,代理 grpc 服务接口: -* 注意: 这个route的属性`service_protocal` 必须设置为 `grpc` -* 例子所代理的grpc服务可参考:[grpc_server_example](https://github.com/nic-chen/grpc_server_example) +* 注意: 这个 route 的属性`service_protocal` 必须设置为 `grpc` +* 例子所代理的 grpc 服务可参考:[grpc_server_example](https://github.com/nic-chen/grpc_server_example) ```shell curl http://127.0.0.1:9080/apisix/admin/routes/111 -X PUT -d ' @@ -69,13 +71,10 @@ curl http://127.0.0.1:9080/apisix/admin/routes/111 -X PUT -d ' #### 测试 -访问上面配置的route: -```shell -curl -i http://127.0.0.1:9080/grpctest -``` +访问上面配置的 route: -response: -``` +```shell +$ curl -i http://127.0.0.1:9080/grpctest HTTP/1.1 200 OK Date: Fri, 16 Aug 2019 11:55:36 GMT Content-Type: application/json @@ -87,5 +86,5 @@ Proxy-Connection: keep-alive {"message":"Hello world"} ``` -这意味着代理成功 +这表示已成功代理。 diff --git a/doc/plugins/grpc-proxy.md b/doc/plugins/grpc-proxy.md index 1d7a1a5b9b46..d2cc8e0694ae 100644 --- a/doc/plugins/grpc-proxy.md +++ b/doc/plugins/grpc-proxy.md @@ -1,6 +1,7 @@ [中文](grpc-proxy-cn.md) # grpc-proxy +HTTP(s) -> APISIX -> gRPC server ### Proto @@ -29,16 +30,16 @@ curl http://127.0.0.1:9080/apisix/admin/proto/1 -X PUT -d ' ``` ### Parameters + * `proto_id`: `.proto` content id. * `service`: the grpc service name. * `method`: the method name of grpc service. - - ### example #### enable plugin -Here's an example, enable the grpc-proxy plugin on the specified route: + +Here's an example, to enable the grpc-proxy plugin to specified route: * attention: the route's option `service_protocal` must be `grpc` * the grpc server example:[grpc_server_example](https://github.com/nic-chen/grpc_server_example) From 8a0f38043439297b21e6f7ce2b94688b5bf7553a Mon Sep 17 00:00:00 2001 From: nic-chen Date: Tue, 20 Aug 2019 20:25:18 +0800 Subject: [PATCH 23/50] travis add grpc server running with golang --- .travis/linux_runner.sh | 4 ++++ .travis/osx_runner.sh | 6 +++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/.travis/linux_runner.sh b/.travis/linux_runner.sh index a8a7c602ef25..2e4186620449 100755 --- a/.travis/linux_runner.sh +++ b/.travis/linux_runner.sh @@ -28,6 +28,7 @@ do_install() { sudo add-apt-repository -y "deb http://openresty.org/package/ubuntu $(lsb_release -sc) main" sudo apt-get update sudo apt-get install openresty-debug + sudo apt-get install golang export_or_prefix @@ -35,12 +36,15 @@ do_install() { sudo luarocks install --lua-dir=${OPENRESTY_PREFIX}luajit lua-resty-libr3 --tree=deps --local git clone https://github.com/openresty/test-nginx.git test-nginx + + git clone https://github.com/nic-chen/grpc_server_example.git grpc_server_example } script() { export_or_prefix export PATH=$OPENRESTY_PREFIX/nginx/sbin:$OPENRESTY_PREFIX/luajit/bin:$OPENRESTY_PREFIX/bin:$PATH sudo service etcd start + go run ./grpc_server_example/main.go & ./bin/apisix help ./bin/apisix init ./bin/apisix init_etcd diff --git a/.travis/osx_runner.sh b/.travis/osx_runner.sh index 54c587ca509a..d9a90c43e11a 100755 --- a/.travis/osx_runner.sh +++ b/.travis/osx_runner.sh @@ -17,7 +17,7 @@ export_or_prefix() { } before_install() { - HOMEBREW_NO_AUTO_UPDATE=1 brew install perl cpanminus etcd luarocks openresty/brew/openresty-debug + HOMEBREW_NO_AUTO_UPDATE=1 brew install perl cpanminus etcd luarocks openresty/brew/openresty-debug golang sudo cpanm --notest Test::Nginx >build.log 2>&1 || (cat build.log && exit 1) export_or_prefix luarocks install --lua-dir=${OPENRESTY_PREFIX}/luajit luacov-coveralls --local --tree=deps @@ -30,6 +30,7 @@ do_install() { make dev_r3 git clone https://github.com/openresty/test-nginx.git test-nginx + git clone https://github.com/nic-chen/grpc_server_example.git grpc_server_example } script() { @@ -38,6 +39,9 @@ script() { luarocks install luacheck brew services start etcd + + go run ./grpc_server_example/main.go & + make help make init sudo make run From 1eecd1ede0dcc8d91e45b5d50054669502f77c68 Mon Sep 17 00:00:00 2001 From: Yuansheng Date: Mon, 19 Aug 2019 21:35:52 +0800 Subject: [PATCH 24/50] optimize: local cache global variable. --- Makefile | 3 +- doc/plugins/grpc-proxy.md | 6 ++-- lua/apisix/plugins/grpc-proxy.lua | 31 ++++++++-------- lua/apisix/plugins/grpc-proxy/proto.lua | 22 ++++++------ lua/apisix/plugins/grpc-proxy/request.lua | 26 ++++++++------ lua/apisix/plugins/grpc-proxy/response.lua | 16 +++++---- lua/apisix/plugins/grpc-proxy/util.lua | 41 ++++++++++++++-------- 7 files changed, 84 insertions(+), 61 deletions(-) diff --git a/Makefile b/Makefile index 16f5a6afe5db..806ef87a5b30 100644 --- a/Makefile +++ b/Makefile @@ -52,7 +52,8 @@ check: lua/apisix/admin/*.lua \ lua/apisix/core/*.lua \ lua/apisix/http/*.lua \ - lua/apisix/plugins/*.lua > \ + lua/apisix/plugins/*.lua \ + lua/apisix/plugins/grpc-proxy/*.lua > \ /tmp/check.log 2>&1 || (cat /tmp/check.log && exit 1) diff --git a/doc/plugins/grpc-proxy.md b/doc/plugins/grpc-proxy.md index d2cc8e0694ae..62b9adadc9ae 100644 --- a/doc/plugins/grpc-proxy.md +++ b/doc/plugins/grpc-proxy.md @@ -52,9 +52,9 @@ curl http://127.0.0.1:9080/apisix/admin/routes/111 -X PUT -d ' "service_protocol": "grpc", "plugins": { "grpc-proxy": { - "proto_id": "1", - "service": "helloworld.Greeter", - "method": "SayHello" + "proto_id": "1", + "service": "helloworld.Greeter", + "method": "SayHello" } }, "upstream": { diff --git a/lua/apisix/plugins/grpc-proxy.lua b/lua/apisix/plugins/grpc-proxy.lua index 4851139301b7..e147fdbcd754 100644 --- a/lua/apisix/plugins/grpc-proxy.lua +++ b/lua/apisix/plugins/grpc-proxy.lua @@ -5,6 +5,7 @@ local proto = require("apisix.plugins.grpc-proxy.proto") local request = require("apisix.plugins.grpc-proxy.request") local response = require("apisix.plugins.grpc-proxy.response") + local schema = { type = "object", additionalProperties = true @@ -35,24 +36,28 @@ end function _M.access(conf, ctx) + core.log.info("conf: ", core.json.delay_encode(conf)) + local proto_id = conf.proto_id if not proto_id then - ngx.log(ngx.ERR, ("proto id miss: %s"):format(proto_id)) + core.log.error("proto id miss: ", proto_id) return end - local p, err = proto.new(proto_id) + local proto_obj, err = proto.fetch(proto_id) if err then - ngx.log(ngx.ERR, ("proto load error: %s"):format(err)) + core.log.error("proto load error: ", err) return end - local req = request.new(p) + + local req = request.new(proto_obj) err = req:transform(conf.service, conf.method) if err then - ngx.log(ngx.ERR, ("trasnform request error: %s"):format(err)) + core.log.error("trasnform request error: ", err) return end + ctx.proto_obj = proto_obj end @@ -62,21 +67,15 @@ end function _M.body_filter(conf, ctx) - local proto_id = conf.proto_id - if not proto_id then - ngx.log(ngx.ERR, ("proto id miss: %s"):format(proto_id)) + local proto_obj = ctx.proto_obj + if not proto_obj then return end - local p, err = proto.new(proto_id) - if err then - ngx.log(ngx.ERR, ("proto load error: %s"):format(err)) - return - end - local resp = response.new(p) - err = resp:transform(conf.service, conf.method) + local resp = response.new(proto_obj) + local err = resp:transform(conf.service, conf.method) if err then - ngx.log(ngx.ERR, ("trasnform response error: %s"):format(err)) + core.log.error("trasnform response error: ", err) return end end diff --git a/lua/apisix/plugins/grpc-proxy/proto.lua b/lua/apisix/plugins/grpc-proxy/proto.lua index 7a3a3b30e44a..594b996ef0e3 100644 --- a/lua/apisix/plugins/grpc-proxy/proto.lua +++ b/lua/apisix/plugins/grpc-proxy/proto.lua @@ -1,7 +1,6 @@ local core = require("apisix.core") local protoc = require("protoc") -local config = require("apisix.core.config_etcd") -local schema = require("apisix.core.schema") +local ipairs = ipairs local protos @@ -9,7 +8,8 @@ local lrucache_proto = core.lrucache.new({ ttl = 300, count = 100 }) -local function protos_arrange(proto_id) + +local function create_proto_obj(proto_id) if protos.values == nil then return nil end @@ -23,8 +23,7 @@ local function protos_arrange(proto_id) end if not content then - ngx.log(ngx.ERR, "failed to find proto by id: " .. proto_id) - return + return nil, "failed to find proto by id: " .. proto_id end local _p = protoc.new() @@ -37,20 +36,21 @@ end local _M = {version = 0.1} -function _M.new(proto_id) - local key = "/proto"..proto_id - return lrucache_proto(key, protos.conf_version, protos_arrange, proto_id) +function _M.fetch(proto_id) + return lrucache_proto(proto_id, protos.conf_version, + create_proto_obj, proto_id) end function _M.init() local err - protos, err = config.new("/proto", { + protos, err = core.config.new("/proto", { automatic = true, - item_schema = schema.proto + item_schema = core.schema.proto }) if not protos then - core.log.error("failed to create etcd instance for fetching protos: " .. err) + core.log.error("failed to create etcd instance for fetching protos: ", + err) return end end diff --git a/lua/apisix/plugins/grpc-proxy/request.lua b/lua/apisix/plugins/grpc-proxy/request.lua index af1feddff644..5c63e3bf46d7 100644 --- a/lua/apisix/plugins/grpc-proxy/request.lua +++ b/lua/apisix/plugins/grpc-proxy/request.lua @@ -1,20 +1,25 @@ -local pb = require("pb") -local bit = require("bit") -local util = require("apisix.plugins.grpc-proxy.util") +local ngx = ngx +local string = string +local table = table +local pb = require("pb") +local bit = require("bit") +local util = require("apisix.plugins.grpc-proxy.util") -local _M = {} +local _M = {version = 0.1} -_M.new = function(proto) +function _M.new(proto) local instance = {} instance.transform = function(self, service, method, default_values) local m = util.find_method(proto, service, method) if not m then - return ("1.Undefined service method: %s/%s end."):format(service, method) + return "Undefined service method: " .. service .. "/" .. method + .. " end" end ngx.req.read_body() - local encoded = pb.encode(m.input_type, util.map_message(m.input_type, default_values or {})) + local encoded = pb.encode(m.input_type, + util.map_message(m.input_type, default_values or {})) local size = string.len(encoded) local prefix = { string.char(0), @@ -27,9 +32,9 @@ _M.new = function(proto) local message = table.concat(prefix, "") .. encoded ngx.req.set_method(ngx.HTTP_POST) - ngx.req.set_uri(("/%s/%s"):format(service, method), false) + ngx.req.set_uri("/" .. service .. "/" .. method, false) ngx.req.set_uri_args({}) - ngx.req.init_body(string.len(message)) + ngx.req.init_body(#message) ngx.req.set_body_data(message) return nil end @@ -37,4 +42,5 @@ _M.new = function(proto) return instance end -return _M \ No newline at end of file + +return _M diff --git a/lua/apisix/plugins/grpc-proxy/response.lua b/lua/apisix/plugins/grpc-proxy/response.lua index aa69ddfcf69a..9bf54a68825c 100644 --- a/lua/apisix/plugins/grpc-proxy/response.lua +++ b/lua/apisix/plugins/grpc-proxy/response.lua @@ -1,15 +1,19 @@ -local pb = require("pb") -local json = require("cjson") -local util = require("apisix.plugins.grpc-proxy.util") +local ngx = ngx +local string = string +local table = table +local pb = require("pb") +local json = require("cjson") +local util = require("apisix.plugins.grpc-proxy.util") -local _M = {} +local _M = {version = 0.1} -_M.new = function(proto) +function _M.new(proto) local instance = {} instance.transform = function(self, service, method) local m = util.find_method(proto, service, method) if not m then - return ("2.Undefined service method: %s/%s end."):format(service, method) + return "2.Undefined service method: " .. service .. "/" .. method + .. " end." end local chunk, eof = ngx.arg[1], ngx.arg[2] diff --git a/lua/apisix/plugins/grpc-proxy/util.lua b/lua/apisix/plugins/grpc-proxy/util.lua index edb04275a302..ff8bbaed1a7b 100644 --- a/lua/apisix/plugins/grpc-proxy/util.lua +++ b/lua/apisix/plugins/grpc-proxy/util.lua @@ -1,12 +1,18 @@ -local pb = require("pb") -local json -if not os.getenv("LUAUNIT") then - json = require("cjson") -end +local json = require("apisix.core.json") +local pb = require("pb") +local ngx = ngx +local io = io +local pairs = pairs +local ipairs = ipairs +local string = string +local tonumber = tonumber +local type = type + + +local _M = {version = 0.1} -local _M = {} -_M.file_exists = function(file) +function _M.file_exists(file) local fp = io.open(file, "r") if fp then fp:close() @@ -15,7 +21,8 @@ _M.file_exists = function(file) return false end -_M.find_method = function(protos, service, method) + +function _M.find_method(protos, service, method) for k, loaded in pairs(protos) do if type(loaded) == 'table' then local package = loaded.package @@ -34,10 +41,12 @@ _M.find_method = function(protos, service, method) return nil end + local function get_from_request(name, kind) local request_table if ngx.req.get_method() == "POST" then - if string.find(ngx.req.get_headers()["Content-Type"] or "", "application/json") then + if string.find(ngx.req.get_headers()["Content-Type"] or "", + "application/json", true) then request_table = json.decode(ngx.req.get_body_data()) else request_table = ngx.req.get_post_args() @@ -45,22 +54,25 @@ local function get_from_request(name, kind) else request_table = ngx.req.get_uri_args() end + local prefix = kind:sub(1, 3) if prefix == "str" then return request_table[name] or nil - elseif prefix == "int" then + end + + if prefix == "int" then if request_table[name] then return tonumber(request_table[name]) - else - return nil end end + return nil end -_M.map_message = function(field, default_values) + +function _M.map_message(field, default_values) if not pb.type(field) then - return nil, ("Field %s is not defined"):format(field) + return nil, "Field " .. field .. " is not defined" end local request = {} @@ -79,4 +91,5 @@ _M.map_message = function(field, default_values) return request, nil end + return _M From 666480c32e07c62890ed16cd4e81136f4d04c564 Mon Sep 17 00:00:00 2001 From: Yuansheng Date: Mon, 19 Aug 2019 21:43:50 +0800 Subject: [PATCH 25/50] change: code style. --- lua/apisix/plugins/grpc-proxy.lua | 5 +- lua/apisix/plugins/grpc-proxy/request.lua | 61 ++++++++++------------- 2 files changed, 27 insertions(+), 39 deletions(-) diff --git a/lua/apisix/plugins/grpc-proxy.lua b/lua/apisix/plugins/grpc-proxy.lua index e147fdbcd754..05c177197a2b 100644 --- a/lua/apisix/plugins/grpc-proxy.lua +++ b/lua/apisix/plugins/grpc-proxy.lua @@ -50,9 +50,8 @@ function _M.access(conf, ctx) return end - local req = request.new(proto_obj) - err = req:transform(conf.service, conf.method) - if err then + local ok, err = request(proto_obj, conf.service, conf.method) + if not ok then core.log.error("trasnform request error: ", err) return end diff --git a/lua/apisix/plugins/grpc-proxy/request.lua b/lua/apisix/plugins/grpc-proxy/request.lua index 5c63e3bf46d7..b97fd535c577 100644 --- a/lua/apisix/plugins/grpc-proxy/request.lua +++ b/lua/apisix/plugins/grpc-proxy/request.lua @@ -5,42 +5,31 @@ local pb = require("pb") local bit = require("bit") local util = require("apisix.plugins.grpc-proxy.util") -local _M = {version = 0.1} - -function _M.new(proto) - local instance = {} - - instance.transform = function(self, service, method, default_values) - local m = util.find_method(proto, service, method) - if not m then - return "Undefined service method: " .. service .. "/" .. method - .. " end" - end - - ngx.req.read_body() - local encoded = pb.encode(m.input_type, - util.map_message(m.input_type, default_values or {})) - local size = string.len(encoded) - local prefix = { - string.char(0), - string.char(bit.band(bit.rshift(size, 24), 0xFF)), - string.char(bit.band(bit.rshift(size, 16), 0xFF)), - string.char(bit.band(bit.rshift(size, 8), 0xFF)), - string.char(bit.band(size, 0xFF)) - } - - local message = table.concat(prefix, "") .. encoded - - ngx.req.set_method(ngx.HTTP_POST) - ngx.req.set_uri("/" .. service .. "/" .. method, false) - ngx.req.set_uri_args({}) - ngx.req.init_body(#message) - ngx.req.set_body_data(message) - return nil +return function (proto, service, method, default_values) + local m = util.find_method(proto, service, method) + if not m then + return false, "Undefined service method: " .. service .. "/" .. method + .. " end" end - return instance + ngx.req.read_body() + local encoded = pb.encode(m.input_type, + util.map_message(m.input_type, default_values or {})) + local size = string.len(encoded) + local prefix = { + string.char(0), + string.char(bit.band(bit.rshift(size, 24), 0xFF)), + string.char(bit.band(bit.rshift(size, 16), 0xFF)), + string.char(bit.band(bit.rshift(size, 8), 0xFF)), + string.char(bit.band(size, 0xFF)) + } + + local message = table.concat(prefix, "") .. encoded + + ngx.req.set_method(ngx.HTTP_POST) + ngx.req.set_uri("/" .. service .. "/" .. method, false) + ngx.req.set_uri_args({}) + ngx.req.init_body(#message) + ngx.req.set_body_data(message) + return true end - - -return _M From dc1a929793aaf3be92a129d0fdd2e914ab0811f2 Mon Sep 17 00:00:00 2001 From: Yuansheng Date: Mon, 19 Aug 2019 21:47:53 +0800 Subject: [PATCH 26/50] change: code style. --- lua/apisix/plugins/grpc-proxy.lua | 3 +- lua/apisix/plugins/grpc-proxy/request.lua | 8 ++- lua/apisix/plugins/grpc-proxy/response.lua | 65 ++++++++++------------ 3 files changed, 36 insertions(+), 40 deletions(-) diff --git a/lua/apisix/plugins/grpc-proxy.lua b/lua/apisix/plugins/grpc-proxy.lua index 05c177197a2b..60abaf23a0cc 100644 --- a/lua/apisix/plugins/grpc-proxy.lua +++ b/lua/apisix/plugins/grpc-proxy.lua @@ -71,8 +71,7 @@ function _M.body_filter(conf, ctx) return end - local resp = response.new(proto_obj) - local err = resp:transform(conf.service, conf.method) + local err = response(proto_obj, conf.service, conf.method) if err then core.log.error("trasnform response error: ", err) return diff --git a/lua/apisix/plugins/grpc-proxy/request.lua b/lua/apisix/plugins/grpc-proxy/request.lua index b97fd535c577..4d1d3954e01f 100644 --- a/lua/apisix/plugins/grpc-proxy/request.lua +++ b/lua/apisix/plugins/grpc-proxy/request.lua @@ -1,9 +1,11 @@ + +local util = require("apisix.plugins.grpc-proxy.util") +local pb = require("pb") +local bit = require("bit") local ngx = ngx local string = string local table = table -local pb = require("pb") -local bit = require("bit") -local util = require("apisix.plugins.grpc-proxy.util") + return function (proto, service, method, default_values) local m = util.find_method(proto, service, method) diff --git a/lua/apisix/plugins/grpc-proxy/response.lua b/lua/apisix/plugins/grpc-proxy/response.lua index 9bf54a68825c..55c04363e104 100644 --- a/lua/apisix/plugins/grpc-proxy/response.lua +++ b/lua/apisix/plugins/grpc-proxy/response.lua @@ -1,46 +1,41 @@ +local util = require("apisix.plugins.grpc-proxy.util") +local core = require("apisix.core") +local pb = require("pb") local ngx = ngx local string = string local table = table -local pb = require("pb") -local json = require("cjson") -local util = require("apisix.plugins.grpc-proxy.util") -local _M = {version = 0.1} -function _M.new(proto) - local instance = {} - instance.transform = function(self, service, method) - local m = util.find_method(proto, service, method) - if not m then - return "2.Undefined service method: " .. service .. "/" .. method - .. " end." - end +return function(proto, service, method) + local m = util.find_method(proto, service, method) + if not m then + return false, "2.Undefined service method: " .. service .. "/" .. method + .. " end." + end + + local chunk, eof = ngx.arg[1], ngx.arg[2] + local buffered = ngx.ctx.buffered + if not buffered then + buffered = {} + ngx.ctx.buffered = buffered + end - local chunk, eof = ngx.arg[1], ngx.arg[2] - local buffered = ngx.ctx.buffered - if not buffered then - buffered = {} - ngx.ctx.buffered = buffered - end - if chunk ~= "" then - buffered[#buffered + 1] = chunk - ngx.arg[1] = nil - end + if chunk ~= "" then + buffered[#buffered + 1] = chunk + ngx.arg[1] = nil + end - if eof then - ngx.ctx.buffered = nil - local buffer = table.concat(buffered) - if not ngx.req.get_headers()["X-Grpc-Web"] then - buffer = string.sub(buffer, 6) - end + if not eof then + return + end - local decoded = pb.decode(m.output_type, buffer) - local response = json.encode(decoded) - ngx.arg[1] = response - end + ngx.ctx.buffered = nil + local buffer = table.concat(buffered) + if not ngx.req.get_headers()["X-Grpc-Web"] then + buffer = string.sub(buffer, 6) end - return instance + local decoded = pb.decode(m.output_type, buffer) + local response = core.json.encode(decoded) + ngx.arg[1] = response end - -return _M From 7cc3ef96a70c5f20e768583ceb5dd97335f0e9eb Mon Sep 17 00:00:00 2001 From: Yuansheng Date: Tue, 20 Aug 2019 10:45:58 +0800 Subject: [PATCH 27/50] change: code style. --- lua/apisix/plugins/grpc-proxy/request.lua | 2 ++ lua/apisix/plugins/grpc-proxy/util.lua | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lua/apisix/plugins/grpc-proxy/request.lua b/lua/apisix/plugins/grpc-proxy/request.lua index 4d1d3954e01f..3bfac33401f1 100644 --- a/lua/apisix/plugins/grpc-proxy/request.lua +++ b/lua/apisix/plugins/grpc-proxy/request.lua @@ -1,5 +1,6 @@ local util = require("apisix.plugins.grpc-proxy.util") +local core = require("apisix.core") local pb = require("pb") local bit = require("bit") local ngx = ngx @@ -8,6 +9,7 @@ local table = table return function (proto, service, method, default_values) + core.log.info("proto: ", core.json.delay_encode(proto, true)) local m = util.find_method(proto, service, method) if not m then return false, "Undefined service method: " .. service .. "/" .. method diff --git a/lua/apisix/plugins/grpc-proxy/util.lua b/lua/apisix/plugins/grpc-proxy/util.lua index ff8bbaed1a7b..34753945ee1c 100644 --- a/lua/apisix/plugins/grpc-proxy/util.lua +++ b/lua/apisix/plugins/grpc-proxy/util.lua @@ -27,7 +27,7 @@ function _M.find_method(protos, service, method) if type(loaded) == 'table' then local package = loaded.package for _, s in ipairs(loaded.service or {}) do - if ("%s.%s"):format(package, s.name) == service then + if package .. "." .. s.name == service then for _, m in ipairs(s.method) do if m.name == method then return m From 6bcc2a8e0c73118149b5044a470d44ea4b61f579 Mon Sep 17 00:00:00 2001 From: Yuansheng Date: Tue, 20 Aug 2019 10:52:34 +0800 Subject: [PATCH 28/50] change: code style. --- lua/apisix/plugins/grpc-proxy/util.lua | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/lua/apisix/plugins/grpc-proxy/util.lua b/lua/apisix/plugins/grpc-proxy/util.lua index 34753945ee1c..d8f64000797d 100644 --- a/lua/apisix/plugins/grpc-proxy/util.lua +++ b/lua/apisix/plugins/grpc-proxy/util.lua @@ -1,7 +1,6 @@ local json = require("apisix.core.json") local pb = require("pb") local ngx = ngx -local io = io local pairs = pairs local ipairs = ipairs local string = string @@ -12,16 +11,6 @@ local type = type local _M = {version = 0.1} -function _M.file_exists(file) - local fp = io.open(file, "r") - if fp then - fp:close() - return true - end - return false -end - - function _M.find_method(protos, service, method) for k, loaded in pairs(protos) do if type(loaded) == 'table' then @@ -88,7 +77,7 @@ function _M.map_message(field, default_values) request[name] = get_from_request(name, field_type) or default_values[name] or nil end end - return request, nil + return request end From 332a289480ff3f37b0fc61adb453f8b7a7f3dcd1 Mon Sep 17 00:00:00 2001 From: Yuansheng Date: Tue, 20 Aug 2019 10:56:53 +0800 Subject: [PATCH 29/50] change(CLI): updated `nginx.conf` from CLI. --- bin/apisix | 7 ++++--- conf/nginx.conf | 11 ++++++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/bin/apisix b/bin/apisix index f5dca22fd70f..c4cff64ca53a 100755 --- a/bin/apisix +++ b/bin/apisix @@ -128,7 +128,7 @@ http { apisix.http_balancer_phase() } - keepalive 32; + keepalive 320; } init_by_lua_block { @@ -247,7 +247,8 @@ http { } grpc_set_header Content-Type application/grpc; - grpc_pass apisix_backend; + grpc_socket_keepalive on; + grpc_pass grpc://apisix_backend; header_filter_by_lua_block { apisix.http_header_filter_phase() @@ -255,7 +256,7 @@ http { body_filter_by_lua_block { apisix.http_body_filter_phase() - } + } log_by_lua_block { apisix.http_log_phase() diff --git a/conf/nginx.conf b/conf/nginx.conf index 3e4c59ba1c6c..40fe263db597 100644 --- a/conf/nginx.conf +++ b/conf/nginx.conf @@ -62,7 +62,7 @@ http { apisix.http_balancer_phase() } - keepalive 32; + keepalive 320; } init_by_lua_block { @@ -141,6 +141,10 @@ http { apisix.http_header_filter_phase() } + body_filter_by_lua_block { + apisix.http_body_filter_phase() + } + log_by_lua_block { apisix.http_log_phase() } @@ -153,7 +157,8 @@ http { } grpc_set_header Content-Type application/grpc; - grpc_pass apisix_backend; + grpc_socket_keepalive on; + grpc_pass grpc://apisix_backend; header_filter_by_lua_block { apisix.http_header_filter_phase() @@ -161,7 +166,7 @@ http { body_filter_by_lua_block { apisix.http_body_filter_phase() - } + } log_by_lua_block { apisix.http_log_phase() From 3fb58016a237030bec33f9daa3eea1c598471387 Mon Sep 17 00:00:00 2001 From: nic-chen Date: Tue, 20 Aug 2019 20:42:52 +0800 Subject: [PATCH 30/50] fix golang version --- .travis/linux_runner.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis/linux_runner.sh b/.travis/linux_runner.sh index 2e4186620449..9372aa6ebb9c 100755 --- a/.travis/linux_runner.sh +++ b/.travis/linux_runner.sh @@ -28,6 +28,9 @@ do_install() { sudo add-apt-repository -y "deb http://openresty.org/package/ubuntu $(lsb_release -sc) main" sudo apt-get update sudo apt-get install openresty-debug + + add-apt-repository ppa:longsleep/golang-backports + apt-get update sudo apt-get install golang export_or_prefix From c1fecdfea24f33a51fbf2d98ea5b641df1573783 Mon Sep 17 00:00:00 2001 From: nic-chen Date: Tue, 20 Aug 2019 20:54:21 +0800 Subject: [PATCH 31/50] fix run as root --- .travis/linux_runner.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis/linux_runner.sh b/.travis/linux_runner.sh index 9372aa6ebb9c..309e2f29c73b 100755 --- a/.travis/linux_runner.sh +++ b/.travis/linux_runner.sh @@ -29,8 +29,8 @@ do_install() { sudo apt-get update sudo apt-get install openresty-debug - add-apt-repository ppa:longsleep/golang-backports - apt-get update + sudo add-apt-repository ppa:longsleep/golang-backports + sudo apt-get update sudo apt-get install golang export_or_prefix From f9a8b7d800c46890bb76192589569506ba9dfce4 Mon Sep 17 00:00:00 2001 From: nic-chen Date: Tue, 20 Aug 2019 21:20:52 +0800 Subject: [PATCH 32/50] fix force yes --- .travis/linux_runner.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis/linux_runner.sh b/.travis/linux_runner.sh index 309e2f29c73b..388325211b37 100755 --- a/.travis/linux_runner.sh +++ b/.travis/linux_runner.sh @@ -29,7 +29,7 @@ do_install() { sudo apt-get update sudo apt-get install openresty-debug - sudo add-apt-repository ppa:longsleep/golang-backports + sudo add-apt-repository -y ppa:longsleep/golang-backports sudo apt-get update sudo apt-get install golang From f4133d0605c342295f0b96712c1f56630944253a Mon Sep 17 00:00:00 2001 From: nic-chen Date: Tue, 20 Aug 2019 21:36:41 +0800 Subject: [PATCH 33/50] test mac os --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 282faf9a75d6..d7d7d67c9beb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,6 @@ matrix: include: - os: linux - os: osx - if: type IN (push) cache: directories: - $HOME/Library/Caches/Homebrew From 0fd15f3d2969e051e2833c2ee35ab756c0bf5089 Mon Sep 17 00:00:00 2001 From: nic-chen Date: Tue, 20 Aug 2019 22:02:33 +0800 Subject: [PATCH 34/50] test mac os --- .travis/osx_runner.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis/osx_runner.sh b/.travis/osx_runner.sh index d9a90c43e11a..8e7dd3ee89e8 100755 --- a/.travis/osx_runner.sh +++ b/.travis/osx_runner.sh @@ -17,7 +17,8 @@ export_or_prefix() { } before_install() { - HOMEBREW_NO_AUTO_UPDATE=1 brew install perl cpanminus etcd luarocks openresty/brew/openresty-debug golang + HOMEBREW_NO_AUTO_UPDATE=1 brew install perl cpanminus etcd luarocks openresty/brew/openresty-debug + brew upgrade go sudo cpanm --notest Test::Nginx >build.log 2>&1 || (cat build.log && exit 1) export_or_prefix luarocks install --lua-dir=${OPENRESTY_PREFIX}/luajit luacov-coveralls --local --tree=deps From 170d87cb174cb9e000fbf6ddc62a6d95850af2d1 Mon Sep 17 00:00:00 2001 From: Yuansheng Date: Tue, 20 Aug 2019 23:27:06 +0800 Subject: [PATCH 35/50] test: added test cases. --- bin/apisix | 1 - t/APISix.pm | 26 ++++++++++ t/plugin/grpc-proxy.t | 108 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 t/plugin/grpc-proxy.t diff --git a/bin/apisix b/bin/apisix index c4cff64ca53a..e9b238e1c70a 100755 --- a/bin/apisix +++ b/bin/apisix @@ -262,7 +262,6 @@ http { apisix.http_log_phase() } } - } } ]=] diff --git a/t/APISix.pm b/t/APISix.pm index 836b4675fb97..960fdb43513b 100644 --- a/t/APISix.pm +++ b/t/APISix.pm @@ -158,6 +158,32 @@ _EOC_ apisix.http_header_filter_phase() } + body_filter_by_lua_block { + apisix.http_body_filter_phase() + } + + log_by_lua_block { + apisix.http_log_phase() + } + } + + location \@grpc_pass { + access_by_lua_block { + apisix.grpc_access_phase() + } + + grpc_set_header Content-Type application/grpc; + grpc_socket_keepalive on; + grpc_pass grpc://apisix_backend; + + header_filter_by_lua_block { + apisix.http_header_filter_phase() + } + + body_filter_by_lua_block { + apisix.http_body_filter_phase() + } + log_by_lua_block { apisix.http_log_phase() } diff --git a/t/plugin/grpc-proxy.t b/t/plugin/grpc-proxy.t new file mode 100644 index 000000000000..d963ef30964e --- /dev/null +++ b/t/plugin/grpc-proxy.t @@ -0,0 +1,108 @@ +BEGIN { + if ($ENV{TEST_NGINX_CHECK_LEAK}) { + $SkipReason = "unavailable for the hup tests"; + + } else { + $ENV{TEST_NGINX_USE_HUP} = 1; + undef $ENV{TEST_NGINX_USE_STAP}; + } +} + +use t::APISix 'no_plan'; + +repeat_each(1); +no_long_string(); +no_shuffle(); +no_root_location(); + +run_tests; + +__DATA__ + +=== TEST 1: set proto(id: 1) +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/proto/1', + ngx.HTTP_PUT, + [[{ + "content" : "syntax = \"proto3\"; + package helloworld; + service Greeter { + rpc SayHello (HelloRequest) returns (HelloReply) {} + } + message HelloRequest { + string name = 1; + } + message HelloReply { + string message = 1; + }" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed +--- no_error_log +[error] + + + +=== TEST 2: set routes(id: 1) +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "methods": ["GET"], + "uri": "/grpctest", + "service_protocol": "grpc", + "plugins": { + "grpc-proxy": { + "proto_id": "1", + "service": "helloworld.Greeter", + "method": "SayHello" + } + }, + "upstream": { + "type": "roundrobin", + "nodes": { + "127.0.0.1:50051": 1 + } + } + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed +--- no_error_log +[error] + + + +=== TEST 3: hit route +--- request +GET /grpctest +--- response_body +hello world +--- no_error_log +[error] +--- SKIP From 704e3c10a95b73e80c05d335d2e0153db4a96b0e Mon Sep 17 00:00:00 2001 From: Yuansheng Date: Wed, 21 Aug 2019 11:10:14 +0800 Subject: [PATCH 36/50] bugfix: added `Trailer` for grpc-proxy case --- lua/apisix/plugins/grpc-proxy.lua | 1 + lua/apisix/plugins/grpc-proxy/request.lua | 1 - lua/apisix/plugins/grpc-proxy/response.lua | 2 +- t/plugin/grpc-proxy.t | 3 ++- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lua/apisix/plugins/grpc-proxy.lua b/lua/apisix/plugins/grpc-proxy.lua index 60abaf23a0cc..3e9c82ba941e 100644 --- a/lua/apisix/plugins/grpc-proxy.lua +++ b/lua/apisix/plugins/grpc-proxy.lua @@ -62,6 +62,7 @@ end function _M.header_filter(conf, ctx) ngx.header["Content-Type"] = "application/json" + ngx.header["Trailer"] = {"grpc-status", "grpc-message"} end diff --git a/lua/apisix/plugins/grpc-proxy/request.lua b/lua/apisix/plugins/grpc-proxy/request.lua index 3bfac33401f1..9e52ac6cb9e2 100644 --- a/lua/apisix/plugins/grpc-proxy/request.lua +++ b/lua/apisix/plugins/grpc-proxy/request.lua @@ -33,7 +33,6 @@ return function (proto, service, method, default_values) ngx.req.set_method(ngx.HTTP_POST) ngx.req.set_uri("/" .. service .. "/" .. method, false) ngx.req.set_uri_args({}) - ngx.req.init_body(#message) ngx.req.set_body_data(message) return true end diff --git a/lua/apisix/plugins/grpc-proxy/response.lua b/lua/apisix/plugins/grpc-proxy/response.lua index 55c04363e104..7440ee94c732 100644 --- a/lua/apisix/plugins/grpc-proxy/response.lua +++ b/lua/apisix/plugins/grpc-proxy/response.lua @@ -21,7 +21,7 @@ return function(proto, service, method) end if chunk ~= "" then - buffered[#buffered + 1] = chunk + core.table.insert(buffered, chunk) ngx.arg[1] = nil end diff --git a/t/plugin/grpc-proxy.t b/t/plugin/grpc-proxy.t index d963ef30964e..a103cf0eec36 100644 --- a/t/plugin/grpc-proxy.t +++ b/t/plugin/grpc-proxy.t @@ -14,6 +14,7 @@ repeat_each(1); no_long_string(); no_shuffle(); no_root_location(); +log_level('debug'); run_tests; @@ -98,7 +99,7 @@ passed -=== TEST 3: hit route +=== TEST 3: hit route (test::nginx has bug, it do not support HTTP Trailer) --- request GET /grpctest --- response_body From d2f9d8e75b0de9b7b9405308aced4b12d7d3c8ca Mon Sep 17 00:00:00 2001 From: nic-chen Date: Wed, 21 Aug 2019 11:46:42 +0800 Subject: [PATCH 37/50] fix clean go cache --- .travis/osx_runner.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis/osx_runner.sh b/.travis/osx_runner.sh index 8e7dd3ee89e8..7d77458fa98f 100755 --- a/.travis/osx_runner.sh +++ b/.travis/osx_runner.sh @@ -19,6 +19,8 @@ export_or_prefix() { before_install() { HOMEBREW_NO_AUTO_UPDATE=1 brew install perl cpanminus etcd luarocks openresty/brew/openresty-debug brew upgrade go + go clean + export GO111MOUDULE=on sudo cpanm --notest Test::Nginx >build.log 2>&1 || (cat build.log && exit 1) export_or_prefix luarocks install --lua-dir=${OPENRESTY_PREFIX}/luajit luacov-coveralls --local --tree=deps From ee8f1202780d1a65273d0f477eecd1a23739eea2 Mon Sep 17 00:00:00 2001 From: nic-chen Date: Wed, 21 Aug 2019 14:04:12 +0800 Subject: [PATCH 38/50] =?UTF-8?q?feat=20=E6=9F=A5=E7=9C=8Bgo=E7=8E=AF?= =?UTF-8?q?=E5=A2=83=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .travis/osx_runner.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis/osx_runner.sh b/.travis/osx_runner.sh index 7d77458fa98f..e0b264b79bba 100755 --- a/.travis/osx_runner.sh +++ b/.travis/osx_runner.sh @@ -43,6 +43,7 @@ script() { luarocks install luacheck brew services start etcd + go env go run ./grpc_server_example/main.go & make help From a6d2b752b426b10c3e698ab88173f2c7b5712a38 Mon Sep 17 00:00:00 2001 From: Yuansheng Date: Wed, 21 Aug 2019 14:21:33 +0800 Subject: [PATCH 39/50] bugfix: supported to parse http response body which contains http trailer. --- .travis/linux_runner.sh | 2 +- .travis/osx_runner.sh | 4 ++-- t/plugin/grpc-proxy.t | 7 +++---- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/.travis/linux_runner.sh b/.travis/linux_runner.sh index 388325211b37..1087f174fd97 100755 --- a/.travis/linux_runner.sh +++ b/.travis/linux_runner.sh @@ -38,7 +38,7 @@ do_install() { sudo luarocks make --lua-dir=${OPENRESTY_PREFIX}luajit rockspec/apisix-dev-1.0-0.rockspec --tree=deps --only-deps --local sudo luarocks install --lua-dir=${OPENRESTY_PREFIX}luajit lua-resty-libr3 --tree=deps --local - git clone https://github.com/openresty/test-nginx.git test-nginx + git clone https://github.com/membphis/test-nginx.git test-nginx git clone https://github.com/nic-chen/grpc_server_example.git grpc_server_example } diff --git a/.travis/osx_runner.sh b/.travis/osx_runner.sh index e0b264b79bba..9b87e3552b17 100755 --- a/.travis/osx_runner.sh +++ b/.travis/osx_runner.sh @@ -17,7 +17,7 @@ export_or_prefix() { } before_install() { - HOMEBREW_NO_AUTO_UPDATE=1 brew install perl cpanminus etcd luarocks openresty/brew/openresty-debug + HOMEBREW_NO_AUTO_UPDATE=1 brew install perl cpanminus etcd luarocks openresty/brew/openresty-debug brew upgrade go go clean export GO111MOUDULE=on @@ -32,7 +32,7 @@ do_install() { make dev make dev_r3 - git clone https://github.com/openresty/test-nginx.git test-nginx + git clone https://github.com/membphis/test-nginx.git test-nginx git clone https://github.com/nic-chen/grpc_server_example.git grpc_server_example } diff --git a/t/plugin/grpc-proxy.t b/t/plugin/grpc-proxy.t index a103cf0eec36..a7699d05f4c9 100644 --- a/t/plugin/grpc-proxy.t +++ b/t/plugin/grpc-proxy.t @@ -99,11 +99,10 @@ passed -=== TEST 3: hit route (test::nginx has bug, it do not support HTTP Trailer) +=== TEST 3: hit route --- request GET /grpctest ---- response_body -hello world +--- response_body eval +qr/\{"message":"Hello "\}/ --- no_error_log [error] ---- SKIP From 22cf2ac1a3f7c5b5bf9e343d5e07b8947190b60d Mon Sep 17 00:00:00 2001 From: Yuansheng Date: Wed, 21 Aug 2019 14:29:11 +0800 Subject: [PATCH 40/50] copyright --- COPYRIGHT | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/COPYRIGHT b/COPYRIGHT index d0290c406358..4886689e4d2c 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -307,3 +307,29 @@ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. %%%%%%%%% + +lua-resty-grpc-gateway +https://github.com/ysugimoto/lua-resty-grpc-gateway + +MIT License + +Copyright (c) 2019 Yoshiaki Sugimoto + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + From a6a893e1f8de247929433327069c4f6f1cc66d6f Mon Sep 17 00:00:00 2001 From: nic-chen Date: Wed, 21 Aug 2019 15:06:16 +0800 Subject: [PATCH 41/50] fix go run error --- .travis/linux_runner.sh | 8 +++++++- .travis/osx_runner.sh | 6 +++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.travis/linux_runner.sh b/.travis/linux_runner.sh index 1087f174fd97..82fc449615fc 100755 --- a/.travis/linux_runner.sh +++ b/.travis/linux_runner.sh @@ -32,6 +32,8 @@ do_install() { sudo add-apt-repository -y ppa:longsleep/golang-backports sudo apt-get update sudo apt-get install golang + + export GO111MOUDULE=on export_or_prefix @@ -47,7 +49,11 @@ script() { export_or_prefix export PATH=$OPENRESTY_PREFIX/nginx/sbin:$OPENRESTY_PREFIX/luajit/bin:$OPENRESTY_PREFIX/bin:$PATH sudo service etcd start - go run ./grpc_server_example/main.go & + + cd grpc_server_example/ + go run main.go & + cd .. + ./bin/apisix help ./bin/apisix init ./bin/apisix init_etcd diff --git a/.travis/osx_runner.sh b/.travis/osx_runner.sh index 9b87e3552b17..e518e8d9b5c4 100755 --- a/.travis/osx_runner.sh +++ b/.travis/osx_runner.sh @@ -19,7 +19,6 @@ export_or_prefix() { before_install() { HOMEBREW_NO_AUTO_UPDATE=1 brew install perl cpanminus etcd luarocks openresty/brew/openresty-debug brew upgrade go - go clean export GO111MOUDULE=on sudo cpanm --notest Test::Nginx >build.log 2>&1 || (cat build.log && exit 1) export_or_prefix @@ -43,8 +42,9 @@ script() { luarocks install luacheck brew services start etcd - go env - go run ./grpc_server_example/main.go & + cd grpc_server_example/ + go run main.go & + cd .. make help make init From e20ebc7dce1645ee7a98ada3c6da4b2258c89588 Mon Sep 17 00:00:00 2001 From: nic-chen Date: Wed, 21 Aug 2019 15:19:57 +0800 Subject: [PATCH 42/50] fix build && run --- .travis/linux_runner.sh | 8 +++++--- .travis/osx_runner.sh | 8 +++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/.travis/linux_runner.sh b/.travis/linux_runner.sh index 82fc449615fc..dde269df03fe 100755 --- a/.travis/linux_runner.sh +++ b/.travis/linux_runner.sh @@ -43,6 +43,10 @@ do_install() { git clone https://github.com/membphis/test-nginx.git test-nginx git clone https://github.com/nic-chen/grpc_server_example.git grpc_server_example + + cd grpc_server_example/ + go build -o grpc_server_example main.go + cd .. } script() { @@ -50,9 +54,7 @@ script() { export PATH=$OPENRESTY_PREFIX/nginx/sbin:$OPENRESTY_PREFIX/luajit/bin:$OPENRESTY_PREFIX/bin:$PATH sudo service etcd start - cd grpc_server_example/ - go run main.go & - cd .. + ./grpc_server_example/grpc_server_example & ./bin/apisix help ./bin/apisix init diff --git a/.travis/osx_runner.sh b/.travis/osx_runner.sh index e518e8d9b5c4..1f02b4490df0 100755 --- a/.travis/osx_runner.sh +++ b/.travis/osx_runner.sh @@ -33,6 +33,10 @@ do_install() { git clone https://github.com/membphis/test-nginx.git test-nginx git clone https://github.com/nic-chen/grpc_server_example.git grpc_server_example + + cd grpc_server_example/ + go build -o grpc_server_example main.go + cd .. } script() { @@ -42,9 +46,7 @@ script() { luarocks install luacheck brew services start etcd - cd grpc_server_example/ - go run main.go & - cd .. + ./grpc_server_example/grpc_server_example & make help make init From adc056de3880cc7548e877b6552633d0e90ab81c Mon Sep 17 00:00:00 2001 From: nic-chen Date: Wed, 21 Aug 2019 17:11:53 +0800 Subject: [PATCH 43/50] feat route's service protocol limit --- lua/apisix/core/schema.lua | 3 +-- t/plugin/grpc-proxy.t | 43 +++++++++++++++++++++++++++++++++++++- 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/lua/apisix/core/schema.lua b/lua/apisix/core/schema.lua index 2aeca5df67ee..de1d15219892 100644 --- a/lua/apisix/core/schema.lua +++ b/lua/apisix/core/schema.lua @@ -259,8 +259,7 @@ local route = [[{ "uniqueItems": true }, "service_protocol": { - "type": "string", - "maxLength": 20 + "enum": [ "grpc", "http" ] }, "desc": {"type": "string", "maxLength": 256}, "plugins": ]] .. json.encode(plugins_schema) .. [[, diff --git a/t/plugin/grpc-proxy.t b/t/plugin/grpc-proxy.t index a7699d05f4c9..0f9b579f4985 100644 --- a/t/plugin/grpc-proxy.t +++ b/t/plugin/grpc-proxy.t @@ -98,7 +98,6 @@ passed [error] - === TEST 3: hit route --- request GET /grpctest @@ -106,3 +105,45 @@ GET /grpctest qr/\{"message":"Hello "\}/ --- no_error_log [error] + + +=== TEST 2: wrong service protocol +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "methods": ["GET"], + "uri": "/grpctest", + "service_protocol": "asf", + "plugins": { + "grpc-proxy": { + "proto_id": "1", + "service": "helloworld.Greeter", + "method": "SayHello" + } + }, + "upstream": { + "type": "roundrobin", + "nodes": { + "127.0.0.1:50051": 1 + } + } + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- error_code: 400 +--- no_error_log +[error] + + From 5996b8e28ee4de54cd92ade73a5d3d99b9d2cd43 Mon Sep 17 00:00:00 2001 From: nic-chen Date: Wed, 21 Aug 2019 18:02:29 +0800 Subject: [PATCH 44/50] add lua-protobug copyright --- COPYRIGHT | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/COPYRIGHT b/COPYRIGHT index 4886689e4d2c..1f1293eff277 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -333,3 +333,32 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +%%%%%%%%% + +lua-protobuf +https://github.com/starwing/lua-protobuf + +MIT License + +Copyright (c) 2019 Yoshiaki Sugimoto + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + + From b31ca726448ccdb58adf98ed1f5656f88a0c95df Mon Sep 17 00:00:00 2001 From: nic-chen Date: Wed, 21 Aug 2019 18:03:42 +0800 Subject: [PATCH 45/50] fix test index --- t/plugin/grpc-proxy.t | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/plugin/grpc-proxy.t b/t/plugin/grpc-proxy.t index 0f9b579f4985..7409ca248061 100644 --- a/t/plugin/grpc-proxy.t +++ b/t/plugin/grpc-proxy.t @@ -107,7 +107,7 @@ qr/\{"message":"Hello "\}/ [error] -=== TEST 2: wrong service protocol +=== TEST 4: wrong service protocol --- config location /t { content_by_lua_block { From 1bc9ae41ec16a6188c3009e50f1eda3efeba3f7f Mon Sep 17 00:00:00 2001 From: nic-chen Date: Wed, 21 Aug 2019 18:12:03 +0800 Subject: [PATCH 46/50] fix capture proto loading error --- lua/apisix/plugins/grpc-proxy/proto.lua | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lua/apisix/plugins/grpc-proxy/proto.lua b/lua/apisix/plugins/grpc-proxy/proto.lua index 594b996ef0e3..ea0c01951cd4 100644 --- a/lua/apisix/plugins/grpc-proxy/proto.lua +++ b/lua/apisix/plugins/grpc-proxy/proto.lua @@ -26,8 +26,13 @@ local function create_proto_obj(proto_id) return nil, "failed to find proto by id: " .. proto_id end - local _p = protoc.new() - _p:load(content) + local _p = protoc.new() + local res = _p:load(content) + + if not res or not _p.loaded then + return nil, "failed to load proto content" + end + return _p.loaded end From cc60515353be3eda92bc44d2d34c95c6d87fb592 Mon Sep 17 00:00:00 2001 From: nic-chen Date: Wed, 21 Aug 2019 20:04:22 +0800 Subject: [PATCH 47/50] fix code style --- lua/apisix.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lua/apisix.lua b/lua/apisix.lua index 69b1607ed881..522a3333838a 100644 --- a/lua/apisix.lua +++ b/lua/apisix.lua @@ -142,7 +142,7 @@ function _M.http_access_phase() local ngx_ctx = ngx.ctx local api_ctx = ngx_ctx.api_ctx - if api_ctx == nil then + if not api_ctx then api_ctx = core.tablepool.fetch("api_ctx", 0, 32) ngx_ctx.api_ctx = api_ctx end @@ -160,7 +160,7 @@ function _M.http_access_phase() end -- - if route.value.service_protocol=="grpc" then + if route.value.service_protocol == "grpc" then return ngx.exec("@grpc_pass") end @@ -206,7 +206,7 @@ function _M.grpc_access_phase() local ngx_ctx = ngx.ctx local api_ctx = ngx_ctx.api_ctx - if api_ctx == nil then + if not api_ctx then api_ctx = core.tablepool.fetch("api_ctx", 0, 32) ngx_ctx.api_ctx = api_ctx end From 07a24b9b85ad50d116ad678b34fda4485bde8846 Mon Sep 17 00:00:00 2001 From: nic-chen Date: Wed, 21 Aug 2019 20:57:45 +0800 Subject: [PATCH 48/50] travis condition --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index d7d7d67c9beb..282faf9a75d6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,7 @@ matrix: include: - os: linux - os: osx + if: type IN (push) cache: directories: - $HOME/Library/Caches/Homebrew From cb4567a7fd9c2ba5bad86e9c3d0cafa968169fe0 Mon Sep 17 00:00:00 2001 From: nic-chen Date: Wed, 21 Aug 2019 22:07:10 +0800 Subject: [PATCH 49/50] check proto is using or not when deleting it --- lua/apisix/admin/proto.lua | 44 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/lua/apisix/admin/proto.lua b/lua/apisix/admin/proto.lua index 7eadc51ab9c0..9463fdcd350c 100644 --- a/lua/apisix/admin/proto.lua +++ b/lua/apisix/admin/proto.lua @@ -1,4 +1,6 @@ local core = require("apisix.core") +local get_routes = require("apisix.http.router").http_routes +local get_services = require("apisix.http.service").services local tostring = tostring @@ -86,12 +88,54 @@ function _M.post(id, conf) return res.status, res.body end +function _M.check_proto_used(plugins, deleting, ptype, pid) + + core.log.info("plugins1: ", core.json.delay_encode(plugins, true)) + + if plugins then + if type(plugins) == "table" and plugins["grpc-proxy"] + and plugins["grpc-proxy"].proto_id + and tostring(plugins["grpc-proxy"].proto_id) == deleting then + return 400, {error_msg = "can not delete this proto," + .. ptype .. " [" .. pid + .. "] is still using it now"} + end + end +end function _M.delete(id) if not id then return 400, {error_msg = "missing proto id"} end + local routes, routes_ver = get_routes() + + core.log.info("routes: ", core.json.delay_encode(routes, true)) + core.log.info("routes_ver: ", routes_ver) + + if routes_ver and routes then + for _, route in ipairs(routes) do + if type(route) == "table" and route.value + and route.value.plugins then + return _M.check_proto_used(route.value.plugins, id, "route", route.value.id) + end + end + end + + local services, services_ver = get_services() + + core.log.info("services: ", core.json.delay_encode(services, true)) + core.log.info("services_ver: ", services_ver) + + if services_ver and services then + for _, service in ipairs(services) do + if type(service) == "table" and service.value + and service.value.plugins then + return _M.check_proto_used(service.value.plugins, id, "service", service.value.id) + end + end + end + local key = "/proto/" .. id -- core.log.info("key: ", key) local res, err = core.etcd.delete(key) From e481d9df4fffcbf1d04c4be214c9d75f5ed2ff3d Mon Sep 17 00:00:00 2001 From: Yuansheng Date: Wed, 21 Aug 2019 23:00:16 +0800 Subject: [PATCH 50/50] optimize: local cache global variable. --- lua/apisix/admin/proto.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lua/apisix/admin/proto.lua b/lua/apisix/admin/proto.lua index 9463fdcd350c..d9fcabd1f153 100644 --- a/lua/apisix/admin/proto.lua +++ b/lua/apisix/admin/proto.lua @@ -1,3 +1,5 @@ +local type = type +local ipairs = ipairs local core = require("apisix.core") local get_routes = require("apisix.http.router").http_routes local get_services = require("apisix.http.service").services @@ -123,7 +125,7 @@ function _M.delete(id) end local services, services_ver = get_services() - + core.log.info("services: ", core.json.delay_encode(services, true)) core.log.info("services_ver: ", services_ver)