Vanguard Go Library

repository·main·Indexed 19 days ago

https://github.com/connectrpc/vanguard-go

A Go library for net/http servers that provides high-performance transcoding between REST and RPC protocols, including gRPC, gRPC-Web, and Connect. Vanguard uses Protobuf definitions and HTTP transcoding annotations to allow existing gRPC handlers to support Connect clients and RESTful transformations without rewriting service logic.

Tokens
9.3K
Snippets
30
Records
52
Agent score
62%

What's inside vanguard-go

  1. Overview of Vanguard

    main

    Vanguard is a Go library designed for net/http servers that enables seamless transcoding between REST and various RPC protocols (gRPC, gRPC-Web, and Connect). It uses strongly typed Protobuf definitions and supports Google's HTTP transcoding options to translate protocols automatically.

    Key benefits include:

    • RESTful Transformation: Supports REST clients during migrations from REST to schema-driven RPC APIs using HTTP transcoding annotations.
    • Efficiency: Operates within Go servers (compatible with Connect and gRPC) without requiring extensive code generation.
    • Dynamic Loading: Service definitions can be loaded from configuration, schema registries, or via gRPC Server Reflection, allowing for updates without recompilation.
    • Legacy Compatibility: Allows legacy REST clients to interact with Protobuf RPC services.
    • Protocol Bridging: Bridges the gap between gRPC and Connect, allowing existing gRPC handlers to be used with Connect clients.
  2. Understand the Pet Store example architecture

    main

    The Pet Store example demonstrates Vanguard's protocol translation capabilities by chaining two services: a frontend (pets-fe) and a backend (pets-be).

    1. pets-fe (Frontend): Acts as a multi-protocol gateway. It accepts REST, Connect, gRPC, or gRPC-Web requests and uses Vanguard middleware to translate them into an RPC protocol before forwarding them to the backend.
    2. pets-be (Backend): Acts as an RPC-to-REST bridge. It accepts RPC requests from the frontend and uses Vanguard middleware to translate them into REST requests, which are then forwarded to an external API (e.g., petstore.swagger.io).

    This setup demonstrates how Vanguard can be used to bridge different communication styles (REST $\leftrightarrow$ RPC) across different layers of an application.

  3. Use Vanguard to support Connect clients on a gRPC server

    main
    Vanguard allows you to support Connect and gRPC-Web clients on an existing gRPC server without rewriting your service logic. It acts as a translation layer (transcoder) that converts incoming Connect requests into gRPC requests, which are then passed to your existing gRPC handlers. This is particularly useful for migrations where web or mobile clients adopt the Connect protocol before the backend infrastructure is fully updated.
  4. Migrate from gRPC to Connect using Vanguard

    main

    To facilitate a migration, you can wrap your existing gRPC server handlers in Vanguard middleware. This enables the server to transparently handle multiple protocols:

    1. gRPC: The original protocol used by existing clients.
    2. Connect: The modern protocol used by web and mobile clients.
    3. gRPC-Web: For browser-based clients.

    In the provided example implementation, the server component uses the google.golang.org/grpc runtime and protoc-gen-grpc-go generated code, but wraps the handler in Vanguard middleware to enable this multi-protocol support.

  5. Wire the Transcoder to an HTTP server

    main

    Since the *vanguard.Transcoder implements http.Handler, you can register it with an http.Server or http.ServeMux.

    It is recommended to use a single transcoder for all your services to ensure correct dispatching. Registering the transcoder at the root path (/) is a common pattern to allow it to handle various REST-ful paths defined via HTTP annotations.

    // Option 1: Use the transcoder as the sole handler
    err := http.Serve(listener, transcoder)
    
    // Option 2: Register the transcoder on a Mux (e.g., at the root path)
    mux := http.NewServeMux()
    mux.Handle("/", transcoder)
    err := http.Serve(listener, mux)
  6. Understand the message lifecycle stages

    main

    The transcoder manages messages through a state machine defined by messageStage. Understanding these stages is key to how data is transformed between protocols:

    1. stageEmpty: The initial state.
    2. stageRead: Raw data has been read from the client or written by the server handler. The buffer contains the original encoding (potentially compressed).
    3. stageDecoded: Data has been decompressed and unmarshaled into a proto.Message. The msg field is now usable.
    4. stageSend: Data has been re-encoded and re-compressed, and is ready to be sent to the client or read by the server handler.
    type messageStage int
    
    const (
    	stageEmpty messageStage = iota
    	stageRead
    	stageDecoded
    	stageSend
    )
  7. Understand the responseEnd structure

    main

    The responseEnd type is a protocol-agnostic representation of how an RPC finished. It is used by both client and server handlers to signal the end of a stream or an error.

    Fields:

    • err *connect.Error: The error encountered during the RPC, if any.
    • trailers http.Header: Any protocol-specific trailers sent at the end of the RPC.
    • httpCode int: The HTTP status code (populated if the end signal came from response headers).
    • wasCompressed bool: True if the end signal was part of an enveloped stream payload that was compressed.
  8. How gRPC protocol handling works in Vanguard

    main

    Vanguard implements the gRPC protocol through internal handlers that manage the lifecycle of an RPC, including header extraction, envelope encoding/decoding, and error handling via trailers.

    When a client sends a gRPC request, Vanguard uses grpcClientProtocol to:

    1. Extract request metadata (timeouts, codecs, compression) from headers.
    2. Handle streaming types.
    3. Encode responses and trailers.

    When the server sends a response to a client, Vanguard uses grpcServerProtocol to:

    1. Add required gRPC headers (like Te: trailers).
    2. Extract response metadata and errors from HTTP trailers.
    3. Decode the data envelope (compression flags and length).

    For browser-based environments, Vanguard also supports the gRPC-Web protocol via grpcWebClientProtocol and grpcWebServerProtocol, which handles in-body trailers since gRPC-Web cannot use standard HTTP trailers.

  9. How the Transcoder and Services work together

    main

    Vanguard uses a hierarchical configuration model:

    1. Transcoder Level: You define global defaults and global rules (like REST mappings) using TranscoderOption. These are applied to all services via WithDefaultServiceOptions.
    2. Service Level: Each Service can have its own specific ServiceOptions. Service-specific options always override the defaults provided at the Transcoder level.

    This allows you to define a standard set of protocols and codecs for your entire API while allowing specific high-performance or specialized services to opt into different behaviors.

  10. How the Transcoder handles request routing

    main

    The Transcoder resolves the target method using two primary strategies:

    1. REST Routing: If the incoming request is identified as a REST protocol, the Transcoder uses a routeTrie to match the URI path and HTTP method against configured HttpRule annotations.
    2. RPC Routing: For Connect, gRPC, and gRPC-Web, the Transcoder matches the request's URI path directly against the registered RPC method paths (e.g., /package.Service/Method).

    If no match is found, the Transcoder returns a 404 Not Found error or invokes a configured unknownHandler if one is provided.

  11. Understand the Codec interfaces

    main

    Vanguard uses a hierarchy of interfaces to handle different message encoding requirements across various RPC protocols:

    1. Codec: The base interface for any encoding format. It handles full message marshalling and unmarshalling.
    2. StableCodec: An extension of Codec for formats that can produce deterministic, stable output (via MarshalAppendStable). This is required for the Connect protocol to use HTTP GET methods for unary RPCs, as message parameters must be encoded in the URL query string.
    3. RESTCodec: An extension of Codec used specifically by the REST protocol. It provides methods to marshal and unmarshal individual fields of a message, supporting query string variables or request bodies that contain only a specific field rather than a whole message.
  12. gRPC error handling via trailers

    main

    In the gRPC protocol, errors are communicated through HTTP trailers rather than the response body. Vanguard looks for the following specific trailer keys to reconstruct a connect.Error:

    • Grpc-Status: An integer representing the gRPC status code (e.g., 0 for OK).
    • Grpc-Message: A percent-encoded string containing a human-readable error message.
    • Grpc-Status-Details-Bin: A base64-encoded (via connect.EncodeBinaryHeader) Protobuf-encoded google.golang.org/genproto/googleapis/rpc/status.Status message containing rich error details.

    Vanguard prioritizes the binary Grpc-Status-Details-Bin header for error details if available, following the behavior of grpc-go.