diff --git a/apisix/plugins/grpc-web.lua b/apisix/plugins/grpc-web.lua new file mode 100644 index 00000000..428b641c --- /dev/null +++ b/apisix/plugins/grpc-web.lua @@ -0,0 +1,147 @@ +-- +-- Licensed to the Apache Software Foundation (ASF) under one or more +-- contributor license agreements. See the NOTICE file distributed with +-- this work for additional information regarding copyright ownership. +-- The ASF licenses this file to You under the Apache License, Version 2.0 +-- (the "License"); you may not use this file except in compliance with +-- the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. + +local ngx = ngx +local ngx_arg = ngx.arg +local core = require("apisix.core") +local req_set_uri = ngx.req.set_uri +local req_set_body_data = ngx.req.set_body_data +local decode_base64 = ngx.decode_base64 +local encode_base64 = ngx.encode_base64 + + +local ALLOW_METHOD_OPTIONS = "OPTIONS" +local ALLOW_METHOD_POST = "POST" +local CONTENT_ENCODING_BASE64 = "base64" +local CONTENT_ENCODING_BINARY = "binary" +local DEFAULT_CORS_CONTENT_TYPE = "application/grpc-web-text+proto" +local DEFAULT_CORS_ALLOW_ORIGIN = "*" +local DEFAULT_CORS_ALLOW_METHODS = ALLOW_METHOD_POST +local DEFAULT_CORS_ALLOW_HEADERS = "content-type,x-grpc-web,x-user-agent" +local DEFAULT_PROXY_CONTENT_TYPE = "application/grpc" + + +local plugin_name = "grpc-web" + +local schema = { + type = "object", + properties = {}, +} + +local grpc_web_content_encoding = { + ["application/grpc-web"] = CONTENT_ENCODING_BINARY, + ["application/grpc-web-text"] = CONTENT_ENCODING_BASE64, + ["application/grpc-web+proto"] = CONTENT_ENCODING_BINARY, + ["application/grpc-web-text+proto"] = CONTENT_ENCODING_BASE64, +} + +local _M = { + version = 0.1, + priority = 505, + name = plugin_name, + schema = schema, +} + +function _M.check_schema(conf) + return core.schema.check(schema, conf) +end + +function _M.access(conf, ctx) + local method = core.request.get_method() + if method == ALLOW_METHOD_OPTIONS then + return 204 + end + + if method ~= ALLOW_METHOD_POST then + -- https://github.com/grpc/grpc-web/blob/master/doc/browser-features.md#cors-support + core.log.error("request method: `", method, "` invalid") + return 400 + end + + local mimetype = core.request.header(ctx, "Content-Type") + local encoding = grpc_web_content_encoding[mimetype] + if not encoding then + core.log.error("request Content-Type: `", mimetype, "` invalid") + return 400 + end + + -- set grpc path + if not (ctx.curr_req_matched and ctx.curr_req_matched[":ext"]) then + core.log.error("routing configuration error, grpc-web plugin only supports ", + "`prefix matching` pattern routing") + return 400 + end + + local path = ctx.curr_req_matched[":ext"] + if path:byte(1) ~= core.string.byte("/") then + path = "/" .. path + end + + req_set_uri(path) + + -- set grpc body + local body, err = core.request.get_body() + if err then + core.log.error("failed to read request body, err: ", err) + return 400 + end + + if encoding == CONTENT_ENCODING_BASE64 then + body = decode_base64(body) + if not body then + core.log.error("failed to decode request body") + return 400 + end + end + + -- set grpc content-type + core.request.set_header(ctx, "Content-Type", DEFAULT_PROXY_CONTENT_TYPE) + -- set grpc body + req_set_body_data(body) + + -- set context variable + ctx.grpc_web_mime = mimetype + ctx.grpc_web_encoding = encoding +end + +function _M.header_filter(conf, ctx) + local method = core.request.get_method() + if method == ALLOW_METHOD_OPTIONS then + core.response.set_header("Access-Control-Allow-Methods", DEFAULT_CORS_ALLOW_METHODS) + core.response.set_header("Access-Control-Allow-Headers", DEFAULT_CORS_ALLOW_HEADERS) + end + core.response.set_header("Access-Control-Allow-Origin", DEFAULT_CORS_ALLOW_ORIGIN) + core.response.set_header("Content-Type", ctx.grpc_web_mime or DEFAULT_CORS_CONTENT_TYPE) +end + +function _M.body_filter(conf, ctx) + -- If the MIME extension type description of the gRPC-Web standard is not obtained, + -- indicating that the request is not based on the gRPC Web specification, + -- the processing of the request body will be ignored + -- https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-WEB.md + -- https://github.com/grpc/grpc-web/blob/master/doc/browser-features.md#cors-support + if not ctx.grpc_web_mime then + return + end + + if ctx.grpc_web_encoding == CONTENT_ENCODING_BASE64 then + local chunk = ngx_arg[1] + chunk = encode_base64(chunk) + ngx_arg[1] = chunk + end +end + +return _M diff --git a/ci/centos7-ci.sh b/ci/centos7-ci.sh index 3167fcec..7c74eba9 100755 --- a/ci/centos7-ci.sh +++ b/ci/centos7-ci.sh @@ -75,6 +75,15 @@ install_dependencies() { # installing grpcurl install_grpcurl + # install nodejs + install_nodejs + + # grpc-web server && client + cd t/plugin/grpc-web + ./setup.sh + # back to home directory + cd ../../../ + # install dependencies git clone https://github.com/iresty/test-nginx.git test-nginx create_lua_deps diff --git a/ci/common.sh b/ci/common.sh index 51bee69a..fe015b16 100644 --- a/ci/common.sh +++ b/ci/common.sh @@ -62,4 +62,16 @@ install_vault_cli () { unzip vault_${VAULT_VERSION}_linux_amd64.zip && mv ./vault /usr/local/bin } +install_nodejs () { + NODEJS_PREFIX="/usr/local/node" + NODEJS_VERSION="16.13.1" + wget https://nodejs.org/dist/v${NODEJS_VERSION}/node-v${NODEJS_VERSION}-linux-x64.tar.xz + tar -xvf node-v${NODEJS_VERSION}-linux-x64.tar.xz + rm -f /usr/local/bin/node + rm -f /usr/local/bin/npm + mv node-v${NODEJS_VERSION}-linux-x64 ${NODEJS_PREFIX} + ln -s ${NODEJS_PREFIX}/bin/node /usr/local/bin/node + ln -s ${NODEJS_PREFIX}/bin/npm /usr/local/bin/npm +} + GRPC_SERVER_EXAMPLE_VER=20210819 diff --git a/ci/linux_openresty_common_runner.sh b/ci/linux_openresty_common_runner.sh index 9cfee16f..ced0b83f 100755 --- a/ci/linux_openresty_common_runner.sh +++ b/ci/linux_openresty_common_runner.sh @@ -57,6 +57,15 @@ do_install() { # install grpcurl install_grpcurl + # install nodejs + install_nodejs + + # grpc-web server && client + cd t/plugin/grpc-web + ./setup.sh + # back to home directory + cd ../../../ + # install vault cli capabilities install_vault_cli } diff --git a/conf/config-default.yaml b/conf/config-default.yaml index cbf97dd6..e1ae1791 100644 --- a/conf/config-default.yaml +++ b/conf/config-default.yaml @@ -362,6 +362,7 @@ plugins: # plugin list (sorted by priority) - response-rewrite # priority: 899 #- dubbo-proxy # priority: 507 - grpc-transcode # priority: 506 + - grpc-web # priority: 505 - prometheus # priority: 500 - datadog # priority: 495 - echo # priority: 412 diff --git a/docs/en/latest/config.json b/docs/en/latest/config.json index dcbfe682..3f86445c 100644 --- a/docs/en/latest/config.json +++ b/docs/en/latest/config.json @@ -51,6 +51,7 @@ "plugins/response-rewrite", "plugins/proxy-rewrite", "plugins/grpc-transcode", + "plugins/grpc-web", "plugins/fault-injection" ] }, diff --git a/docs/en/latest/plugins/grpc-web.md b/docs/en/latest/plugins/grpc-web.md new file mode 100644 index 00000000..17c01116 --- /dev/null +++ b/docs/en/latest/plugins/grpc-web.md @@ -0,0 +1,85 @@ +--- +title: grpc-web +--- + + + +## Summary + +- [**Name**](#name) +- [**How To Enable**](#how-to-enable) +- [**Test Plugin**](#test-plugin) +- [**Disable Plugin**](#disable-plugin) + +## Name + +The `grpc-web` plugin is a proxy plugin used to process [gRPC Web](https://github.com/grpc/grpc-web) client requests to `gRPC Server`. + +gRPC Web Client -> APISIX -> gRPC server + +## How To Enable + +To enable the `gRPC Web` proxy plugin, routing must use the `Prefix matching` pattern (for example: `/*` or `/grpc/example/*`), +Because the `gRPC Web` client will pass the `package name`, `service interface name`, `method name` and other information declared in the `proto` in the URI (for example: `/path/a6.RouteService/Insert`) , +When using `Absolute Match`, it will not be able to hit the plugin and extract the `proto` information. + +```bash +curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' +{ + "uri":"/grpc/web/*", + "plugins":{ + "grpc-web":{} + }, + "upstream":{ + "scheme":"grpc", + "type":"roundrobin", + "nodes":{ + "127.0.0.1:1980":1 + } + } +}' +``` + +## Test Plugin + +- The request method only supports `POST` and `OPTIONS`, refer to: [CORS support](https://github.com/grpc/grpc-web/blob/master/doc/browser-features.md#cors-support). +- The `Content-Type` supports `application/grpc-web`, `application/grpc-web-text`, `application/grpc-web+proto`, `application/grpc-web-text+proto`, refer to: [Protocol differences vs gRPC over HTTP2](https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-WEB.md#protocol-differences-vs-grpc-over-http2). +- Client deployment, refer to: [gRPC-Web Client Runtime Library](https://www.npmjs.com/package/grpc-web) or [Apache APISIX gRPC Web Test Framework](https://github.com/apache/apisix/tree/master/t/plugin/grpc-web). +- After the `gRPC Web` client is deployed, you can initiate a `gRPC Web` proxy request to `APISIX` through `browser` or `node`. + +## Disable Plugin + +Just delete the JSON configuration of `grpc-web` in the plugin configuration. +The APISIX plug-in is hot-reloaded, so there is no need to restart APISIX. + +```shell +curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' +{ + "uri":"/grpc/web/*", + "plugins":{}, + "upstream":{ + "scheme":"grpc", + "type":"roundrobin", + "nodes":{ + "127.0.0.1:1980":1 + } + } +}' +``` diff --git a/docs/zh/latest/config.json b/docs/zh/latest/config.json index 7ac810ca..dbe2a001 100644 --- a/docs/zh/latest/config.json +++ b/docs/zh/latest/config.json @@ -51,6 +51,7 @@ "plugins/response-rewrite", "plugins/proxy-rewrite", "plugins/grpc-transcode", + "plugins/grpc-web", "plugins/fault-injection" ] }, diff --git a/docs/zh/latest/plugins/grpc-web.md b/docs/zh/latest/plugins/grpc-web.md new file mode 100644 index 00000000..206a931b --- /dev/null +++ b/docs/zh/latest/plugins/grpc-web.md @@ -0,0 +1,84 @@ +--- +title: grpc-web +--- + + + +## 摘要 + +- [**定义**](#定义) +- [**如何开启**](#如何开启) +- [**测试插件**](#测试插件) +- [**禁用插件**](#禁用插件) + +## 定义 + +`grpc-web` 插件是一个代理插件,用于转换 [gRPC Web](https://github.com/grpc/grpc-web) 客户端到 `gRPC Server` 的请求。 + +gRPC Web Client -> APISIX -> gRPC server + +## 如何开启 + +启用 `gRPC Web` 代理插件,路由必须使用 `前缀匹配` 模式(例如:`/*` 或 `/grpc/example/*`), +因为 `gRPC Web` 客户端会在 URI 中传递 `proto` 中声明的`包名称`、`服务接口名称`、`方法名称`等信息(例如:`/path/a6.RouteService/Insert`), +使用 `绝对匹配` 时将无法命中插件和提取 `proto` 信息。 + +```bash +curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' +{ + "uri":"/grpc/web/*", + "plugins":{ + "grpc-web":{} + }, + "upstream":{ + "scheme":"grpc", + "type":"roundrobin", + "nodes":{ + "127.0.0.1:1980":1 + } + } +}' +``` + +## 测试插件 + +- 请求方式仅支持 `POST` 和 `OPTIONS`,参考:[CORS support](https://github.com/grpc/grpc-web/blob/master/doc/browser-features.md#cors-support) 。 +- 内容类型支持 `application/grpc-web`、`application/grpc-web-text`、`application/grpc-web+proto`、`application/grpc-web-text+proto`,参考:[Protocol differences vs gRPC over HTTP2](https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-WEB.md#protocol-differences-vs-grpc-over-http2) 。 +- 客户端部署,参考:[gRPC-Web Client Runtime Library](https://www.npmjs.com/package/grpc-web) 或 [Apache APISIX gRPC Web 测试框架](https://github.com/apache/apisix/tree/master/t/plugin/grpc-web) 。 +- 完成 `gRPC Web` 客户端部署后,即可通过 `浏览器` 或 `node` 向 `APISIX` 发起 `gRPC Web` 代理请求。 + +## 禁用插件 + +只需删除插件配置中 `grpc-web` 的JSON配置即可。 APISIX 插件是热加载的,所以不需要重启 APISIX。 + +```bash +curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' +{ + "uri":"/grpc/web/*", + "plugins":{}, + "upstream":{ + "scheme":"grpc", + "type":"roundrobin", + "nodes":{ + "127.0.0.1:1980":1 + } + } +}' +``` diff --git a/t/admin/plugins.t b/t/admin/plugins.t index 2495b539..29038eaa 100644 --- a/t/admin/plugins.t +++ b/t/admin/plugins.t @@ -98,6 +98,7 @@ traffic-split redirect response-rewrite grpc-transcode +grpc-web prometheus datadog echo diff --git a/t/plugin/grpc-web.t b/t/plugin/grpc-web.t new file mode 100644 index 00000000..37e1dacc --- /dev/null +++ b/t/plugin/grpc-web.t @@ -0,0 +1,221 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +use t::APISIX 'no_plan'; + +no_long_string(); +no_shuffle(); +no_root_location(); + +add_block_preprocessor(sub { + my ($block) = @_; + + if (!$block->request) { + $block->set_value("request", "GET /t"); + } + + if ((!defined $block->error_log) && (!defined $block->no_error_log)) { + $block->set_value("no_error_log", "[error]"); + } +}); + +run_tests; + +__DATA__ + +=== TEST 1: set route (default grpc web proxy route) +--- config + location /t { + content_by_lua_block { + + local config = { + uri = "/grpc/*", + upstream = { + scheme = "grpc", + type = "roundrobin", + nodes = { + ["127.0.0.1:50001"] = 1 + } + }, + plugins = { + ["grpc-web"] = {} + } + } + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', ngx.HTTP_PUT, config) + + if code >= 300 then + ngx.status = code + ngx.say(body) + return + end + + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 2: Flush all data through APISIX gRPC-Web Proxy +--- exec +node ./t/plugin/grpc-web/client.js FLUSH +--- response_body +[] + + + +=== TEST 3: Insert first data through APISIX gRPC-Web Proxy +--- exec +node ./t/plugin/grpc-web/client.js POST 1 route01 path01 +--- response_body +[["1",{"name":"route01","path":"path01"}]] + + + +=== TEST 4: Update data through APISIX gRPC-Web Proxy +--- exec +node ./t/plugin/grpc-web/client.js PUT 1 route01 hello +--- response_body +[["1",{"name":"route01","path":"hello"}]] + + + +=== TEST 5: Insert second data through APISIX gRPC-Web Proxy +--- exec +node ./t/plugin/grpc-web/client.js POST 2 route02 path02 +--- response_body +[["1",{"name":"route01","path":"hello"}],["2",{"name":"route02","path":"path02"}]] + + + +=== TEST 6: Insert third data through APISIX gRPC-Web Proxy +--- exec +node ./t/plugin/grpc-web/client.js POST 3 route03 path03 +--- response_body +[["1",{"name":"route01","path":"hello"}],["2",{"name":"route02","path":"path02"}],["3",{"name":"route03","path":"path03"}]] + + + +=== TEST 7: Delete first data through APISIX gRPC-Web Proxy +--- exec +node ./t/plugin/grpc-web/client.js DEL 1 +--- response_body +[["2",{"name":"route02","path":"path02"}],["3",{"name":"route03","path":"path03"}]] + + + +=== TEST 8: Get second data through APISIX gRPC-Web Proxy +--- exec +node ./t/plugin/grpc-web/client.js GET 2 +--- response_body +{"name":"route02","path":"path02"} + + + +=== TEST 9: Get all data through APISIX gRPC-Web Proxy +--- exec +node ./t/plugin/grpc-web/client.js all +--- response_body +[["2",{"name":"route02","path":"path02"}],["3",{"name":"route03","path":"path03"}]] + + + +=== TEST 10: test options request +--- request +OPTIONS /grpc/a6.RouteService/GetAll +--- error_code: 204 +--- response_headers +Access-Control-Allow-Methods: POST +Access-Control-Allow-Headers: content-type,x-grpc-web,x-user-agent +Access-Control-Allow-Origin: * + + + +=== TEST 11: test non-options request +--- request +GET /grpc/a6.RouteService/GetAll +--- error_code: 400 +--- response_headers +Access-Control-Allow-Origin: * +Content-Type: application/grpc-web-text+proto +--- error_log +request method: `GET` invalid + + + +=== TEST 12: test non gRPC Web MIME type request +--- request +POST /grpc/a6.RouteService/GetAll +--- more_headers +Content-Type: application/json +--- error_code: 400 +--- response_headers +Access-Control-Allow-Origin: * +Content-Type: application/grpc-web-text+proto +--- error_log +request Content-Type: `application/json` invalid + + + +=== TEST 13: set route (absolute match) +--- config + location /t { + content_by_lua_block { + + local config = { + uri = "/grpc2/a6.RouteService/GetAll", + upstream = { + scheme = "grpc", + type = "roundrobin", + nodes = { + ["127.0.0.1:50001"] = 1 + } + }, + plugins = { + ["grpc-web"] = {} + } + } + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', ngx.HTTP_PUT, config) + + if code >= 300 then + ngx.status = code + ngx.say(body) + return + end + + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 14: test route (absolute match) +--- request +POST /grpc2/a6.RouteService/GetAll +--- more_headers +Content-Type: application/grpc-web +--- error_code: 400 +--- response_headers +Access-Control-Allow-Origin: * +Content-Type: application/grpc-web-text+proto +--- error_log +routing configuration error, grpc-web plugin only supports `prefix matching` pattern routing diff --git a/t/plugin/grpc-web/a6/routes.pb.go b/t/plugin/grpc-web/a6/routes.pb.go new file mode 100644 index 00000000..b00d62a7 --- /dev/null +++ b/t/plugin/grpc-web/a6/routes.pb.go @@ -0,0 +1,513 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package a6 + +import ( + context "context" + fmt "fmt" + proto "github.com/golang/protobuf/proto" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package + +type Empty struct { + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Empty) Reset() { *m = Empty{} } +func (m *Empty) String() string { return proto.CompactTextString(m) } +func (*Empty) ProtoMessage() {} +func (*Empty) Descriptor() ([]byte, []int) { + return fileDescriptor_078f480fb67d0ab3, []int{0} +} + +func (m *Empty) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Empty.Unmarshal(m, b) +} +func (m *Empty) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Empty.Marshal(b, m, deterministic) +} +func (m *Empty) XXX_Merge(src proto.Message) { + xxx_messageInfo_Empty.Merge(m, src) +} +func (m *Empty) XXX_Size() int { + return xxx_messageInfo_Empty.Size(m) +} +func (m *Empty) XXX_DiscardUnknown() { + xxx_messageInfo_Empty.DiscardUnknown(m) +} + +var xxx_messageInfo_Empty proto.InternalMessageInfo + +type Route struct { + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Route) Reset() { *m = Route{} } +func (m *Route) String() string { return proto.CompactTextString(m) } +func (*Route) ProtoMessage() {} +func (*Route) Descriptor() ([]byte, []int) { + return fileDescriptor_078f480fb67d0ab3, []int{1} +} + +func (m *Route) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Route.Unmarshal(m, b) +} +func (m *Route) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Route.Marshal(b, m, deterministic) +} +func (m *Route) XXX_Merge(src proto.Message) { + xxx_messageInfo_Route.Merge(m, src) +} +func (m *Route) XXX_Size() int { + return xxx_messageInfo_Route.Size(m) +} +func (m *Route) XXX_DiscardUnknown() { + xxx_messageInfo_Route.DiscardUnknown(m) +} + +var xxx_messageInfo_Route proto.InternalMessageInfo + +func (m *Route) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *Route) GetPath() string { + if m != nil { + return m.Path + } + return "" +} + +type Request struct { + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Route *Route `protobuf:"bytes,2,opt,name=route,proto3" json:"route,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Request) Reset() { *m = Request{} } +func (m *Request) String() string { return proto.CompactTextString(m) } +func (*Request) ProtoMessage() {} +func (*Request) Descriptor() ([]byte, []int) { + return fileDescriptor_078f480fb67d0ab3, []int{2} +} + +func (m *Request) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Request.Unmarshal(m, b) +} +func (m *Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Request.Marshal(b, m, deterministic) +} +func (m *Request) XXX_Merge(src proto.Message) { + xxx_messageInfo_Request.Merge(m, src) +} +func (m *Request) XXX_Size() int { + return xxx_messageInfo_Request.Size(m) +} +func (m *Request) XXX_DiscardUnknown() { + xxx_messageInfo_Request.DiscardUnknown(m) +} + +var xxx_messageInfo_Request proto.InternalMessageInfo + +func (m *Request) GetId() string { + if m != nil { + return m.Id + } + return "" +} + +func (m *Request) GetRoute() *Route { + if m != nil { + return m.Route + } + return nil +} + +type Response struct { + Status bool `protobuf:"varint,1,opt,name=status,proto3" json:"status,omitempty"` + Route *Route `protobuf:"bytes,2,opt,name=route,proto3" json:"route,omitempty"` + Routes map[string]*Route `protobuf:"bytes,3,rep,name=routes,proto3" json:"routes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Response) Reset() { *m = Response{} } +func (m *Response) String() string { return proto.CompactTextString(m) } +func (*Response) ProtoMessage() {} +func (*Response) Descriptor() ([]byte, []int) { + return fileDescriptor_078f480fb67d0ab3, []int{3} +} + +func (m *Response) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Response.Unmarshal(m, b) +} +func (m *Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Response.Marshal(b, m, deterministic) +} +func (m *Response) XXX_Merge(src proto.Message) { + xxx_messageInfo_Response.Merge(m, src) +} +func (m *Response) XXX_Size() int { + return xxx_messageInfo_Response.Size(m) +} +func (m *Response) XXX_DiscardUnknown() { + xxx_messageInfo_Response.DiscardUnknown(m) +} + +var xxx_messageInfo_Response proto.InternalMessageInfo + +func (m *Response) GetStatus() bool { + if m != nil { + return m.Status + } + return false +} + +func (m *Response) GetRoute() *Route { + if m != nil { + return m.Route + } + return nil +} + +func (m *Response) GetRoutes() map[string]*Route { + if m != nil { + return m.Routes + } + return nil +} + +func init() { + proto.RegisterType((*Empty)(nil), "a6.Empty") + proto.RegisterType((*Route)(nil), "a6.Route") + proto.RegisterType((*Request)(nil), "a6.Request") + proto.RegisterType((*Response)(nil), "a6.Response") + proto.RegisterMapType((map[string]*Route)(nil), "a6.Response.RoutesEntry") +} + +func init() { proto.RegisterFile("routes.proto", fileDescriptor_078f480fb67d0ab3) } + +var fileDescriptor_078f480fb67d0ab3 = []byte{ + // 307 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x92, 0x4f, 0x4b, 0xf3, 0x40, + 0x10, 0x87, 0xdf, 0x24, 0x6f, 0xd2, 0x76, 0x52, 0x44, 0xe6, 0x20, 0xa1, 0x17, 0xcb, 0x4a, 0xa1, + 0xa7, 0x54, 0x2a, 0x14, 0xa9, 0x27, 0xc5, 0x5a, 0xbc, 0xae, 0x78, 0xf1, 0xb6, 0x9a, 0x81, 0x06, + 0xf3, 0xcf, 0xec, 0x26, 0x90, 0xcf, 0xe6, 0xc7, 0xf2, 0x0b, 0x48, 0x26, 0x39, 0x14, 0x14, 0x73, + 0x9b, 0x3c, 0x79, 0xe6, 0x37, 0x93, 0x21, 0x30, 0x2d, 0xf3, 0xca, 0x90, 0x0e, 0x8b, 0x32, 0x37, + 0x39, 0xda, 0x6a, 0x23, 0x46, 0xe0, 0xee, 0xd2, 0xc2, 0x34, 0x62, 0x05, 0xae, 0x6c, 0x5f, 0x22, + 0xc2, 0xff, 0x4c, 0xa5, 0x14, 0x58, 0x73, 0x6b, 0x39, 0x91, 0x5c, 0xb7, 0xac, 0x50, 0xe6, 0x10, + 0xd8, 0x1d, 0x6b, 0x6b, 0xb1, 0x85, 0x91, 0xa4, 0x8f, 0x8a, 0xb4, 0xc1, 0x13, 0xb0, 0xe3, 0xa8, + 0x6f, 0xb0, 0xe3, 0x08, 0xcf, 0xc1, 0xe5, 0x41, 0xec, 0xfb, 0xeb, 0x49, 0xa8, 0x36, 0x21, 0x87, + 0xcb, 0x8e, 0x8b, 0x4f, 0x0b, 0xc6, 0x92, 0x74, 0x91, 0x67, 0x9a, 0xf0, 0x0c, 0x3c, 0x6d, 0x94, + 0xa9, 0x34, 0x27, 0x8c, 0x65, 0xff, 0x34, 0x98, 0x82, 0x97, 0xe0, 0x75, 0xdf, 0x13, 0x38, 0x73, + 0x67, 0xe9, 0xaf, 0x03, 0x36, 0xfa, 0xd8, 0x4e, 0xd5, 0xbb, 0xcc, 0x94, 0x8d, 0xec, 0xbd, 0xd9, + 0x3d, 0xf8, 0x47, 0x18, 0x4f, 0xc1, 0x79, 0xa7, 0xa6, 0x5f, 0xbc, 0x2d, 0xdb, 0x99, 0xb5, 0x4a, + 0xaa, 0xdf, 0x66, 0x32, 0xdf, 0xda, 0xd7, 0xd6, 0xfa, 0xcb, 0x82, 0x29, 0xc3, 0x27, 0x2a, 0xeb, + 0xf8, 0x8d, 0x70, 0x01, 0xe3, 0x87, 0xa4, 0xd2, 0x87, 0xdb, 0x24, 0x41, 0x6e, 0xe1, 0x93, 0xce, + 0xa6, 0xc7, 0xfb, 0x88, 0x7f, 0x78, 0x01, 0xde, 0x9e, 0xcc, 0x80, 0x24, 0xc0, 0xd9, 0x93, 0x41, + 0xbf, 0xc3, 0x7c, 0xdf, 0x1f, 0xce, 0x02, 0xbc, 0xc7, 0x4c, 0x53, 0x39, 0xac, 0x3d, 0x17, 0x91, + 0x32, 0x34, 0xa8, 0x49, 0x4a, 0xf3, 0xfa, 0x6f, 0xed, 0x6e, 0xf4, 0xe2, 0x86, 0xab, 0x1b, 0xb5, + 0x79, 0xf5, 0xf8, 0xef, 0xb9, 0xfa, 0x0e, 0x00, 0x00, 0xff, 0xff, 0xba, 0xd4, 0xb5, 0x13, 0x4d, + 0x02, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// RouteServiceClient is the client API for RouteService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type RouteServiceClient interface { + FlushAll(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Response, error) + GetAll(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Response, error) + Get(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) + Insert(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) + Update(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) + Remove(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) +} + +type routeServiceClient struct { + cc *grpc.ClientConn +} + +func NewRouteServiceClient(cc *grpc.ClientConn) RouteServiceClient { + return &routeServiceClient{cc} +} + +func (c *routeServiceClient) FlushAll(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Response, error) { + out := new(Response) + err := c.cc.Invoke(ctx, "/a6.RouteService/FlushAll", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *routeServiceClient) GetAll(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Response, error) { + out := new(Response) + err := c.cc.Invoke(ctx, "/a6.RouteService/GetAll", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *routeServiceClient) Get(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) { + out := new(Response) + err := c.cc.Invoke(ctx, "/a6.RouteService/Get", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *routeServiceClient) Insert(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) { + out := new(Response) + err := c.cc.Invoke(ctx, "/a6.RouteService/Insert", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *routeServiceClient) Update(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) { + out := new(Response) + err := c.cc.Invoke(ctx, "/a6.RouteService/Update", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *routeServiceClient) Remove(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) { + out := new(Response) + err := c.cc.Invoke(ctx, "/a6.RouteService/Remove", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// RouteServiceServer is the server API for RouteService service. +type RouteServiceServer interface { + FlushAll(context.Context, *Empty) (*Response, error) + GetAll(context.Context, *Empty) (*Response, error) + Get(context.Context, *Request) (*Response, error) + Insert(context.Context, *Request) (*Response, error) + Update(context.Context, *Request) (*Response, error) + Remove(context.Context, *Request) (*Response, error) +} + +// UnimplementedRouteServiceServer can be embedded to have forward compatible implementations. +type UnimplementedRouteServiceServer struct { +} + +func (*UnimplementedRouteServiceServer) FlushAll(ctx context.Context, req *Empty) (*Response, error) { + return nil, status.Errorf(codes.Unimplemented, "method FlushAll not implemented") +} +func (*UnimplementedRouteServiceServer) GetAll(ctx context.Context, req *Empty) (*Response, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetAll not implemented") +} +func (*UnimplementedRouteServiceServer) Get(ctx context.Context, req *Request) (*Response, error) { + return nil, status.Errorf(codes.Unimplemented, "method Get not implemented") +} +func (*UnimplementedRouteServiceServer) Insert(ctx context.Context, req *Request) (*Response, error) { + return nil, status.Errorf(codes.Unimplemented, "method Insert not implemented") +} +func (*UnimplementedRouteServiceServer) Update(ctx context.Context, req *Request) (*Response, error) { + return nil, status.Errorf(codes.Unimplemented, "method Update not implemented") +} +func (*UnimplementedRouteServiceServer) Remove(ctx context.Context, req *Request) (*Response, error) { + return nil, status.Errorf(codes.Unimplemented, "method Remove not implemented") +} + +func RegisterRouteServiceServer(s *grpc.Server, srv RouteServiceServer) { + s.RegisterService(&_RouteService_serviceDesc, srv) +} + +func _RouteService_FlushAll_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(RouteServiceServer).FlushAll(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/a6.RouteService/FlushAll", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(RouteServiceServer).FlushAll(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _RouteService_GetAll_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(RouteServiceServer).GetAll(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/a6.RouteService/GetAll", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(RouteServiceServer).GetAll(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _RouteService_Get_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Request) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(RouteServiceServer).Get(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/a6.RouteService/Get", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(RouteServiceServer).Get(ctx, req.(*Request)) + } + return interceptor(ctx, in, info, handler) +} + +func _RouteService_Insert_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Request) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(RouteServiceServer).Insert(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/a6.RouteService/Insert", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(RouteServiceServer).Insert(ctx, req.(*Request)) + } + return interceptor(ctx, in, info, handler) +} + +func _RouteService_Update_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Request) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(RouteServiceServer).Update(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/a6.RouteService/Update", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(RouteServiceServer).Update(ctx, req.(*Request)) + } + return interceptor(ctx, in, info, handler) +} + +func _RouteService_Remove_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Request) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(RouteServiceServer).Remove(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/a6.RouteService/Remove", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(RouteServiceServer).Remove(ctx, req.(*Request)) + } + return interceptor(ctx, in, info, handler) +} + +var _RouteService_serviceDesc = grpc.ServiceDesc{ + ServiceName: "a6.RouteService", + HandlerType: (*RouteServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "FlushAll", + Handler: _RouteService_FlushAll_Handler, + }, + { + MethodName: "GetAll", + Handler: _RouteService_GetAll_Handler, + }, + { + MethodName: "Get", + Handler: _RouteService_Get_Handler, + }, + { + MethodName: "Insert", + Handler: _RouteService_Insert_Handler, + }, + { + MethodName: "Update", + Handler: _RouteService_Update_Handler, + }, + { + MethodName: "Remove", + Handler: _RouteService_Remove_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "routes.proto", +} diff --git a/t/plugin/grpc-web/a6/routes.proto b/t/plugin/grpc-web/a6/routes.proto new file mode 100644 index 00000000..d8946c60 --- /dev/null +++ b/t/plugin/grpc-web/a6/routes.proto @@ -0,0 +1,49 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one or more +// contributor license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright ownership. +// The ASF licenses this file to You under the Apache License, Version 2.0 +// (the "License"); you may not use this file except in compliance with +// the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +syntax = "proto3"; + +package a6; + +option go_package = "./;a6"; + +service RouteService { + rpc FlushAll (Empty) returns (Response) {} + rpc GetAll (Empty) returns (Response) {} + rpc Get (Request) returns (Response) {} + rpc Insert (Request) returns (Response) {} + rpc Update (Request) returns (Response) {} + rpc Remove (Request) returns (Response) {} +} + +message Empty {} + +message Route { + string name = 1; + string path = 2; +} + +message Request { + string id = 1; + Route route = 2; +} + +message Response { + bool status = 1; + Route route = 2; + map routes = 3; +} diff --git a/t/plugin/grpc-web/a6/routes_grpc.pb.go b/t/plugin/grpc-web/a6/routes_grpc.pb.go new file mode 100644 index 00000000..e2bdab4c --- /dev/null +++ b/t/plugin/grpc-web/a6/routes_grpc.pb.go @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package a6 + +import ( + "errors" + "golang.org/x/net/context" + + uuid "github.com/satori/go.uuid" +) + +type RouteServer struct { + Routes map[string]*Route +} + +func (rs *RouteServer) init() { + if rs.Routes == nil { + rs.Routes = make(map[string]*Route) + } +} + +func (rs *RouteServer) FlushAll(ctx context.Context, req *Empty) (*Response, error) { + if rs.Routes != nil { + rs.Routes = make(map[string]*Route) + } + return &Response{Routes: rs.Routes, Status: true}, nil +} + +func (rs *RouteServer) GetAll(ctx context.Context, req *Empty) (*Response, error) { + rs.init() + return &Response{Routes: rs.Routes, Status: true}, nil +} + +func (rs *RouteServer) Get(ctx context.Context, req *Request) (*Response, error) { + rs.init() + if len(req.Id) == 0 { + return &Response{Status: false}, errors.New("route ID undefined") + } + + if route, ok := rs.Routes[req.Id]; ok { + return &Response{Status: true, Route: route}, nil + } + + return &Response{Status: false}, errors.New("route not found") +} + +func (rs *RouteServer) Insert(ctx context.Context, req *Request) (*Response, error) { + rs.init() + if len(req.Id) <= 0 { + req.Id = uuid.NewV4().String() + } + rs.Routes[req.Id] = req.Route + return &Response{Status: true, Routes: rs.Routes}, nil +} + +func (rs *RouteServer) Update(ctx context.Context, req *Request) (*Response, error) { + rs.init() + if len(req.Id) == 0 { + return &Response{Status: false}, errors.New("route ID undefined") + } + + if _, ok := rs.Routes[req.Id]; ok { + rs.Routes[req.Id] = req.Route + return &Response{Status: true, Routes: rs.Routes}, nil + } + + return &Response{Status: false}, errors.New("route not found") +} + +func (rs *RouteServer) Remove(ctx context.Context, req *Request) (*Response, error) { + rs.init() + if len(req.Id) == 0 { + return &Response{Status: false}, errors.New("route ID undefined") + } + + if _, ok := rs.Routes[req.Id]; ok { + delete(rs.Routes, req.Id) + return &Response{Status: true, Routes: rs.Routes}, nil + } + + return &Response{Status: false}, errors.New("route not found") +} diff --git a/t/plugin/grpc-web/a6/routes_grpc_web_pb.js b/t/plugin/grpc-web/a6/routes_grpc_web_pb.js new file mode 100644 index 00000000..fff570e1 --- /dev/null +++ b/t/plugin/grpc-web/a6/routes_grpc_web_pb.js @@ -0,0 +1,443 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const grpc = {}; +grpc.web = require('grpc-web'); + +const proto = {}; +proto.a6 = require('./routes_pb.js'); + +/** + * @param {string} hostname + * @param {?Object} credentials + * @param {?grpc.web.ClientOptions} options + * @constructor + * @struct + * @final + */ +proto.a6.RouteServiceClient = + function(hostname, credentials, options) { + if (!options) options = {}; + options.format = 'binary'; + + /** + * @private @const {!grpc.web.GrpcWebClientBase} The client + */ + this.client_ = new grpc.web.GrpcWebClientBase(options); + + /** + * @private @const {string} The hostname + */ + this.hostname_ = hostname; + +}; + + +/** + * @param {string} hostname + * @param {?Object} credentials + * @param {?grpc.web.ClientOptions} options + * @constructor + * @struct + * @final + */ +proto.a6.RouteServicePromiseClient = + function(hostname, credentials, options) { + if (!options) options = {}; + options.format = 'binary'; + + /** + * @private @const {!grpc.web.GrpcWebClientBase} The client + */ + this.client_ = new grpc.web.GrpcWebClientBase(options); + + /** + * @private @const {string} The hostname + */ + this.hostname_ = hostname; + +}; + + +/** + * @const + * @type {!grpc.web.MethodDescriptor< + * !proto.a6.Empty, + * !proto.a6.Response>} + */ +const methodDescriptor_RouteService_FlushAll = new grpc.web.MethodDescriptor( + '/a6.RouteService/FlushAll', + grpc.web.MethodType.UNARY, + proto.a6.Empty, + proto.a6.Response, + /** + * @param {!proto.a6.Empty} request + * @return {!Uint8Array} + */ + function(request) { + return request.serializeBinary(); + }, + proto.a6.Response.deserializeBinary +); + + +/** + * @param {!proto.a6.Empty} request The + * request proto + * @param {?Object} metadata User defined + * call metadata + * @param {function(?grpc.web.RpcError, ?proto.a6.Response)} + * callback The callback function(error, response) + * @return {!grpc.web.ClientReadableStream|undefined} + * The XHR Node Readable Stream + */ +proto.a6.RouteServiceClient.prototype.flushAll = + function(request, metadata, callback) { + return this.client_.rpcCall(this.hostname_ + + '/a6.RouteService/FlushAll', + request, + metadata || {}, + methodDescriptor_RouteService_FlushAll, + callback); +}; + + +/** + * @param {!proto.a6.Empty} request The + * request proto + * @param {?Object=} metadata User defined + * call metadata + * @return {!Promise} + * Promise that resolves to the response + */ +proto.a6.RouteServicePromiseClient.prototype.flushAll = + function(request, metadata) { + return this.client_.unaryCall(this.hostname_ + + '/a6.RouteService/FlushAll', + request, + metadata || {}, + methodDescriptor_RouteService_FlushAll); +}; + + +/** + * @const + * @type {!grpc.web.MethodDescriptor< + * !proto.a6.Empty, + * !proto.a6.Response>} + */ +const methodDescriptor_RouteService_GetAll = new grpc.web.MethodDescriptor( + '/a6.RouteService/GetAll', + grpc.web.MethodType.UNARY, + proto.a6.Empty, + proto.a6.Response, + /** + * @param {!proto.a6.Empty} request + * @return {!Uint8Array} + */ + function(request) { + return request.serializeBinary(); + }, + proto.a6.Response.deserializeBinary +); + + +/** + * @param {!proto.a6.Empty} request The + * request proto + * @param {?Object} metadata User defined + * call metadata + * @param {function(?grpc.web.RpcError, ?proto.a6.Response)} + * callback The callback function(error, response) + * @return {!grpc.web.ClientReadableStream|undefined} + * The XHR Node Readable Stream + */ +proto.a6.RouteServiceClient.prototype.getAll = + function(request, metadata, callback) { + return this.client_.rpcCall(this.hostname_ + + '/a6.RouteService/GetAll', + request, + metadata || {}, + methodDescriptor_RouteService_GetAll, + callback); +}; + + +/** + * @param {!proto.a6.Empty} request The + * request proto + * @param {?Object=} metadata User defined + * call metadata + * @return {!Promise} + * Promise that resolves to the response + */ +proto.a6.RouteServicePromiseClient.prototype.getAll = + function(request, metadata) { + return this.client_.unaryCall(this.hostname_ + + '/a6.RouteService/GetAll', + request, + metadata || {}, + methodDescriptor_RouteService_GetAll); +}; + + +/** + * @const + * @type {!grpc.web.MethodDescriptor< + * !proto.a6.Request, + * !proto.a6.Response>} + */ +const methodDescriptor_RouteService_Get = new grpc.web.MethodDescriptor( + '/a6.RouteService/Get', + grpc.web.MethodType.UNARY, + proto.a6.Request, + proto.a6.Response, + /** + * @param {!proto.a6.Request} request + * @return {!Uint8Array} + */ + function(request) { + return request.serializeBinary(); + }, + proto.a6.Response.deserializeBinary +); + + +/** + * @param {!proto.a6.Request} request The + * request proto + * @param {?Object} metadata User defined + * call metadata + * @param {function(?grpc.web.RpcError, ?proto.a6.Response)} + * callback The callback function(error, response) + * @return {!grpc.web.ClientReadableStream|undefined} + * The XHR Node Readable Stream + */ +proto.a6.RouteServiceClient.prototype.get = + function(request, metadata, callback) { + return this.client_.rpcCall(this.hostname_ + + '/a6.RouteService/Get', + request, + metadata || {}, + methodDescriptor_RouteService_Get, + callback); +}; + + +/** + * @param {!proto.a6.Request} request The + * request proto + * @param {?Object=} metadata User defined + * call metadata + * @return {!Promise} + * Promise that resolves to the response + */ +proto.a6.RouteServicePromiseClient.prototype.get = + function(request, metadata) { + return this.client_.unaryCall(this.hostname_ + + '/a6.RouteService/Get', + request, + metadata || {}, + methodDescriptor_RouteService_Get); +}; + + +/** + * @const + * @type {!grpc.web.MethodDescriptor< + * !proto.a6.Request, + * !proto.a6.Response>} + */ +const methodDescriptor_RouteService_Insert = new grpc.web.MethodDescriptor( + '/a6.RouteService/Insert', + grpc.web.MethodType.UNARY, + proto.a6.Request, + proto.a6.Response, + /** + * @param {!proto.a6.Request} request + * @return {!Uint8Array} + */ + function(request) { + return request.serializeBinary(); + }, + proto.a6.Response.deserializeBinary +); + + +/** + * @param {!proto.a6.Request} request The + * request proto + * @param {?Object} metadata User defined + * call metadata + * @param {function(?grpc.web.RpcError, ?proto.a6.Response)} + * callback The callback function(error, response) + * @return {!grpc.web.ClientReadableStream|undefined} + * The XHR Node Readable Stream + */ +proto.a6.RouteServiceClient.prototype.insert = + function(request, metadata, callback) { + return this.client_.rpcCall(this.hostname_ + + '/a6.RouteService/Insert', + request, + metadata || {}, + methodDescriptor_RouteService_Insert, + callback); +}; + + +/** + * @param {!proto.a6.Request} request The + * request proto + * @param {?Object=} metadata User defined + * call metadata + * @return {!Promise} + * Promise that resolves to the response + */ +proto.a6.RouteServicePromiseClient.prototype.insert = + function(request, metadata) { + return this.client_.unaryCall(this.hostname_ + + '/a6.RouteService/Insert', + request, + metadata || {}, + methodDescriptor_RouteService_Insert); +}; + + +/** + * @const + * @type {!grpc.web.MethodDescriptor< + * !proto.a6.Request, + * !proto.a6.Response>} + */ +const methodDescriptor_RouteService_Update = new grpc.web.MethodDescriptor( + '/a6.RouteService/Update', + grpc.web.MethodType.UNARY, + proto.a6.Request, + proto.a6.Response, + /** + * @param {!proto.a6.Request} request + * @return {!Uint8Array} + */ + function(request) { + return request.serializeBinary(); + }, + proto.a6.Response.deserializeBinary +); + + +/** + * @param {!proto.a6.Request} request The + * request proto + * @param {?Object} metadata User defined + * call metadata + * @param {function(?grpc.web.RpcError, ?proto.a6.Response)} + * callback The callback function(error, response) + * @return {!grpc.web.ClientReadableStream|undefined} + * The XHR Node Readable Stream + */ +proto.a6.RouteServiceClient.prototype.update = + function(request, metadata, callback) { + return this.client_.rpcCall(this.hostname_ + + '/a6.RouteService/Update', + request, + metadata || {}, + methodDescriptor_RouteService_Update, + callback); +}; + + +/** + * @param {!proto.a6.Request} request The + * request proto + * @param {?Object=} metadata User defined + * call metadata + * @return {!Promise} + * Promise that resolves to the response + */ +proto.a6.RouteServicePromiseClient.prototype.update = + function(request, metadata) { + return this.client_.unaryCall(this.hostname_ + + '/a6.RouteService/Update', + request, + metadata || {}, + methodDescriptor_RouteService_Update); +}; + + +/** + * @const + * @type {!grpc.web.MethodDescriptor< + * !proto.a6.Request, + * !proto.a6.Response>} + */ +const methodDescriptor_RouteService_Remove = new grpc.web.MethodDescriptor( + '/a6.RouteService/Remove', + grpc.web.MethodType.UNARY, + proto.a6.Request, + proto.a6.Response, + /** + * @param {!proto.a6.Request} request + * @return {!Uint8Array} + */ + function(request) { + return request.serializeBinary(); + }, + proto.a6.Response.deserializeBinary +); + + +/** + * @param {!proto.a6.Request} request The + * request proto + * @param {?Object} metadata User defined + * call metadata + * @param {function(?grpc.web.RpcError, ?proto.a6.Response)} + * callback The callback function(error, response) + * @return {!grpc.web.ClientReadableStream|undefined} + * The XHR Node Readable Stream + */ +proto.a6.RouteServiceClient.prototype.remove = + function(request, metadata, callback) { + return this.client_.rpcCall(this.hostname_ + + '/a6.RouteService/Remove', + request, + metadata || {}, + methodDescriptor_RouteService_Remove, + callback); +}; + + +/** + * @param {!proto.a6.Request} request The + * request proto + * @param {?Object=} metadata User defined + * call metadata + * @return {!Promise} + * Promise that resolves to the response + */ +proto.a6.RouteServicePromiseClient.prototype.remove = + function(request, metadata) { + return this.client_.unaryCall(this.hostname_ + + '/a6.RouteService/Remove', + request, + metadata || {}, + methodDescriptor_RouteService_Remove); +}; + + +module.exports = proto.a6; + diff --git a/t/plugin/grpc-web/a6/routes_pb.js b/t/plugin/grpc-web/a6/routes_pb.js new file mode 100644 index 00000000..90612d3d --- /dev/null +++ b/t/plugin/grpc-web/a6/routes_pb.js @@ -0,0 +1,766 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var jspb = require('google-protobuf'); +var goog = jspb; +var global = Function('return this')(); + +goog.exportSymbol('proto.a6.Empty', null, global); +goog.exportSymbol('proto.a6.Request', null, global); +goog.exportSymbol('proto.a6.Response', null, global); +goog.exportSymbol('proto.a6.Route', null, global); +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.a6.Empty = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.a6.Empty, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.a6.Empty.displayName = 'proto.a6.Empty'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.a6.Route = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.a6.Route, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.a6.Route.displayName = 'proto.a6.Route'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.a6.Request = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.a6.Request, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.a6.Request.displayName = 'proto.a6.Request'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.a6.Response = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.a6.Response, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.a6.Response.displayName = 'proto.a6.Response'; +} + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.a6.Empty.prototype.toObject = function(opt_includeInstance) { + return proto.a6.Empty.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.a6.Empty} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.a6.Empty.toObject = function(includeInstance, msg) { + var f, obj = { + + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.a6.Empty} + */ +proto.a6.Empty.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.a6.Empty; + return proto.a6.Empty.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.a6.Empty} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.a6.Empty} + */ +proto.a6.Empty.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.a6.Empty.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.a6.Empty.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.a6.Empty} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.a6.Empty.serializeBinaryToWriter = function(message, writer) { + var f = undefined; +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.a6.Route.prototype.toObject = function(opt_includeInstance) { + return proto.a6.Route.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.a6.Route} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.a6.Route.toObject = function(includeInstance, msg) { + var f, obj = { + name: jspb.Message.getFieldWithDefault(msg, 1, ""), + path: jspb.Message.getFieldWithDefault(msg, 2, "") + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.a6.Route} + */ +proto.a6.Route.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.a6.Route; + return proto.a6.Route.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.a6.Route} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.a6.Route} + */ +proto.a6.Route.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {string} */ (reader.readString()); + msg.setName(value); + break; + case 2: + var value = /** @type {string} */ (reader.readString()); + msg.setPath(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.a6.Route.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.a6.Route.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.a6.Route} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.a6.Route.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getName(); + if (f.length > 0) { + writer.writeString( + 1, + f + ); + } + f = message.getPath(); + if (f.length > 0) { + writer.writeString( + 2, + f + ); + } +}; + + +/** + * optional string name = 1; + * @return {string} + */ +proto.a6.Route.prototype.getName = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * @param {string} value + * @return {!proto.a6.Route} returns this + */ +proto.a6.Route.prototype.setName = function(value) { + return jspb.Message.setProto3StringField(this, 1, value); +}; + + +/** + * optional string path = 2; + * @return {string} + */ +proto.a6.Route.prototype.getPath = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); +}; + + +/** + * @param {string} value + * @return {!proto.a6.Route} returns this + */ +proto.a6.Route.prototype.setPath = function(value) { + return jspb.Message.setProto3StringField(this, 2, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.a6.Request.prototype.toObject = function(opt_includeInstance) { + return proto.a6.Request.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.a6.Request} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.a6.Request.toObject = function(includeInstance, msg) { + var f, obj = { + id: jspb.Message.getFieldWithDefault(msg, 1, ""), + route: (f = msg.getRoute()) && proto.a6.Route.toObject(includeInstance, f) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.a6.Request} + */ +proto.a6.Request.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.a6.Request; + return proto.a6.Request.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.a6.Request} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.a6.Request} + */ +proto.a6.Request.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {string} */ (reader.readString()); + msg.setId(value); + break; + case 2: + var value = new proto.a6.Route; + reader.readMessage(value,proto.a6.Route.deserializeBinaryFromReader); + msg.setRoute(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.a6.Request.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.a6.Request.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.a6.Request} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.a6.Request.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getId(); + if (f.length > 0) { + writer.writeString( + 1, + f + ); + } + f = message.getRoute(); + if (f != null) { + writer.writeMessage( + 2, + f, + proto.a6.Route.serializeBinaryToWriter + ); + } +}; + + +/** + * optional string id = 1; + * @return {string} + */ +proto.a6.Request.prototype.getId = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * @param {string} value + * @return {!proto.a6.Request} returns this + */ +proto.a6.Request.prototype.setId = function(value) { + return jspb.Message.setProto3StringField(this, 1, value); +}; + + +/** + * optional Route route = 2; + * @return {?proto.a6.Route} + */ +proto.a6.Request.prototype.getRoute = function() { + return /** @type{?proto.a6.Route} */ ( + jspb.Message.getWrapperField(this, proto.a6.Route, 2)); +}; + + +/** + * @param {?proto.a6.Route|undefined} value + * @return {!proto.a6.Request} returns this +*/ +proto.a6.Request.prototype.setRoute = function(value) { + return jspb.Message.setWrapperField(this, 2, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.a6.Request} returns this + */ +proto.a6.Request.prototype.clearRoute = function() { + return this.setRoute(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.a6.Request.prototype.hasRoute = function() { + return jspb.Message.getField(this, 2) != null; +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.a6.Response.prototype.toObject = function(opt_includeInstance) { + return proto.a6.Response.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.a6.Response} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.a6.Response.toObject = function(includeInstance, msg) { + var f, obj = { + status: jspb.Message.getBooleanFieldWithDefault(msg, 1, false), + route: (f = msg.getRoute()) && proto.a6.Route.toObject(includeInstance, f), + routesMap: (f = msg.getRoutesMap()) ? f.toObject(includeInstance, proto.a6.Route.toObject) : [] + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.a6.Response} + */ +proto.a6.Response.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.a6.Response; + return proto.a6.Response.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.a6.Response} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.a6.Response} + */ +proto.a6.Response.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {boolean} */ (reader.readBool()); + msg.setStatus(value); + break; + case 2: + var value = new proto.a6.Route; + reader.readMessage(value,proto.a6.Route.deserializeBinaryFromReader); + msg.setRoute(value); + break; + case 3: + var value = msg.getRoutesMap(); + reader.readMessage(value, function(message, reader) { + jspb.Map.deserializeBinary(message, reader, jspb.BinaryReader.prototype.readString, jspb.BinaryReader.prototype.readMessage, proto.a6.Route.deserializeBinaryFromReader, "", new proto.a6.Route()); + }); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.a6.Response.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.a6.Response.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.a6.Response} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.a6.Response.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getStatus(); + if (f) { + writer.writeBool( + 1, + f + ); + } + f = message.getRoute(); + if (f != null) { + writer.writeMessage( + 2, + f, + proto.a6.Route.serializeBinaryToWriter + ); + } + f = message.getRoutesMap(true); + if (f && f.getLength() > 0) { + f.serializeBinary(3, writer, jspb.BinaryWriter.prototype.writeString, jspb.BinaryWriter.prototype.writeMessage, proto.a6.Route.serializeBinaryToWriter); + } +}; + + +/** + * optional bool status = 1; + * @return {boolean} + */ +proto.a6.Response.prototype.getStatus = function() { + return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 1, false)); +}; + + +/** + * @param {boolean} value + * @return {!proto.a6.Response} returns this + */ +proto.a6.Response.prototype.setStatus = function(value) { + return jspb.Message.setProto3BooleanField(this, 1, value); +}; + + +/** + * optional Route route = 2; + * @return {?proto.a6.Route} + */ +proto.a6.Response.prototype.getRoute = function() { + return /** @type{?proto.a6.Route} */ ( + jspb.Message.getWrapperField(this, proto.a6.Route, 2)); +}; + + +/** + * @param {?proto.a6.Route|undefined} value + * @return {!proto.a6.Response} returns this +*/ +proto.a6.Response.prototype.setRoute = function(value) { + return jspb.Message.setWrapperField(this, 2, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.a6.Response} returns this + */ +proto.a6.Response.prototype.clearRoute = function() { + return this.setRoute(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.a6.Response.prototype.hasRoute = function() { + return jspb.Message.getField(this, 2) != null; +}; + + +/** + * map routes = 3; + * @param {boolean=} opt_noLazyCreate Do not create the map if + * empty, instead returning `undefined` + * @return {!jspb.Map} + */ +proto.a6.Response.prototype.getRoutesMap = function(opt_noLazyCreate) { + return /** @type {!jspb.Map} */ ( + jspb.Message.getMapField(this, 3, opt_noLazyCreate, + proto.a6.Route)); +}; + + +/** + * Clears values from the map. The map will be non-null. + * @return {!proto.a6.Response} returns this + */ +proto.a6.Response.prototype.clearRoutesMap = function() { + this.getRoutesMap().clear(); + return this;}; + + +goog.object.extend(exports, proto.a6); diff --git a/t/plugin/grpc-web/client.js b/t/plugin/grpc-web/client.js new file mode 100644 index 00000000..7f37ff06 --- /dev/null +++ b/t/plugin/grpc-web/client.js @@ -0,0 +1,178 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +global.XMLHttpRequest = require('xhr2') + +const {Empty, Request, Route} = require('./a6/routes_pb') +const {RouteServiceClient} = require('./a6/routes_grpc_web_pb') + +const FUNCTION_ALL = "ALL" +const FUNCTION_GET = "GET" +const FUNCTION_POST = "POST" +const FUNCTION_PUT = "PUT" +const FUNCTION_DEL = "DEL" +const FUNCTION_FLUSH = "FLUSH" + +const functions = [FUNCTION_ALL, FUNCTION_GET, FUNCTION_POST, FUNCTION_PUT, FUNCTION_DEL, FUNCTION_FLUSH] + +class gRPCWebClient { + constructor() { + this.client = new RouteServiceClient("http://127.0.0.1:1984/grpc", null, null) + }; + + flush() { + let request = new Empty() + this.client.flushAll(request, {}, function (error, response) { + if (error) { + console.log(error) + return + } + console.log(JSON.stringify(response.toObject().routesMap)) + }); + } + + all() { + let request = new Empty() + this.client.getAll(request, {}, function (error, response) { + if (error) { + console.log(error) + return + } + console.log(JSON.stringify(response.toObject().routesMap)) + }); + } + + get(params) { + if (params[0] === null) { + console.log("route ID invalid") + return + } + let request = new Request() + request.setId(params[0]) + this.client.get(request, {}, function (error, response) { + if (error) { + console.log(error) + return + } + console.log(JSON.stringify(response.toObject().route)) + }); + } + + post(params) { + if (params[0] === null) { + console.log("route ID invalid") + return + } + if (params[1] === null) { + console.log("route Name invalid") + return + } + if (params[2] === null) { + console.log("route Path invalid") + return + } + let request = new Request() + let route = new Route() + request.setId(params[0]) + route.setName(params[1]) + route.setPath(params[2]) + request.setRoute(route) + this.client.insert(request, {}, function (error, response) { + if (error) { + console.log(error) + return + } + console.log(JSON.stringify(response.toObject().routesMap)) + }); + } + + put(params) { + if (params[0] === null) { + console.log("route ID invalid") + return + } + if (params[1] === null) { + console.log("route Name invalid") + return + } + if (params[2] === null) { + console.log("route Path invalid") + return + } + let request = new Request() + let route = new Route() + request.setId(params[0]) + route.setName(params[1]) + route.setPath(params[2]) + request.setRoute(route) + this.client.update(request, {}, function (error, response) { + if (error) { + console.log(error) + return + } + console.log(JSON.stringify(response.toObject().routesMap)) + }) + } + + del() { + if (params[0] === null) { + console.log("route ID invalid") + return + } + let request = new Request() + request.setId(params[0]) + this.client.remove(request, {}, function (error, response) { + if (error) { + console.log(error) + return + } + console.log(JSON.stringify(response.toObject().routesMap)) + }) + } +} + + +const arguments = process.argv.splice(2) + +if (arguments.length === 0) { + console.log("please input dispatch function, e.g: node client.js insert arg_id arg_name arg_path") + return +} + +const func = arguments[0].toUpperCase() +if (!functions.includes(func)) { + console.log("dispatch function not found") + return +} + +const params = arguments.splice(1) + +let grpc = new gRPCWebClient(); + +if (func === FUNCTION_GET) { + grpc.get(params) +} else if (func === FUNCTION_POST) { + grpc.post(params) +} else if (func === FUNCTION_PUT) { + grpc.put(params) +} else if (func === FUNCTION_DEL) { + grpc.del(params) +} else if (func === FUNCTION_FLUSH) { + grpc.flush() +} else { + grpc.all() +} diff --git a/t/plugin/grpc-web/go.mod b/t/plugin/grpc-web/go.mod new file mode 100644 index 00000000..cece5f1d --- /dev/null +++ b/t/plugin/grpc-web/go.mod @@ -0,0 +1,13 @@ +module apisix.apache.org/plugin/grpc-web + +go 1.16 + +require ( + github.com/golang/protobuf v1.4.3 + github.com/satori/go.uuid v1.2.0 // indirect + golang.org/x/net v0.0.0-20200822124328-c89045814202 // indirect + golang.org/x/sys v0.0.0-20210423082822-04245dca01da // indirect + golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 // indirect + golang.org/x/text v0.3.6 // indirect + google.golang.org/grpc v1.43.0 +) diff --git a/t/plugin/grpc-web/go.sum b/t/plugin/grpc-web/go.sum new file mode 100644 index 00000000..626dadfd --- /dev/null +++ b/t/plugin/grpc-web/go.sum @@ -0,0 +1,125 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20211216030914-fe4d6282115f h1:hEYJvxw1lSnWIl8X9ofsYMklzaDs90JI2az5YMd4fPM= +golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.43.0 h1:Eeu7bZtDZ2DpRCsLhUlcrLnvYaMK1Gz86a+hMVvELmM= +google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/t/plugin/grpc-web/package-lock.json b/t/plugin/grpc-web/package-lock.json new file mode 100644 index 00000000..dade6174 --- /dev/null +++ b/t/plugin/grpc-web/package-lock.json @@ -0,0 +1,52 @@ +{ + "name": "apisix-grpc-web", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "apisix.apache.org/plugin/grpc-web", + "dependencies": { + "google-protobuf": "^3.19.1", + "grpc-web": "^1.3.0", + "xhr2": "^0.2.1" + } + }, + "node_modules/google-protobuf": { + "version": "3.19.1", + "resolved": "https://registry.npmmirror.com/google-protobuf/download/google-protobuf-3.19.1.tgz?cache=0&sync_timestamp=1635869461201&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fgoogle-protobuf%2Fdownload%2Fgoogle-protobuf-3.19.1.tgz", + "integrity": "sha1-WvU5DoIGxEbY9J/rr/1Lf0rCj0E=", + "license": "BSD-3-Clause" + }, + "node_modules/grpc-web": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/grpc-web/download/grpc-web-1.3.0.tgz", + "integrity": "sha1-TDbZfnp7YQKn30Y+eCLNhtT2Xtg=", + "license": "Apache-2.0" + }, + "node_modules/xhr2": { + "version": "0.2.1", + "resolved": "https://registry.npm.taobao.org/xhr2/download/xhr2-0.2.1.tgz", + "integrity": "sha1-TnOtxPnP7Jy9IVf3Pv3OOl8QipM=", + "engines": { + "node": ">= 6" + } + } + }, + "dependencies": { + "google-protobuf": { + "version": "3.19.1", + "resolved": "https://registry.npmmirror.com/google-protobuf/download/google-protobuf-3.19.1.tgz?cache=0&sync_timestamp=1635869461201&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fgoogle-protobuf%2Fdownload%2Fgoogle-protobuf-3.19.1.tgz", + "integrity": "sha1-WvU5DoIGxEbY9J/rr/1Lf0rCj0E=" + }, + "grpc-web": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/grpc-web/download/grpc-web-1.3.0.tgz", + "integrity": "sha1-TDbZfnp7YQKn30Y+eCLNhtT2Xtg=" + }, + "xhr2": { + "version": "0.2.1", + "resolved": "https://registry.npm.taobao.org/xhr2/download/xhr2-0.2.1.tgz", + "integrity": "sha1-TnOtxPnP7Jy9IVf3Pv3OOl8QipM=" + } + } +} diff --git a/t/plugin/grpc-web/package.json b/t/plugin/grpc-web/package.json new file mode 100644 index 00000000..29b035cd --- /dev/null +++ b/t/plugin/grpc-web/package.json @@ -0,0 +1,8 @@ +{ + "name": "apisix-grpc-web", + "dependencies": { + "google-protobuf": "^3.19.1", + "grpc-web": "^1.3.0", + "xhr2": "^0.2.1" + } +} diff --git a/t/plugin/grpc-web/server.go b/t/plugin/grpc-web/server.go new file mode 100644 index 00000000..100b9579 --- /dev/null +++ b/t/plugin/grpc-web/server.go @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package main + +import ( + "flag" + "log" + "net" + + "apisix.apache.org/plugin/grpc-web/a6" + "google.golang.org/grpc" + "google.golang.org/grpc/reflection" +) + +var grpcListenAddress string + +func init() { + flag.StringVar(&grpcListenAddress, "listen", ":50001", "address for grpc") +} + +func main() { + flag.Parse() + listen, err := net.Listen("tcp", grpcListenAddress) + if err != nil { + log.Fatalf("failed to listen gRPC-Web Test Server: %v", err) + } else { + log.Printf("successful to listen gRPC-Web Test Server, address %s", grpcListenAddress) + } + + s := a6.RouteServer{} + grpcServer := grpc.NewServer() + reflection.Register(grpcServer) + a6.RegisterRouteServiceServer(grpcServer, &s) + + if err = grpcServer.Serve(listen); err != nil { + log.Fatalf("failed to serve: %v", err) + } +} diff --git a/t/plugin/grpc-web/setup.sh b/t/plugin/grpc-web/setup.sh new file mode 100755 index 00000000..4305ee49 --- /dev/null +++ b/t/plugin/grpc-web/setup.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +set -ex + +npm install + +CGO_ENABLED=0 go build -o grpc-web-server server.go + +./grpc-web-server > grpc-web-server.log 2>&1 || (cat grpc-web-server.log && exit 1)&