gRPC-JSON transcoder
This filter should be configured with the type URL
type.googleapis.com/envoy.extensions.filters.http.grpc_json_transcoder.v3.GrpcJsonTranscoder
.
This is a filter which allows a RESTful JSON API client to send requests to Envoy over HTTP and get proxied to a gRPC service. The HTTP mapping for the gRPC service has to be defined by custom options.
JSON mapping
The protobuf to JSON mapping is defined here. For gRPC stream request parameters, Envoy expects an array of messages, and it returns an array of messages for stream response parameters.
How to generate proto descriptor set
Envoy has to know the proto descriptor of your gRPC service in order to do the transcoding.
To generate a protobuf descriptor set for the gRPC service, you’ll also need to clone the googleapis repository from GitHub before running protoc, as you’ll need annotations.proto in your include path, to define the HTTP mapping.
$ git clone https://github.com/googleapis/googleapis
$ GOOGLEAPIS_DIR=<your-local-googleapis-folder>
Then run protoc to generate the descriptor set. For example using the test bookstore.proto provided in the Envoy repository:
$ protoc -I${GOOGLEAPIS_DIR} -I. --include_imports --include_source_info \
--descriptor_set_out=proto.pb test/proto/bookstore.proto
If you have more than one proto source files, you can pass all of them in one command.
Route configs for transcoded requests
The route configs to be used with the gRPC-JSON transcoder should be identical to the gRPC route.
The requests processed by the transcoder filter will have /<package>.<service>/<method>
path and
POST
method. The route configs for those requests should match on /<package>.<service>/<method>
,
not the incoming request path. This allows the routes to be used for both gRPC requests and
gRPC-JSON transcoded requests.
For example, with the following proto example, the router will process /helloworld.Greeter/SayHello
as the path, so the route config prefix /say
won’t match requests to SayHello
. If you want to
match the incoming request path, set match_incoming_request_route
to true.
syntax = "proto3";
package helloworld;
import "google/api/annotations.proto";
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello(HelloRequest) returns (HelloReply) {
option (google.api.http) = {
get: "/say"
};
}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
Assuming you have checked out the google APIs as described above, and have saved the proto file as
protos/helloworld.proto
you can build it with:
$ protoc -I$(GOOGLEAPIS_DIR) -I. --include_imports --include_source_info \
--descriptor_set_out=protos/helloworld.pb protos/helloworld.proto
Sending arbitrary content
By default, when transcoding occurs, gRPC-JSON encodes the message output of a gRPC service method into
JSON and sets the HTTP response Content-Type
header to application/json
. To send arbitrary content,
a gRPC service method can use
google.api.HttpBody
as its output message type. The implementation needs to set
content_type
(which sets the value of the HTTP response Content-Type
header) and
data
(which sets the HTTP response body) accordingly.
Multiple google.api.HttpBody
can be send by the gRPC server in the server streaming case.
In this case, HTTP response header Content-Type
will use the content-type
from the first
google.api.HttpBody.
Headers
gRPC-JSON forwards the following headers to the gRPC server:
x-envoy-original-path
, containing the value of the original path of HTTP requestx-envoy-original-method
, containing the value of the original method of HTTP request
Sample Envoy configuration
Here’s a sample Envoy configuration that proxies to a gRPC server running on localhost:50051. Port 51051 proxies gRPC requests and uses the gRPC-JSON transcoder filter to provide the RESTful JSON mapping. I.e., you can make either gRPC or RESTful JSON requests to localhost:51051.
1admin:
2 address:
3 socket_address:
4 address: 0.0.0.0
5 port_value: 9901
6
7static_resources:
8 listeners:
9 - name: listener1
10 address:
11 socket_address:
12 address: 0.0.0.0
13 port_value: 51051
14 filter_chains:
15 - filters:
16 - name: envoy.filters.network.http_connection_manager
17 typed_config:
18 "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
19 stat_prefix: grpc_json
20 codec_type: AUTO
21 route_config:
22 name: local_route
23 virtual_hosts:
24 - name: local_service
25 domains: ["*"]
26 routes:
27 - match:
28 prefix: /helloworld.Greeter
29 route:
30 cluster: grpc
31 timeout: 60s
32 http_filters:
33 - name: envoy.filters.http.grpc_json_transcoder
34 typed_config:
35 "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_json_transcoder.v3.GrpcJsonTranscoder
36 proto_descriptor: protos/helloworld.pb
37 services:
38 - helloworld.Greeter
39 print_options:
40 add_whitespace: true
41 always_print_primitive_fields: true
42 always_print_enums_as_ints: false
43 preserve_proto_field_names: false
44 - name: envoy.filters.http.router
45 typed_config:
46 "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
47
48 clusters:
49 - name: grpc
50 type: LOGICAL_DNS
51 lb_policy: ROUND_ROBIN
52 dns_lookup_family: V4_ONLY
53 typed_extension_protocol_options:
54 envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
55 "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions
56 explicit_http_config:
57 http2_protocol_options: {}
58 load_assignment:
59 cluster_name: grpc
60 endpoints:
61 - lb_endpoints:
62 - endpoint:
63 address:
64 socket_address:
65 # WARNING: "docker.for.mac.localhost" has been deprecated from Docker v18.03.0.
66 # If you're running an older version of Docker, please use "docker.for.mac.localhost" instead.
67 # Reference: https://docs.docker.com/docker-for-mac/release-notes/#docker-community-edition-18030-ce-mac59-2018-03-26
68 address: host.docker.internal
69 port_value: 50051