grpcreflect

package module
v1.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 22, 2023 License: Apache-2.0 Imports: 16 Imported by: 34

README

connect-grpcreflect-go

Build Report Card GoDoc

connect-grpcreflect-go adds support for gRPC's server reflection API to any net/http server — including those built with Connect. With server reflection enabled, ad-hoc debugging tools can call your gRPC-compatible handlers and print the responses without a copy of the schema.

The exposed reflection API is wire compatible with Google's gRPC implementations, so it works with grpcurl, grpcui, BloomRPC, and many other tools.

For more on Connect, see the announcement blog post, the documentation on connect.build (especially the Getting Started guide for Go), the connect-go repo, or the demo service.

Example

package main

import (
  "net/http"

  "golang.org/x/net/http2"
  "golang.org/x/net/http2/h2c"
  grpcreflect "github.com/bufbuild/connect-grpcreflect-go"
)

func main() {
  mux := http.NewServeMux()
  reflector := grpcreflect.NewStaticReflector(
    "acme.user.v1.UserService",
    "acme.group.v1.GroupService",
    // protoc-gen-connect-go generates package-level constants
    // for these fully-qualified protobuf service names, so you'd more likely
    // reference userv1.UserServiceName and groupv1.GroupServiceName.
  )
  mux.Handle(grpcreflect.NewHandlerV1(reflector))
  // Many tools still expect the older version of the server reflection API, so
  // most servers should mount both handlers.
  mux.Handle(grpcreflect.NewHandlerV1Alpha(reflector))
  // If you don't need to support HTTP/2 without TLS (h2c), you can drop
  // x/net/http2 and use http.ListenAndServeTLS instead.
  http.ListenAndServe(
    ":8080",
    h2c.NewHandler(mux, &http2.Server{}),
  )
}

Status: Stable

This module is stable. It supports:

Within those parameters, connect-grpcreflect-go follows semantic versioning. We will not make breaking changes in the 1.x series of releases.

Offered under the Apache 2 license.

Documentation

Overview

Package grpcreflect enables any net/http server, including those built with Connect, to handle gRPC's server reflection API. This lets ad-hoc debugging tools call your Protobuf services and print the responses without a copy of the schema.

The exposed reflection API is wire compatible with Google's gRPC implementations, so it works with grpcurl, grpcui, BloomRPC, and many other tools.

The core Connect package is github.com/bufbuild/connect-go. Documentation is available at https://connect.build.

Index

Examples

Constants

View Source
const (
	// ReflectV1ServiceName is the fully-qualified name of the v1 version of the reflection service.
	ReflectV1ServiceName = "grpc.reflection.v1.ServerReflection"
	// ReflectV1AlphaServiceName is the fully-qualified name of the v1alpha version of the reflection service.
	ReflectV1AlphaServiceName = "grpc.reflection.v1alpha.ServerReflection"
)

Variables

This section is empty.

Functions

func IsReflectionStreamBroken added in v1.1.0

func IsReflectionStreamBroken(err error) bool

IsReflectionStreamBroken returns true if the given error was the result of a ClientStream failing. If the stream returns an error for which this function returns false, only the one operation failed; the stream is still intact and may be used for subsequent operations.

func NewHandlerV1

func NewHandlerV1(reflector *Reflector, options ...connect.HandlerOption) (string, http.Handler)

NewHandlerV1 constructs an implementation of v1 of the gRPC server reflection API. It returns an HTTP handler and the path on which to mount it.

Note that because the reflection API requires bidirectional streaming, the returned handler doesn't support HTTP/1.1. If your server must also support older tools that use the v1alpha server reflection API, see NewHandlerV1Alpha.

func NewHandlerV1Alpha

func NewHandlerV1Alpha(reflector *Reflector, options ...connect.HandlerOption) (string, http.Handler)

NewHandlerV1Alpha constructs an implementation of v1alpha of the gRPC server reflection API, which is useful to support tools that haven't updated to the v1 API. It returns an HTTP handler and the path on which to mount it.

Like NewHandlerV1, the returned handler doesn't support HTTP/1.1.

Types

type Client added in v1.1.0

type Client struct {
	// contains filtered or unexported fields
}

Client is a Connect client for the server reflection service.

func NewClient added in v1.1.0

func NewClient(httpClient connect.HTTPClient, baseURL string, options ...connect.ClientOption) *Client

NewClient returns a client for interacting with the gRPC server reflection service. The given HTTP client, base URL, and options are used to connect to the service.

This client will try "v1" of the service first (grpc.reflection.v1.ServerReflection). If this results in a "Not Implemented" error, the client will fall back to "v1alpha" of the service (grpc.reflection.v1alpha.ServerReflection).

Example
// Create a client to the Connect demo server.
client := grpcreflect.NewClient(http.DefaultClient, "https://demo.connect.build")
// Create a new reflection stream.
stream := client.NewStream(context.Background())
// Ask the server for its services and for the file descriptor that contains the first one.
names, err := stream.ListServices()
if err != nil {
	log.Printf("error listing services: %v", err)
	return
}
fmt.Printf("services: %v\n", names)
files, err := stream.FileContainingSymbol(names[0])
if err != nil {
	log.Printf("error getting file that contains %q: %v", names[0], err)
	return
}
fmt.Printf("file descriptor for %q\n", files[len(files)-1].GetName())
Output:

services: [buf.connect.demo.eliza.v1.ElizaService]
file descriptor for "buf/connect/demo/eliza/v1/eliza.proto"

func (*Client) NewStream added in v1.1.0

func (c *Client) NewStream(ctx context.Context, options ...ClientStreamOption) *ClientStream

NewStream creates a new stream that is used to download reflection information from the server. This is a bidirectional stream, so can only be successfully used over HTTP/2. The ClientStream.Close method should be called when the caller is done with the stream.

If any operation returns an error for which IsReflectionStreamBroken returns true, the stream is broken and all subsequent operations will fail. If the error is not a permanent error, the caller should create another stream and try again.

type ClientStream added in v1.1.0

type ClientStream struct {
	// contains filtered or unexported fields
}

ClientStream represents a bidirectional stream for downloading reflection information. The reflection protocol resembles a sequence of unary RPCs: multiple requests sent on the stream, each getting back a corresponding response. However, all such requests and responses and sent on a single stream to a single server, to ensure consistency in the information downloaded (since different servers could potentially have different versions of reflection information).

func (*ClientStream) AllExtensionNumbers added in v1.1.0

func (cs *ClientStream) AllExtensionNumbers(messageName protoreflect.FullName) ([]protoreflect.FieldNumber, error)

AllExtensionNumbers retrieves the tag numbers for all extensions of the given message that are known to the server.

This may return a *connect.Error indicating the reason the list of extension numbers could not be retrieved (such as "Not Found" if the given message is not known). But if IsReflectionStreamBroken returns true for the returned error, the stream is broken and cannot be used for further operations.

This operation sends a request message on the stream and waits for the corresponding response.

func (*ClientStream) Close added in v1.1.0

func (cs *ClientStream) Close() (http.Header, error)

Close closes the stream and returns any trailers sent by the server.

func (*ClientStream) FileByFilename added in v1.1.0

func (cs *ClientStream) FileByFilename(filename string) ([]*descriptorpb.FileDescriptorProto, error)

FileByFilename retrieves the descriptor for the file with the given path and name. The server may respond with multiple files, which represent the request file plus its imports or full transitive dependency graph. If the server has already sent back some of those files on this stream, they may be omitted from the response.

This may return a *connect.Error indicating the reason the file could not be retrieved (such as "Not Found" if the given path is not known). But if IsReflectionStreamBroken returns true for the returned error, the stream is broken and cannot be used for further operations.

This operation sends a request message on the stream and waits for the corresponding response.

func (*ClientStream) FileContainingExtension added in v1.1.0

func (cs *ClientStream) FileContainingExtension(messageName protoreflect.FullName, extensionNumber protoreflect.FieldNumber) ([]*descriptorpb.FileDescriptorProto, error)

FileContainingExtension retrieves the descriptor for the file that defines the extension with the given tag number and for the given extendable message. The server may respond with multiple files, which represent not only the file containing the requested symbol but also its imports or full transitive dependency graph. If the server has already sent back some of those files on this stream, they may be omitted from the response.

This may return a *connect.Error indicating the reason the file could not be retrieved (such as "Not Found" if the given extension is not known). But if IsReflectionStreamBroken returns true for the returned error, the stream is broken and cannot be used for further operations.

This operation sends a request message on the stream and waits for the corresponding response.

func (*ClientStream) FileContainingSymbol added in v1.1.0

func (cs *ClientStream) FileContainingSymbol(name protoreflect.FullName) ([]*descriptorpb.FileDescriptorProto, error)

FileContainingSymbol retrieves the descriptor for the file that defines the element with the given fully-qualified name. The server may respond with multiple files, which represent not only the file containing the requested symbol but also its imports or full transitive dependency graph. If the server has already sent back some of those files on this stream, they may be omitted from the response.

This may return a *connect.Error indicating the reason the file could not be retrieved (such as "Not Found" if the given element is not known). But if IsReflectionStreamBroken returns true for the returned error, the stream is broken and cannot be used for further operations.

This operation sends a request message on the stream and waits for the corresponding response.

func (*ClientStream) ListServices added in v1.1.0

func (cs *ClientStream) ListServices() ([]protoreflect.FullName, error)

ListServices retrieves the fully-qualified names for services exposed the server.

This may return a *connect.Error indicating the reason the list of services could not be retrieved. But if IsReflectionStreamBroken returns true for the returned error, the stream is broken and cannot be used for further operations.

This operation sends a request message on the stream and waits for the corresponding response.

func (*ClientStream) Peer added in v1.1.0

func (cs *ClientStream) Peer() connect.Peer

Peer describes the server for the RPC.

func (*ClientStream) ResponseHeader added in v1.1.0

func (cs *ClientStream) ResponseHeader() http.Header

ResponseHeader returns the headers received from the server. It blocks until the response headers have been sent by the server.

It is possible that the server implementation won't send back response headers until after it receives the first request message, sending back headers along with the first response message. So it is safest to either call this method from a different goroutine than the one that invokes other stream operations or to not call this until after the first such operation has completed.

The operations that send a message on the stream are [ListServices], [FileByFilename], [FileContainingSymbol], [FileContainingExtension], and [AllExtensionNumbers].

func (*ClientStream) Spec added in v1.1.0

func (cs *ClientStream) Spec() connect.Spec

Spec returns the specification for the reflection RPC.

type ClientStreamOption added in v1.1.0

type ClientStreamOption interface {
	// contains filtered or unexported methods
}

ClientStreamOption is an option that can be provided when calling Client.NewStream.

func WithReflectionHost added in v1.1.0

func WithReflectionHost(host string) ClientStreamOption

WithReflectionHost is an option that allows the caller to provide the hostname that will be included with all requests on the stream. This may be used by the server when deciding what source of reflection information to use (which could include forwarding the request message to a different host).

func WithRequestHeaders added in v1.1.0

func WithRequestHeaders(headers http.Header) ClientStreamOption

WithRequestHeaders is an option that allows the caller to provide the request headers that will be sent when a stream is created.

type ExtensionResolver

type ExtensionResolver interface {
	protoregistry.ExtensionTypeResolver

	RangeExtensionsByMessage(protoreflect.FullName, func(protoreflect.ExtensionType) bool)
}

An ExtensionResolver lets server reflection implementations query details about the registered Protobuf extensions. protoregistry.GlobalTypes implements ExtensionResolver.

ExtensionResolvers must be safe to call concurrently.

type Namer

type Namer interface {
	Names() []string
}

A Namer lists the fully-qualified Protobuf service names available for reflection (for example, "acme.user.v1.UserService"). Namers must be safe to call concurrently.

type Option

type Option interface {
	// contains filtered or unexported methods
}

An Option configures a Reflector.

func WithDescriptorResolver

func WithDescriptorResolver(resolver protodesc.Resolver) Option

WithDescriptorResolver sets the resolver used to find Protobuf type information (typically called a "descriptor"). By default, Reflectors use protoregistry.GlobalFiles.

func WithExtensionResolver

func WithExtensionResolver(resolver ExtensionResolver) Option

WithExtensionResolver sets the resolver used to find Protobuf extensions. By default, Reflectors use protoregistry.GlobalTypes.

type Reflector

type Reflector struct {
	// contains filtered or unexported fields
}

Reflector implements the underlying logic for gRPC's protobuf server reflection. They're configurable, so they can support straightforward process-local reflection or more complex proxying.

Keep in mind that by default, Reflectors expose every protobuf type and extension compiled into your binary. Think twice before including the default Reflector in a public API.

For more information, see https://github.com/grpc/grpc-go/blob/master/Documentation/server-reflection-tutorial.md, https://github.com/grpc/grpc/blob/master/doc/server-reflection.md, and https://github.com/fullstorydev/grpcurl.

func NewReflector

func NewReflector(namer Namer, options ...Option) *Reflector

NewReflector constructs a highly configurable Reflector: it can serve a dynamic list of services, proxy reflection requests to other backends, or use an alternate source of extension information.

To build a simpler Reflector that supports a static list of services using information from the package-global Protobuf registry, use NewStaticReflector.

func NewStaticReflector

func NewStaticReflector(services ...string) *Reflector

NewStaticReflector constructs a simple Reflector that supports a static list of services using information from the package-global Protobuf registry. For a more configurable Reflector, use NewReflector.

The supplied strings should be fully-qualified Protobuf service names (for example, "acme.user.v1.UserService"). Generated Connect service files have this declared as a constant.

Directories

Path Synopsis
internal

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL