Gateway reference¶
gitlab.com/phpboyscout/go/transport/gateway wraps a
grpc-gateway mux so a gRPC
service is also reachable over REST/JSON. It is a thin package: three options,
two entry points, and a clear rule about who owns the gRPC connection.
Entry points¶
func New(ctx context.Context, conn *grpc.ClientConn, register RegisterFunc,
opts ...Option) (http.Handler, error)
func Register(ctx context.Context, id string, controller controls.Controllable,
logger *slog.Logger, conn *grpc.ClientConn,
httpSettings transporthttp.ServerSettings, httpTLS gtls.Pair,
register RegisterFunc, opts ...Option) (*http.Server, error)
RegisterFunc is the only gateway-specific code you write — it calls the
registration function protoc generated for your service:
func(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
return widgetv1.RegisterWidgetServiceHandler(ctx, mux, conn)
}
New versus Register: what each one owns¶
New |
Register |
|
|---|---|---|
| Returns | a plain http.Handler |
a running, supervised *http.Server |
| Serves | nothing — you serve it | its own HTTP listener via transporthttp.Register |
| Health endpoints | none | /healthz, /livez, /readyz, outside the middleware chain |
| Middleware application point | wraps the returned handler directly | threaded into the managed server |
Owns the *grpc.ClientConn |
no — you close it | yes — do not close it yourself |
Because the middleware application point differs, so does what it covers: on the
New path the chain wraps only the gateway handler, because there are no
health endpoints to keep out of it. On the Register path the chain is handed to
the managed HTTP server, which mounts health endpoints outside it — exactly as
transporthttp.Register does for any other handler.
Who closes the gRPC connection¶
Register takes ownership of conn. It closes it if it returns an error,
and registers a controller stop hook so it is closed on shutdown. Closing it
yourself after calling Register is a bug.
The stop hook is registered under id + ":conn", before the HTTP server is
registered. The controller stops services in reverse order, so the HTTP server
drains first and the gRPC connection is closed only afterwards — the gateway
never dials a closed connection mid-drain. The close is guarded by a
sync.Once, so the error path and the stop hook cannot double-close (which gRPC
reports as ErrClientConnClosing).
New takes no ownership: the caller both serves the handler and closes the
connection.
Options¶
| Option | Default | Effect |
|---|---|---|
WithMuxOptions(...runtime.ServeMuxOption) |
none | Passed to runtime.NewServeMux — a custom error handler, header matcher, marshaler, and so on. Accumulates across calls. |
WithDialOptions(...grpc.DialOption) |
none | Extra dial options for the gateway's outbound gRPC connection. Accumulates across calls. |
WithMiddleware(transithttp.Chain) |
none | An HTTP middleware chain over the REST surface. Last call wins. |
WithDialOptions is the mTLS escape hatch¶
Transport security for the gateway's dial is selected automatically. When the
upstream gRPC server requires client certificates, that automatic selection
is not enough: pass
grpc.WithTransportCredentials(credentials.NewTLS(cfg)) with a client
*tls.Config carrying the gateway's own client certificate and the CA pool that
signed the server's certificate. An explicit transport-credentials option
overrides the automatically selected one.
Settings¶
type Settings struct {
HTTP transporthttp.ServerSettings `yaml:"http" json:"http"`
HTTPTLS gtls.Pair `yaml:"http_tls" json:"http_tls"`
GRPC transportgrpc.ServerSettings `yaml:"grpc" json:"grpc"`
GRPCTLS gtls.Pair `yaml:"grpc_tls" json:"grpc_tls"`
}
A gateway spans two listeners — its own HTTP one and the upstream gRPC one — so
Settings is assembled by a caller from two config blocks, not decoded from
one section. That is why it carries yaml/json tags but no mapstructure
tags: the tags exist for documentation and snapshot serialisation, and nothing in
this package decodes into the struct.
Note that Register does not take a Settings. It takes
httpSettings and httpTLS directly, because by the time you call it the gRPC
side is already a live *grpc.ClientConn. Settings is the shape a config
adapter resolves before dialling.
The gateway does not dial for you¶
New and Register both require an already-prepared *grpc.ClientConn. This
package contains no dial logic. Use
transportgrpc.DialLocal for an in-process server,
or go/grpcclient for a remote one.
Errors¶
The package defines no error values of its own. What it returns is:
- whatever your
RegisterFuncreturns — a generated handler registration failing, typically a nil connection or a duplicate pattern; - whatever
transporthttp.Registerreturns on theRegisterpath — an invalid port, a bad served certificate, or an unsupported option.
On either failure Register closes conn before returning.