gRPC
Octopus proxies gRPC transparently over HTTP/2. It does not understand your
.proto files — it forwards the gRPC framing, metadata, and trailers between client and
upstream. There is no transcoding (JSON↔gRPC) and no reflection proxy.
Detection01
A request is treated as gRPC when its Content-Type begins with application/grpc. The
gRPC-Web content types (application/grpc-web, application/grpc-web-text) are also
recognized at detection time. Detection happens before body buffering so the proxy
can use the HTTP/2 path.
How it is handled02
Validate. Only POST is accepted. gRPC paths must be /{package.Service}/{Method};
anything else is rejected with a gRPC UNIMPLEMENTED status (the gateway always replies
with HTTP 200 and the gRPC status in headers, per the gRPC spec).
Route. The upstream is resolved by the normal router using the request host and the gRPC path — exactly like an HTTP route. Point a route at your gRPC upstream.
Forward headers. All headers are forwarded except hop-by-hop ones (connection,
keep-alive, transfer-encoding, upgrade, the proxy-auth headers). te: trailers is
always set, which gRPC over HTTP/2 requires. This means grpc-* metadata,
authorization, and custom metadata pass straight through.
Propagate deadlines. If the request carries a grpc-timeout header, it is parsed and
used as the upstream call timeout and re-sent to the upstream. With no grpc-timeout, a
default 30-second timeout applies. On timeout the gateway returns gRPC
DEADLINE_EXCEEDED.
Send over HTTP/2. The request is sent to the upstream over a pooled, multiplexed HTTP/2 connection. The upstream connection is plaintext HTTP/2 (h2c) — the gateway opens a TCP connection and performs an HTTP/2 handshake directly. The upstream response body is streamed back to the client.
If the upstream cannot be reached, the gateway returns gRPC UNAVAILABLE.
The request body is collected (buffered) in full before being sent to the upstream. This works for unary RPCs and is fine for server-streaming responses (the response is streamed back), but it means client-streaming and bidirectional-streaming RPCs are not truly streamed — the client's stream is buffered before forwarding. The handler code documents this as the known limitation of the current implementation.
Example route03
A gRPC upstream is just an HTTP/2 upstream and a route whose path is the gRPC service:
upstreams:
- name: users-grpc
instances:
- id: users-grpc-1
host: 127.0.0.1
port: 50051
routes:
- path: /users.UserService
upstream: users-grpcThere is no protocol flag on the route — detection is driven by the application/grpc
content type. The upstream must speak plaintext HTTP/2 (h2c).
Configuration block04
A top-level grpc configuration block exists (enabled, max_message_size,
enable_reflection, enable_grpc_web, deadline_propagation, and a services
service→upstream map). See gRPC configuration for the schema.
In the current build these grpc config fields are parsed and surfaced via the admin
API, but the live gRPC proxy path does not read them: it uses built-in defaults,
routes via the normal router (not the services map), and always propagates
grpc-timeout when present. Treat enable_reflection, enable_grpc_web,
max_message_size, and services as not yet wired into the proxy.
Limitations05
No reflection proxy. The server reflection service is not proxied or synthesized.
No transcoding. There is no JSON-to-gRPC (HTTP/JSON ↔ gRPC) translation.
gRPC-Web is not handled in the proxy path. Although gRPC-Web content types are detected, the proxy does not perform gRPC-Web ↔ gRPC framing translation.
Streaming is partial. Unary and server-streaming work; client-streaming and bidirectional streaming are buffered on the request side rather than streamed.
Plaintext upstream only. The HTTP/2 upstream connection is h2c (no TLS to the upstream).