Documentation ¶
Overview ¶
Package grpctunnel provides APIs to support tunneling of gRPC services: carrying gRPC calls over a gRPC stream.
A tunnel is a gRPC stream on which you can send other RPC requests, in either direction.
Forward Tunnels ¶
A "forward" tunnel is a tunnel in the normal direction, where the gRPC client initiates RPCs by sending requests, and the gRPC server acts on them and responses.
Forward tunnels allow a client to pin RPCs to a single server since they are all sent over a single stream. Forward tunnels work like so:
- Client creates a new tunnel by calling NewChannel. This issues an RPC that establishes the forward tunnel. The RPC is a full-duplex bidirectional stream, so can support all manner of streaming RPCs over the tunnel.
- RPC stubs can then be created using this tunnel. All RPCs issued on it are transmitted over the stream that was established by the RPC mentioned above.
- Closing the tunnel channel also results in the underlying stream being closed.
Reverse Tunnels ¶
A "reverse" tunnel is a tunnel in the opposite direction of normal: the gRPC server is the actor that initiates RPCs by sending requests, and a gRPC client handles these requests and sends responses.
Reverse tunnels allow for interesting "server push" scenarios, where the pushed messages can be more advanced than "fire and forget" but actually need responses/acknowledgements. Reverse tunnels work like so:
- Client issues an RPC that establishes the reverse tunnel. The RPC is a full-duplex bidirectional stream, so can support all manner of streaming RPCs over the tunnel. (See NewReverseTunnelServer.)
- Server registers the reverse tunnel for use as a channel. (See relevant fields in TunnelServiceHandlerOptions.)
- RPC stubs can be created inside the server, using these reverse tunnel channels. All RPCs issued on this channel are transmitted over the tunnel, on the stream that was established in step 1.
- Shutting down the reverse tunnel server (which is in the client process that established the tunnels) will close all tunnels and their underlying streams.
Service Handler ¶
The TunnelServiceHandler is what implements the tunneling protocol. You can register the RPC services available for forward tunnels with it. You can also use it to access reverse tunnels, for the server to send RPCs back to the client. See NewTunnelServiceHandler.
This is the value that is registered with a *grpc.Server to expose the Tunnel service.
Index ¶
- func TunnelMetadataFromIncomingContext(ctx context.Context) (metadata.MD, bool)
- func TunnelMetadataFromOutgoingContext(ctx context.Context) (metadata.MD, bool)
- func WithTunnelChannel(ch *TunnelChannel) grpc.CallOption
- type PendingChannel
- type ReverseClientConnInterface
- type ReverseTunnelServer
- type TunnelChannel
- type TunnelOption
- type TunnelServiceHandler
- func (s *TunnelServiceHandler) AllReverseTunnels() []TunnelChannel
- func (s *TunnelServiceHandler) AsChannel() ReverseClientConnInterface
- func (s *TunnelServiceHandler) InitiateShutdown()
- func (s *TunnelServiceHandler) KeyAsChannel(key interface{}) ReverseClientConnInterface
- func (s *TunnelServiceHandler) RegisterService(desc *grpc.ServiceDesc, srv interface{})
- func (s *TunnelServiceHandler) Service() tunnelpb.TunnelServiceServer
- type TunnelServiceHandlerOptions
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func TunnelMetadataFromIncomingContext ¶
TunnelMetadataFromIncomingContext provides server-side access to the request metadata used to open a tunnel. This can be used from server interceptors or handlers that are handling tunneled requests in a tunnel.
func TunnelMetadataFromOutgoingContext ¶
TunnelMetadataFromOutgoingContext provides client-side access to the request metadata used to open a forward tunnel. This can be used from client interceptors that are processing tunneled requests in a forward tunnel.
For client interceptors in a reverse tunnel, you can use metadata.FromOutgoingContext with the context passed to the interceptor or handler.
func WithTunnelChannel ¶
func WithTunnelChannel(ch *TunnelChannel) grpc.CallOption
WithTunnelChannel provides the caller access to the specific TunnelChanel that was used to send a tunneled RPC. This is similar to using TunnelChannelFromContext except it can be used with unary RPCs, where the invoker never has access to values added to the request context. When the RPC completes, the given location will be updated with the channel that handled the request. If the channel that handled the request was not a tunnel, the location is left unchanged.
The pointer must point to an allocated location. Passing a nil pointer will result in a panic when an RPC is invoked with the returned option.
Types ¶
type PendingChannel ¶ added in v0.3.0
type PendingChannel interface {
Start(ctx context.Context, opts ...grpc.CallOption) (TunnelChannel, error)
}
PendingChannel is an un-started channel. Calling Start will establish the tunnel and returns a value that implements grpc.ClientConnInterface, so it can be used to create stubs and issue other RPCs that are all carried over a single tunnel stream.
The given context defines the lifetime of the stream and therefore of the channel; if the context times out or is cancelled, the channel will be closed.
func NewChannel ¶
func NewChannel(stub tunnelpb.TunnelServiceClient, opts ...TunnelOption) PendingChannel
NewChannel creates a new pending channel that, once started, can be used for issuing RPCs.
type ReverseClientConnInterface ¶
type ReverseClientConnInterface interface { grpc.ClientConnInterface // Ready returns true if this channel is backed by at least one reverse // tunnel. Returns false if there are no active, matching reverse tunnels. Ready() bool // WaitForReady blocks until either this channel is ready or the given // context is cancelled or times out. If the latter, the context error is // returned. Otherwise, nil is returned. If the channel is immediately // ready, this will not block and will immediately return nil. WaitForReady(context.Context) error }
ReverseClientConnInterface is an RPC channel that reports if there are any active reverse tunnels. If there are no available tunnels, the channel will not be ready. When tunnels are ready, RPCs will be balanced across all such tunnels in a round-robin fashion.
type ReverseTunnelServer ¶
type ReverseTunnelServer struct {
// contains filtered or unexported fields
}
ReverseTunnelServer is a server that can run on the client side of a gRPC connection, handling requests sent over a reverse tunnel.
Callers must first call NewReverseTunnelServer to create a new instance. Then callers register server handlers with the server and use the Serve method to actually create a reverse tunnel and handle requests.
func NewReverseTunnelServer ¶
func NewReverseTunnelServer(stub tunnelpb.TunnelServiceClient, opts ...TunnelOption) *ReverseTunnelServer
NewReverseTunnelServer creates a new server that uses the given stub to create reverse tunnels.
func (*ReverseTunnelServer) GracefulStop ¶
func (s *ReverseTunnelServer) GracefulStop()
GracefulStop initiates graceful shutdown and waits for the server to stop. During graceful shutdown, no new operations will be allowed to start, but existing operations may proceed. The server stops after all existing operations complete.
To given existing operations a time limit, the caller can also arrange to call Stop after some deadline, which forcibly terminates existing operations.
func (*ReverseTunnelServer) RegisterService ¶
func (s *ReverseTunnelServer) RegisterService(desc *grpc.ServiceDesc, srv interface{})
RegisterService implements grpc.ServiceRegistrar. This allows you to use this server when calling registration functions generated by the Go gRPC plugin for protobuf. For example:
revTunnelSvr := NewReverseTunnelServer(tunnelClient) foo.RegisterFooServiceServer(revTunnelSvr, myFooServiceImpl{})
All services registered will be available for the other end of the tunnel to invoke.
func (*ReverseTunnelServer) Serve ¶
func (s *ReverseTunnelServer) Serve(ctx context.Context, opts ...grpc.CallOption) (started bool, err error)
Serve creates a new reverse tunnel and handles incoming RPC requests that arrive over that reverse tunnel. Since this is a reverse tunnel, RPC requests are initiated by the server, and this end (the client) processes the requests and sends responses.
The boolean return value indicates whether the tunnel was created or not. If false, the returned error indicates the reason the tunnel could not be created. If true, the tunnel could be created and requests could be serviced. In this case, the returned error indicates the reason the tunnel was stopped. This will be nil if the stream was closed by the other side of the tunnel (the server, acting as an RPC client, hanging up).
Reasons for the tunnel ending abnormally include detection of invalid usage of the stream (RPC client sending references to invalid stream IDs or sending frames for a stream ID in improper order) or if the stream itself fails (for example, if there is a network disruption or the given context is cancelled).
Callers may call this repeatedly, to create multiple, concurrent tunnels to the gRPC server associated with the stub used to create this reverse tunnel server.
func (*ReverseTunnelServer) Stop ¶
func (s *ReverseTunnelServer) Stop()
Stop shuts down the server immediately. On return, the server has returned and any on-going operations have been cancelled.
type TunnelChannel ¶
type TunnelChannel interface { grpc.ClientConnInterface // Close shuts down the channel, cancelling any outstanding operations and // making it unavailable for subsequent operations. For forward tunnels, // this also closes the underlying stream. // // Channels for forward tunnels are implicitly closed if the context used // to create the underlying stream is cancelled or times out. Close() // Context returns the context for this channel. This context is derived // from the context associated with the underlying stream. // // For forward tunnels, this is a client context. So it will include // outgoing metadata for the request headers that were used to open the // tunnel. For reverse tunnels, this is a server context. So that request // metadata will be available as incoming metadata. Context() context.Context // Done returns a channel that can be used to await the channel closing. Done() <-chan struct{} // Err returns the error that caused the channel to close. If the channel // is not yet closed, this will return nil. Err() error }
TunnelChannel is a special gRPC connection that uses a gRPC stream (a tunnel) as its transport.
See NewChannel for using a forward tunnels. The TunnelServiceHandler provides methods for using reverse tunnels. Both directions use this same interface.
func TunnelChannelFromContext ¶
func TunnelChannelFromContext(ctx context.Context) TunnelChannel
TunnelChannelFromContext returns the TunnelChannel that is handling the given request context. If the given context is not a client-side request context, or if the channel for the request is not a tunnel, this will return nil.
type TunnelOption ¶ added in v0.3.0
type TunnelOption interface {
// contains filtered or unexported methods
}
TunnelOption is an option for configuring the behavior of a tunnel client or tunnel server.
func WithDisableFlowControl ¶ added in v0.3.0
func WithDisableFlowControl() TunnelOption
WithDisableFlowControl returns an option that disables the use of flow control, even when the tunnel peer supports it.
NOTE: This should NOT be used in application code. This is intended for test code, to verify that the tunnels work without flow control, to make sure they can interop correctly with older versions of this package, before flow control was introduced.
Eventually, older versions that do not use flow control will not be supported and this option will be removed.
type TunnelServiceHandler ¶
type TunnelServiceHandler struct {
// contains filtered or unexported fields
}
TunnelServiceHandler provides an implementation for TunnelServiceServer. You can register handlers with it, and it will then expose those handlers for incoming tunnels. If no handlers are registered, the server will reply to OpenTunnel requests with an "Unimplemented" error code. The server may still be used for reverse tunnels
For reverse tunnels, if supported, all connected channels (e.g. all clients that have created reverse tunnels) are available. You can also configure a listener to receive notices when channels are connected and disconnected.
See NewTunnelServiceHandler.
func NewTunnelServiceHandler ¶
func NewTunnelServiceHandler(options TunnelServiceHandlerOptions) *TunnelServiceHandler
NewTunnelServiceHandler creates a new TunnelServiceHandler. The options are used to configure behavior for reverse tunnels. The returned handler is also a grpc.ServiceRegistrar, to register the services that will be available for forward tunnels.
The handler's Service method can be used to actually register the handler with a *grpc.Server (or other grpc.ServiceRegistrar).
func (*TunnelServiceHandler) AllReverseTunnels ¶
func (s *TunnelServiceHandler) AllReverseTunnels() []TunnelChannel
AllReverseTunnels returns the set of all currently active reverse tunnels.
func (*TunnelServiceHandler) AsChannel ¶
func (s *TunnelServiceHandler) AsChannel() ReverseClientConnInterface
AsChannel returns a channel that can be used for issuing RPCs back to clients over reverse tunnels. If no reverse tunnels are established, RPCs will fail with "Unavailable" errors.
The returned channel will use a round-robin strategy to select from available reverse tunnels for any given RPC.
This method panics if the handler was created with an option to disallow the use of reverse tunnels.
func (*TunnelServiceHandler) InitiateShutdown ¶
func (s *TunnelServiceHandler) InitiateShutdown()
InitiateShutdown starts the graceful shutdown process and returns immediately. This should be called when the server wants to shut down. This complements the normal process initiated by calling the GracefulStop method of a *grpc.Server. It prevents new operations from being initiated on any existing tunnel (while the main server's GracefulStop method prevents new tunnels from being established). This allows the server to drain, letting existing operations to complete.
func (*TunnelServiceHandler) KeyAsChannel ¶
func (s *TunnelServiceHandler) KeyAsChannel(key interface{}) ReverseClientConnInterface
KeyAsChannel returns a channel that can be used for issuing RPCs back to clients over reverse tunnels whose affinity key matches the given value. If no reverse tunnels that match are established, RPCs will fail with "Unavailable" errors. If no affinity key function was provided when the handler was created, the only key available will be the nil interface.
The returned channel will use a round-robin strategy to select from matching reverse tunnels for any given RPC.
This method panics if the handler was created with an option to disallow the use of reverse tunnels.
func (*TunnelServiceHandler) RegisterService ¶
func (s *TunnelServiceHandler) RegisterService(desc *grpc.ServiceDesc, srv interface{})
RegisterService implements the grpc.ServiceRegistrar interface. This allows the handler to be passed to generated registration functions, so service implementations can be registered with the handler.
func (*TunnelServiceHandler) Service ¶
func (s *TunnelServiceHandler) Service() tunnelpb.TunnelServiceServer
Service returns the actual tunnel service implementation to register with a grpc.ServiceRegistrar.
type TunnelServiceHandlerOptions ¶
type TunnelServiceHandlerOptions struct { // If set, reverse tunnels will not be allowed. The server will reply to // OpenReverseTunnel requests with an "Unimplemented" error code. NoReverseTunnels bool // If reverse tunnels are allowed, this callback may be configured to // receive information when clients open a reverse tunnel. OnReverseTunnelOpen func(TunnelChannel) // If reverse tunnels are allowed, this callback may be configured to // receive information when reverse tunnels are torn down. OnReverseTunnelClose func(TunnelChannel) // Optional function that accepts a reverse tunnel and returns an affinity // key. The affinity key values can be used to look up outbound channels, // for targeting calls to particular clients or groups of clients. // // The given TunnelChannel has a context which can be used to query for the // identity of the remote peer. Functions like peer.FromContext will return // the peer that opened the reverse tunnel, and metadata.FromIncomingContext // will return the request headers used to open the reverse tunnel. If any // server interceptors ran when the tunnel was opened, then any values they // store in the context is also available. AffinityKey func(TunnelChannel) any // If true, flow control will be disabled, even when the network client // supports flow control. DisableFlowControl bool }
TunnelServiceHandlerOptions contains various fields that can be used to customize a TunnelServiceHandler.
See NewTunnelServiceHandler.
Source Files ¶
Directories ¶
Path | Synopsis |
---|---|
Package tunnelpb contains generated code corresponding to the Protocol Buffer definition of the tunneling protocol.
|
Package tunnelpb contains generated code corresponding to the Protocol Buffer definition of the tunneling protocol. |