grpc-http1: A gRPC via HTTP/1 Enabling Library for Go
This library enables using a subset of the functionality of a gRPC server even if it is exposed behind
a reverse proxy that does not support HTTP/2, or only supports it for clients (such as Amazon's ALB).
This is accomplished via adaptive downgrading to the gRPC-web response format. This library allows
instrumenting both clients and servers to that extent.
For a high-level overview, see this Medium post.
Connection Compatibility Overview
The following table shows what can be expected when a client/server instrumented with the capability
offered by this library compared to an unmodified gRPC client/server, both when accessing it directly and
when accessing it via a reverse proxy not supporting HTTP/2.
| Plain Old gRPC Server | HTTP/1 Downgrading gRPC Server |
| direct | behind reverse proxy | direct | behind reverse proxy |
Plain Old gRPC Client | :white_check_mark: | :x: | :white_check_mark: | :x: |
HTTP/1 downgrading gRPC client | :white_check_mark: | :x: | :white_check_mark: | (:white_check_mark:) |
The (✅) in the bottom right cell indicates that a subset of gRPC calls will be possible, but not
all. These include all calls that do not rely on client-side streaming (i.e., all unary and server-streaming calls).
Support for client-side streaming calls is active work in progress.
As you can see, it is possible to instrument the client or the server only without any (functional) regressions - there
may be a small but fairly negligible performance penalty. This means rolling this feature out to your clients and
servers does not need to happen in a strictly synchronous fashion. However, you will only be able to work with a server
behind an HTTP/2-incompatible reverse proxy if both the client and the server have been instrumented via
this library.
Usage
This library has the canonical import path golang.stackrox.io/grpc-http1
. It fully supports Go modules
and requires Go version 1.13+ to be built and used. To add it as a dependency in your current project,
run go get golang.stackrox.io/grpc-http1
.
Server-side
For using this library on the server-side, you'll need to bypass the regular (*grpc.Server).Serve
method
and instead use the ServeHTTP
method of the *grpc.Server
object -- it is experimental, but we found it
to be fairly stable and reliable.
The only exported function in the golang.grpc.io/grpc-http1/server
package is CreateDowngradingHandler
,
which returns a http.Handler
that can be served by a Go HTTP server. It is crucial that this server is
configured to support HTTP/2; otherwise, your clients using the vanilla gRPC client will no longer be able
to talk to it. You can find an example of how to do so in the _integration-tests/
directory.
Client-Side
For connecting to a gRPC server via a client-side proxy, use the ConnectViaProxy
function exported from the
golang.grpc.io/grpc-http1/client
package. This function has the following signature:
func ConnectViaProxy(ctx context.Context, endpoint string, tlsClientConf *tls.Config, opts ...ConnectOption) (*grpc.ClientConn, error)
The first two arguments are the same as for grpc.DialContext
. The third argument specifies the TLS client
config to be used for connecting to the target address. Note that this is different from the usual gRPC API,
which specifies client TLS config via the grpc.WithTransportCredentials
. For a plaintext (unencrypted)
connection to the server, pass a nil
TLS config; however, this does not free you from passing the
grpc.WithInsecure()
gRPC dial option.
The last (variadic) parameter specifies options that modify the dialing behavior. You can pass any gRPC dial
options via client.DialOpts(...)
. Another important option is client.ForceHTTP2()
, which needs to be used for
a plaintext connection to a server that is not HTTP/1.1 capable (e.g., the vanilla gRPC server). Again, check out the
code in the _integration-tests
directory.