diff --git a/apisix/admin/proto.lua b/apisix/admin/proto.lua index 15d6d3456200..132db68a1406 100644 --- a/apisix/admin/proto.lua +++ b/apisix/admin/proto.lua @@ -20,6 +20,7 @@ local core = require("apisix.core") local utils = require("apisix.admin.utils") local get_routes = require("apisix.router").http_routes local get_services = require("apisix.http.service").services +local compile_proto = require("apisix.plugins.grpc-transcode.proto").compile_proto local tostring = tostring @@ -53,6 +54,11 @@ local function check_conf(id, conf, need_id) return nil, {error_msg = "invalid configuration: " .. err} end + local ok, err = compile_proto(conf.content) + if not ok then + return nil, {error_msg = "invalid content: " .. err} + end + return need_id and id or true end diff --git a/apisix/plugins/grpc-transcode/proto.lua b/apisix/plugins/grpc-transcode/proto.lua index 9f82890605e1..ec515c740917 100644 --- a/apisix/plugins/grpc-transcode/proto.lua +++ b/apisix/plugins/grpc-transcode/proto.lua @@ -17,6 +17,7 @@ local core = require("apisix.core") local config_util = require("apisix.core.config_util") local protoc = require("protoc") +local pcall = pcall local protos @@ -25,6 +26,26 @@ local lrucache_proto = core.lrucache.new({ }) +local function compile_proto(content) + local _p = protoc.new() + -- the loaded proto won't appears in _p.loaded without a file name after lua-protobuf=0.3.2, + -- which means _p.loaded after _p:load(content) is always empty, so we can pass a fake file + -- name to keep the code below unchanged, or we can create our own load function with returning + -- the loaded DescriptorProto table additionally, see more details in + -- https://github.com/apache/apisix/pull/4368 + local ok, res = pcall(_p.load, _p, content, "filename for loaded") + if not ok then + return nil, res + end + + if not res or not _p.loaded then + return nil, "failed to load proto content" + end + + return _p.loaded +end + + local function create_proto_obj(proto_id) if protos.values == nil then return nil @@ -42,24 +63,14 @@ local function create_proto_obj(proto_id) return nil, "failed to find proto by id: " .. proto_id end - local _p = protoc.new() - -- the loaded proto won't appears in _p.loaded without a file name after lua-protobuf=0.3.2, - -- which means _p.loaded after _p:load(content) is always empty, so we can pass a fake file - -- name to keep the code below unchanged, or we can create our own load function with returning - -- the loaded DescriptorProto table additionally, see more details in - -- https://github.com/apache/apisix/pull/4368 - local res = _p:load(content, "filename for loaded") - - if not res or not _p.loaded then - return nil, "failed to load proto content" - end - - - return _p.loaded + return compile_proto(content) end -local _M = {version = 0.1} +local _M = { + version = 0.1, + compile_proto = compile_proto, +} function _M.fetch(proto_id) diff --git a/t/admin/proto.t b/t/admin/proto.t index 1ada1ee6e339..bab49c933b6c 100644 --- a/t/admin/proto.t +++ b/t/admin/proto.t @@ -22,6 +22,18 @@ no_root_location(); no_shuffle(); log_level("info"); +add_block_preprocessor(sub { + my ($block) = @_; + + if (!$block->request) { + $block->set_value("request", "GET /t"); + } + + if (!$block->no_error_log && !$block->error_log) { + $block->set_value("no_error_log", "[error]\n[alert]"); + } +}); + run_tests; __DATA__ @@ -36,20 +48,20 @@ __DATA__ ngx.HTTP_PUT, [[{ "content": "syntax = \"proto3\"; - package proto; - message HelloRequest{ + package proto; + message HelloRequest{ string name = 1; - } + } - message HelloResponse{ + message HelloResponse{ int32 code = 1; string msg = 2; - } - // The greeting service definition. - service Hello { - // Sends a greeting + } + // The greeting service definition. + service Hello { + // Sends a greeting rpc SayHi (HelloRequest) returns (HelloResponse){} - }" + }" }]], [[ { @@ -67,12 +79,8 @@ __DATA__ ngx.say("[put proto] code: ", code, " message: ", message) } } ---- request -GET /t --- response_body [put proto] code: 200 message: passed ---- no_error_log -[error] @@ -99,12 +107,8 @@ GET /t ngx.say("[delete proto] code: ", code, " message: ", message) } } ---- request -GET /t --- response_body [delete proto] code: 200 message: passed ---- no_error_log -[error] @@ -117,21 +121,21 @@ GET /t local code, message = t('/apisix/admin/proto/2', ngx.HTTP_PUT, [[{ - "content": "syntax = \"proto3\"; - package proto; - message HelloRequest{ - string name = 1; - } + "content": "syntax = \"proto3\"; + package proto; + message HelloRequest{ + string name = 1; + } - message HelloResponse{ - int32 code = 1; - string msg = 2; - } - // The greeting service definition. - service Hello { - // Sends a greeting - rpc SayHi (HelloRequest) returns (HelloResponse){} - }" + message HelloResponse{ + int32 code = 1; + string msg = 2; + } + // The greeting service definition. + service Hello { + // Sends a greeting + rpc SayHi (HelloRequest) returns (HelloResponse){} + }" }]], [[ { @@ -194,11 +198,42 @@ GET /t ngx.say("[delete proto] code: ", code) } } ---- request -GET /t --- response_body [put proto] code: 200 message: passed [route refer proto] code: 200 message: passed [delete proto] code: 400 ---- no_error_log -[error] + + + +=== TEST 4: reject invalid proto +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local etcd = require("apisix.core.etcd") + local code, message = t('/apisix/admin/proto/1', + ngx.HTTP_PUT, + [[{ + "content": "syntax = \"proto3\"; + package proto; + message HelloRequest{ + string name = 1; + } + + message HelloResponse{ + int32 code = 1; + string msg = 1; + }" + }]] + ) + + if code ~= 200 then + ngx.status = code + end + + ngx.say(message) + } + } +--- error_code: 400 +--- response_body eval +qr/invalid content:/