Open-Sourcing Our gRPC-via-HTTP/1 Solution

Malte Isberner
StackRox Engineering
4 min readJul 27, 2020

A couple of months ago, we have described our approach to communicating with gRPC backend services through HTTP/2-incompatible load balancers such as AWS’s ALB. We are pleased to announce that we have now open-sourced the respective functionality, licensed under the Apache License, version 2.0. Head over to GitHub to check out the stackrox/go-grpc-http1 repo, or see the package documentation at pkg.go.dev.

BONUS ANNOUNCEMENT: I will be speaking at gRPC Conf 2020 on this topic today, Mon 07/27, at 12:35pm CDT/10:35am PDT. Please consider attending my talk — it is pre-recorded, but there will be a live Q&A session. You can also find a link to the presentation slides on the sched.com session page.

Let’s now take a look at how you can use our golang.stackrox.io/grpc-http1 library in your Go-based gRPC projects!

Adding the Library to Your Project

If you’re using Go modules, adding the library as a dependency to your project is as simple as running go get golang.stackrox.io/grpc-http1 in your terminal. Of course, you can also manually add golang.stackrox.io/grpc-http1 latest to the require stanza of your go.mod file, followed by running go mod tidy.

Using the Library in Your Client Code

The functionality required on the client-side is in the golang.stackrox.io/grpc-http1/client package. It exports a function ConnectViaProxy, which serves as a replacement for grpc.DialContext. The signature differs in two main aspects:

  • ConnectViaProxy receives a *tls.Config directly rather than via a grpc.WithTransportCredentials dial option. This is due to the fact that the client-side proxy needs to be TLS-aware, as the data will only be TLS-encrypted between the client-side proxy and the remote endpoint. For contacting plaintext/non-TLS endpoints, a nil TLS config may be passed; for additional security, it is however still necessary to additionally pass the grpc.WithInsecure dial option.
  • ConnectViaProxy receives options in a separate format. For most applications, the only relevant option is DialOpts , which wraps a slice of grpc.DialOptions that are then applied to the gRPC connection to the proxy.

ConnectViaProxy returns a *grpc.ClientConn, which can be used like any ordinary connection returned by grpc.Dial or grpc.DialContext. The client-side proxy server handling the transcoding runs in a background Goroutine, and will be shut down automatically when the Close() method is invoked on the connection.

Using the Library in Your Server Code

The functionality for the server side is accessible via the golang.stackrox.io/grpc-http1/server package. This package exports a single function, CreateDowngradingHandler , that receives a *grpc.Server and an http.Handler for handling non-gRPC-requests, and returns an http.Handler.

Being able to pass in a non-gRPC HTTP handler allows multiplexing gRPC and HTTP traffic onto the same port. This is a fairly common pattern and allows, for example, to serve a proper gRPC API as well as a REST mapping for this API (courtesy of grpc-gateway) to be served on a single endpoint. If you do not wish to make use of that functionality but just serve gRPC traffic, simply pass http.NotFoundHandler() as the second argument.

The returned http.Handler can be plugged into a standard Go *http.Server , as long as it is configured to serve HTTP/2 traffic. Note that the resulting server will be able to handle all types of gRPC requests when contacted directly — limits to non-client-streaming RPC calls only apply to requests for which gRPC-web downgrading is necessary (i.e., when made through a gRPC-incompatible reverse proxy).

Caveat: When creating a *grpc.Server, it is common to pass a number of options. Since we are circumventing (*grpc.Server).Serve, options that concern the transport level (such as keep-alive settings) will not have any effect, as the transport level is handled by the Go HTTP/2 server implementation — you will need to look into the possible settings for http.Server or http2.Server to find out equivalents. Options that concern individual RPCs, such as interceptors or message size limits, will however apply.

Final Notes

There is rarely such a thing as bug-free software, and this certainly applies to our published library as well. If you encounter any bugs or surprising behavior, or find the documentation lacking in some regards, please don’t hesitate to file a bug report or feature request via GitHub Issues, or just send a PR our way. If you like this software, of course please also consider starring it!

Please keep in mind that the library comes without any warranty, and it is maintained on a best-effort basis. It should also be noted that it internally makes use of the (*grpc.Server).ServeHTTP method, which is flagged as “EXPERIMENTAL” and is known to have inferior performance. We are investigating possibilities for smart auto-sensing to transparently choose the serving paths offering the highest performance; however, if you are not developing performance-critical applications, this probably does not need to concern you.

We hope that this software will be of practical use to you, and that its source code will make for an interesting read. Again, please check out my gRPC Conf 2020 talk on this subject, and stay tuned for our upcoming post on tackling bidirectional streaming requests via WebSockets!

--

--