Documentation
¶
Overview ¶
Package vanguard provides a unified server that serves gRPC, Connect, gRPC-Web, and REST protocols on a single port via Vanguard transcoder with h2c support.
Overview ¶
This package implements the core v5.0 server that composes multiple protocol handlers into a single HTTP endpoint. It uses connectrpc.com/vanguard to transcode between gRPC, Connect, gRPC-Web, and REST (via google.api.http annotations) without any code generation.
Services are auto-discovered from the DI container:
- Connect services implement connect.Registrar and are resolved via di.ResolveAll[connect.Registrar].
- gRPC services are bridged through the gRPC server's raw *grpc.Server via vanguardgrpc.NewTranscoder.
Quick Start ¶
Use the module to register the Vanguard server with your application:
app := gaz.New() app.Use(grpc.NewModule()) // gRPC services (skip-listener mode) app.Use(vanguard.NewModule()) // Vanguard unified server
Health Endpoints ¶
When a health.Manager is present in the DI container, the server automatically mounts health endpoints on the unknown handler. Paths are configurable via health.Config (defaults shown):
- /ready — readiness probe
- /live — liveness probe
- /startup — startup probe
Reflection ¶
gRPC reflection (v1 and v1alpha) is enabled by default for grpcurl compatibility. Reflection handlers are registered as Connect-style services in the Vanguard transcoder.
Configuration ¶
Configuration uses the "server" namespace:
server: port: 8080 read_header_timeout: 5s idle_timeout: 120s reflection: true health_enabled: true
Index ¶
Constants ¶
const ( // PriorityCORS is the priority for the CORS middleware (runs first). PriorityCORS = 0 // PriorityOTEL is the priority for the OTEL middleware (runs after CORS). PriorityOTEL = 100 )
Transport middleware priority constants. Lower values run first (outermost in the handler chain).
const DefaultCORSMaxAge = 86400
DefaultCORSMaxAge is the default max age for preflight request caching (24 hours in seconds).
const DefaultIdleTimeout = 120 * time.Second
DefaultIdleTimeout is the default idle timeout for keep-alive connections.
const DefaultPort = 8080
DefaultPort is the default port for the Vanguard server.
const DefaultReadHeaderTimeout = 5 * time.Second
DefaultReadHeaderTimeout is the default read header timeout. This protects against slowloris attacks.
Variables ¶
This section is empty.
Functions ¶
func NewModule ¶
NewModule creates a Vanguard module. Returns a gaz.Module that registers Vanguard server components.
Components registered:
- vanguard.Config (loaded from flags/config)
- *vanguard.CORSMiddleware (transport middleware, always registered)
- *vanguard.OTELMiddleware (transport middleware, only if TracerProvider registered)
- *vanguard.OTELConnectBundle (connect interceptor bundle, only if TracerProvider registered)
- *connect.LoggingBundle (connect logging interceptor, always registered)
- *connect.RecoveryBundle (connect panic recovery interceptor, always registered)
- *connect.ValidationBundle (connect protovalidate interceptor, always registered)
- *connect.AuthBundle (connect auth interceptor, only if AuthFunc registered)
- *connect.RateLimitBundle (connect rate limit interceptor, uses AlwaysPassLimiter unless Limiter registered)
- *vanguard.Server (eager, starts on app start)
The module depends on grpc.NewModule() being registered first, as it resolves *grpc.Server from the DI container to bridge gRPC services into the Vanguard transcoder.
Example:
app := gaz.New() app.Use(grpc.NewModule()) // Must come first app.Use(vanguard.NewModule()) // Vanguard unified server
Types ¶
type CORSConfig ¶
type CORSConfig struct {
// AllowedOrigins is a list of allowed origins.
// Use ["*"] to allow all origins (dev mode only, not with credentials).
AllowedOrigins []string `json:"allowed_origins" yaml:"allowed_origins" mapstructure:"allowed_origins"`
// AllowedMethods is a list of allowed HTTP methods.
AllowedMethods []string `json:"allowed_methods" yaml:"allowed_methods" mapstructure:"allowed_methods"`
// AllowedHeaders is a list of allowed request headers.
// Use ["*"] to allow all headers (dev mode only).
AllowedHeaders []string `json:"allowed_headers" yaml:"allowed_headers" mapstructure:"allowed_headers"`
// ExposedHeaders is a list of headers exposed to the browser.
ExposedHeaders []string `json:"exposed_headers" yaml:"exposed_headers" mapstructure:"exposed_headers"`
// AllowCredentials indicates whether credentials (cookies, auth headers) are allowed.
// Cannot be used with AllowedOrigins ["*"].
AllowCredentials bool `json:"allow_credentials" yaml:"allow_credentials" mapstructure:"allow_credentials"`
// MaxAge is the maximum age (in seconds) for preflight request caching.
MaxAge int `json:"max_age" yaml:"max_age" mapstructure:"max_age"`
}
CORSConfig holds CORS configuration for the Vanguard server.
func DefaultCORSConfig ¶
func DefaultCORSConfig(devMode bool) CORSConfig
DefaultCORSConfig returns a CORSConfig with appropriate defaults. In dev mode, CORS is wide-open for convenience. In prod mode, origins must be explicitly configured.
type CORSMiddleware ¶
type CORSMiddleware struct {
// contains filtered or unexported fields
}
CORSMiddleware implements TransportMiddleware for CORS handling. In dev mode, it allows all origins. In production, it applies configured CORS restrictions.
func NewCORSMiddleware ¶
func NewCORSMiddleware(cfg CORSConfig, devMode bool) *CORSMiddleware
NewCORSMiddleware creates a new CORS transport middleware. In dev mode, all origins are allowed. In production, the configured CORSConfig origins, methods, and headers are enforced.
func (*CORSMiddleware) Name ¶
func (m *CORSMiddleware) Name() string
Name returns the middleware identifier.
func (*CORSMiddleware) Priority ¶
func (m *CORSMiddleware) Priority() int
Priority returns the CORS priority (outermost handler).
type Config ¶
type Config struct {
// Port is the TCP port the Vanguard server listens on.
// Defaults to 8080 if not set.
Port int `json:"port" yaml:"port" mapstructure:"port" gaz:"port"`
// ReadTimeout is the maximum duration for reading the entire request.
// Zero means no timeout, which is required for streaming RPCs.
// Defaults to 0 (streaming-safe).
ReadTimeout time.Duration `json:"read_timeout" yaml:"read_timeout" mapstructure:"read_timeout" gaz:"read_timeout"`
// WriteTimeout is the maximum duration before timing out writes of the response.
// Zero means no timeout, which is required for streaming RPCs.
// Defaults to 0 (streaming-safe).
WriteTimeout time.Duration `json:"write_timeout" yaml:"write_timeout" mapstructure:"write_timeout" gaz:"write_timeout"`
// IdleTimeout is the maximum duration an idle keep-alive connection will remain open.
// Defaults to 120 seconds.
IdleTimeout time.Duration `json:"idle_timeout" yaml:"idle_timeout" mapstructure:"idle_timeout" gaz:"idle_timeout"`
// ReadHeaderTimeout is the maximum duration for reading request headers.
// This protects against slowloris attacks.
// Defaults to 5 seconds.
ReadHeaderTimeout time.Duration `json:"read_header_timeout" yaml:"read_header_timeout" mapstructure:"read_header_timeout" gaz:"read_header_timeout"`
// Reflection enables gRPC reflection via Connect handlers (v1 and v1alpha).
// When enabled, tools like grpcurl can introspect available services.
// Defaults to true.
Reflection bool `json:"reflection" yaml:"reflection" mapstructure:"reflection" gaz:"reflection"`
// HealthEnabled enables automatic health endpoint mounting.
// When enabled and health.Manager is present, health endpoints are mounted
// on the unknown handler using paths from health.Config.
// Defaults to true.
HealthEnabled bool `json:"health_enabled" yaml:"health_enabled" mapstructure:"health_enabled" gaz:"health_enabled"`
// DevMode enables development mode for verbose error messages.
// Defaults to false.
DevMode bool `json:"dev_mode" yaml:"dev_mode" mapstructure:"dev_mode" gaz:"dev_mode"`
// AllowZeroWriteTimeout explicitly opts in to zero write timeout.
// When false (default), WriteTimeout=0 is rejected by Validate as a Slowloris risk.
// Set to true only when streaming RPCs require no write timeout.
AllowZeroWriteTimeout bool `` /* 134-byte string literal not displayed */
// CORS contains CORS configuration for the Vanguard server.
CORS CORSConfig `json:"cors" yaml:"cors" mapstructure:"cors" gaz:"cors"`
}
Config holds configuration for the Vanguard server.
func DefaultConfig ¶
func DefaultConfig() Config
DefaultConfig returns a Config with safe defaults. ReadTimeout and WriteTimeout are intentionally zero for streaming safety.
func (*Config) Flags ¶
Flags registers the config flags. ReadTimeout and WriteTimeout are not exposed as flags because zero is the correct default for streaming RPCs. Users should only change them via config file if they understand the streaming implications.
func (*Config) Namespace ¶
Namespace returns the config namespace. The Vanguard server uses "server" as its namespace, so flags are prefixed with server-.
func (*Config) SetDefaults ¶
func (c *Config) SetDefaults()
SetDefaults applies default values to zero-value fields. ReadTimeout and WriteTimeout are NOT defaulted because zero is intentional for streaming safety. Only Port, ReadHeaderTimeout, and IdleTimeout are filled. Implements the config.Defaulter interface.
type OTELConnectBundle ¶
type OTELConnectBundle struct {
// contains filtered or unexported fields
}
OTELConnectBundle implements connect.InterceptorBundle for OpenTelemetry Connect RPC tracing. It provides otelconnect interceptors when a TracerProvider is available.
func NewOTELConnectBundle ¶
func NewOTELConnectBundle(tp *sdktrace.TracerProvider, logger *slog.Logger) *OTELConnectBundle
NewOTELConnectBundle creates a new OTEL Connect interceptor bundle.
func (*OTELConnectBundle) Interceptors ¶
func (b *OTELConnectBundle) Interceptors() []connect.Interceptor
Interceptors returns the otelconnect interceptor. Health check procedures are filtered from traces. If interceptor creation fails, a warning is logged and an empty slice is returned.
func (*OTELConnectBundle) Name ¶
func (b *OTELConnectBundle) Name() string
Name returns the bundle identifier.
func (*OTELConnectBundle) Priority ¶
func (b *OTELConnectBundle) Priority() int
Priority returns the OTEL Connect priority (before validation, after auth).
type OTELMiddleware ¶
type OTELMiddleware struct {
// contains filtered or unexported fields
}
OTELMiddleware implements TransportMiddleware for OpenTelemetry HTTP tracing. It wraps the handler with otelhttp instrumentation, filtering out health and reflection endpoints. Health paths are read from health.Config so they stay in sync with the actual health endpoint paths.
func NewOTELMiddleware ¶
func NewOTELMiddleware(tp *sdktrace.TracerProvider, healthCfg health.Config) *OTELMiddleware
NewOTELMiddleware creates a new OTEL transport middleware with the given TracerProvider and health configuration. Health check paths from healthCfg are excluded from traces to reduce noise.
func (*OTELMiddleware) Name ¶
func (m *OTELMiddleware) Name() string
Name returns the middleware identifier.
func (*OTELMiddleware) Priority ¶
func (m *OTELMiddleware) Priority() int
Priority returns the OTEL priority (after CORS).
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server is a unified server that composes gRPC, Connect, gRPC-Web, and REST protocols on a single port via Vanguard transcoder with h2c support. It implements di.Starter and di.Stopper for lifecycle management.
func NewServer ¶
func NewServer(cfg Config, logger *slog.Logger, container *di.Container, grpcServer *grpc.Server) *Server
NewServer creates a new Vanguard server with the given configuration. The server is not started until OnStart is called.
Parameters:
- cfg: Server configuration (port, timeouts, reflection, health)
- logger: Logger for server events (falls back to slog.Default)
- container: DI container for service discovery
- grpcServer: The raw *grpc.Server from the gRPC module (for vanguardgrpc bridge)
func (*Server) OnStart ¶
OnStart starts the Vanguard server. It discovers Connect services, bridges gRPC services, registers reflection and health handlers, builds the Vanguard transcoder, and starts serving over h2c on the configured port. Implements di.Starter.
func (*Server) OnStop ¶
OnStop gracefully shuts down the Vanguard server. It waits for active connections to drain or forces shutdown on context timeout. Implements di.Stopper.
func (*Server) SetUnknownHandler ¶
SetUnknownHandler sets a user-defined handler for non-RPC HTTP routes. Must be called before OnStart.
type TransportMiddleware ¶
type TransportMiddleware interface {
// Name returns a unique identifier for logging and debugging.
Name() string
// Priority determines the order in the middleware chain.
// Lower values wrap outermost (run first on request, last on response).
Priority() int
// Wrap applies the middleware to the given handler.
Wrap(http.Handler) http.Handler
}
TransportMiddleware wraps an http.Handler with cross-cutting HTTP concerns. Implementations are automatically discovered from the DI container and applied in priority order around the Vanguard handler.