Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ func OTELMeterProvider() otelmetric.MeterProvider
OTELMeterProvider returns the global OTel MeterProvider. This is a convenience accessor for code that needs the interface type.

<a name="SetOTELGRPCClientOptions"></a>
## func [SetOTELGRPCClientOptions](<https://github.com/go-coldbrew/core/blob/main/core.go#L575>)
## func [SetOTELGRPCClientOptions](<https://github.com/go-coldbrew/core/blob/main/core.go#L588>)

```go
func SetOTELGRPCClientOptions(opts ...otelgrpc.Option)
Expand All @@ -146,7 +146,7 @@ func SetOTELGRPCClientOptions(opts ...otelgrpc.Option)
Deprecated: Use SetOTELOptions instead. Only applies when OTEL\_USE\_LEGACY\_INSTRUMENTATION=true.

<a name="SetOTELGRPCServerOptions"></a>
## func [SetOTELGRPCServerOptions](<https://github.com/go-coldbrew/core/blob/main/core.go#L569>)
## func [SetOTELGRPCServerOptions](<https://github.com/go-coldbrew/core/blob/main/core.go#L582>)

```go
func SetOTELGRPCServerOptions(opts ...otelgrpc.Option)
Expand All @@ -155,7 +155,7 @@ func SetOTELGRPCServerOptions(opts ...otelgrpc.Option)
Deprecated: Use SetOTELOptions instead. Only applies when OTEL\_USE\_LEGACY\_INSTRUMENTATION=true.

<a name="SetOTELOptions"></a>
## func [SetOTELOptions](<https://github.com/go-coldbrew/core/blob/main/core.go#L582>)
## func [SetOTELOptions](<https://github.com/go-coldbrew/core/blob/main/core.go#L595>)

```go
func SetOTELOptions(opts grpcotel.Options)
Expand Down Expand Up @@ -314,7 +314,7 @@ type CB interface {
```

<a name="New"></a>
### func [New](<https://github.com/go-coldbrew/core/blob/main/core.go#L895>)
### func [New](<https://github.com/go-coldbrew/core/blob/main/core.go#L908>)

```go
func New(c config.Config) CB
Expand Down
20 changes: 14 additions & 6 deletions config/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ import "github.com/go-coldbrew/core/config"


<a name="Config"></a>
## type [Config](<https://github.com/go-coldbrew/core/blob/main/config/config.go#L6-L174>)
## type [Config](<https://github.com/go-coldbrew/core/blob/main/config/config.go#L12-L188>)

Config is the configuration for the Coldbrew server It is populated from environment variables and has sensible defaults for all fields so that you can just use it as is without any configuration The following environment variables are supported and can be used to override the defaults for the fields

Expand Down Expand Up @@ -174,13 +174,21 @@ type Config struct {
// DisableProtoValidate disables the protovalidate interceptor in the default
// interceptor chain. When disabled, proto validation annotations are ignored.
DisableProtoValidate bool `envconfig:"DISABLE_PROTO_VALIDATE" default:"false"`
// DisableDebugLogInterceptor disables the DebugLogInterceptor in the default
// interceptor chain. When disabled, proto debug fields and metadata headers
// will not trigger per-request debug logging.
DisableDebugLogInterceptor bool `envconfig:"DISABLE_DEBUG_LOG_INTERCEPTOR" default:"false"`
// DebugLogHeaderName is the gRPC metadata / HTTP header name that triggers
// per-request debug logging. The header value should be a valid log level
// (e.g., "debug"). Default: "x-debug-log-level".
DebugLogHeaderName string `envconfig:"DEBUG_LOG_HEADER_NAME" default:"x-debug-log-level"`
// DisableVTProtobuf disables the use of the vtprotobuf marshaller and unmarshaller for GRPC
// https://github.com/planetscale/vtprotobuf
DisableVTProtobuf bool `envconfig:"DISABLE_VT_PROTOBUF" default:"false"`
// GRPCMaxSendMsgSize and GRPCMaxRecvMsgSize are the maximum message
// sizes for sending and receiving messages over GRPC
GRPCMaxSendMsgSize int `envconfig:"GRPC_MAX_SEND_MSG_SIZE" default:"2147483647"` // Unlimited
GRPCMaxRecvMsgSize int `envconfig:"GRPC_MAX_RECV_MSG_SIZE" default:"4194304"` // 4MB
// GRPCMaxSendMsgSize is the max response size your service can send back to clients.
GRPCMaxSendMsgSize int `envconfig:"GRPC_MAX_SEND_MSG_SIZE" default:"2147483647"` // ~2GB (gRPC maximum)
// GRPCMaxRecvMsgSize is the max request size your service accepts from clients.
GRPCMaxRecvMsgSize int `envconfig:"GRPC_MAX_RECV_MSG_SIZE" default:"4194304"` // 4MB
// GRPCServerDefaultTimeoutInSeconds is the default timeout (in seconds) for
// incoming unary gRPC requests that arrive without a deadline. Set to 0 to
// disable. Does not apply to stream RPCs.
Expand Down Expand Up @@ -238,7 +246,7 @@ type Config struct {
```

<a name="Config.Validate"></a>
### func \(Config\) [Validate](<https://github.com/go-coldbrew/core/blob/main/config/config.go#L179>)
### func \(Config\) [Validate](<https://github.com/go-coldbrew/core/blob/main/config/config.go#L193>)

```go
func (c Config) Validate() []string
Expand Down
55 changes: 51 additions & 4 deletions config/config.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
package config

import (
"net"
"os"
"strings"
)

// Config is the configuration for the Coldbrew server
// It is populated from environment variables and has sensible defaults for all fields so that you can just use it as is without any configuration
// The following environment variables are supported and can be used to override the defaults for the fields
Expand Down Expand Up @@ -106,13 +112,21 @@ type Config struct {
// DisableProtoValidate disables the protovalidate interceptor in the default
// interceptor chain. When disabled, proto validation annotations are ignored.
DisableProtoValidate bool `envconfig:"DISABLE_PROTO_VALIDATE" default:"false"`
// DisableDebugLogInterceptor disables the DebugLogInterceptor in the default
// interceptor chain. When disabled, proto debug fields and metadata headers
// will not trigger per-request debug logging.
DisableDebugLogInterceptor bool `envconfig:"DISABLE_DEBUG_LOG_INTERCEPTOR" default:"false"`
// DebugLogHeaderName is the gRPC metadata / HTTP header name that triggers
// per-request debug logging. The header value should be a valid log level
// (e.g., "debug"). Default: "x-debug-log-level".
DebugLogHeaderName string `envconfig:"DEBUG_LOG_HEADER_NAME" default:"x-debug-log-level"`
// DisableVTProtobuf disables the use of the vtprotobuf marshaller and unmarshaller for GRPC
// https://github.com/planetscale/vtprotobuf
DisableVTProtobuf bool `envconfig:"DISABLE_VT_PROTOBUF" default:"false"`
// GRPCMaxSendMsgSize and GRPCMaxRecvMsgSize are the maximum message
// sizes for sending and receiving messages over GRPC
GRPCMaxSendMsgSize int `envconfig:"GRPC_MAX_SEND_MSG_SIZE" default:"2147483647"` // Unlimited
GRPCMaxRecvMsgSize int `envconfig:"GRPC_MAX_RECV_MSG_SIZE" default:"4194304"` // 4MB
// GRPCMaxSendMsgSize is the max response size your service can send back to clients.
GRPCMaxSendMsgSize int `envconfig:"GRPC_MAX_SEND_MSG_SIZE" default:"2147483647"` // ~2GB (gRPC maximum)
// GRPCMaxRecvMsgSize is the max request size your service accepts from clients.
GRPCMaxRecvMsgSize int `envconfig:"GRPC_MAX_RECV_MSG_SIZE" default:"4194304"` // 4MB
// GRPCServerDefaultTimeoutInSeconds is the default timeout (in seconds) for
// incoming unary gRPC requests that arrive without a deadline. Set to 0 to
// disable. Does not apply to stream RPCs.
Expand Down Expand Up @@ -210,6 +224,39 @@ func (c Config) Validate() []string {
if c.GRPCServerDefaultTimeoutInSeconds < 0 {
warnings = append(warnings, "GRPCServerDefaultTimeoutInSeconds is negative; use 0 to disable the timeout interceptor")
}
if c.GRPCTLSCertFile != "" && c.GRPCTLSKeyFile != "" {
if _, err := os.Stat(c.GRPCTLSCertFile); err != nil {
if os.IsNotExist(err) {
warnings = append(warnings, "GRPCTLSCertFile not found: "+c.GRPCTLSCertFile)
} else {
warnings = append(warnings, "GRPCTLSCertFile could not be accessed: "+c.GRPCTLSCertFile+": "+err.Error())
}
}
if _, err := os.Stat(c.GRPCTLSKeyFile); err != nil {
if os.IsNotExist(err) {
warnings = append(warnings, "GRPCTLSKeyFile not found: "+c.GRPCTLSKeyFile)
} else {
warnings = append(warnings, "GRPCTLSKeyFile could not be accessed: "+c.GRPCTLSKeyFile+": "+err.Error())
}
}
}
if c.OTLPEndpoint != "" {
if _, _, err := net.SplitHostPort(c.OTLPEndpoint); err != nil {
warnings = append(warnings, "OTLPEndpoint should be in host:port format")
}
}
if c.LogLevel != "" {
switch strings.ToLower(c.LogLevel) {
case "error", "warn", "warning", "info", "debug":
// valid
default:
warnings = append(warnings, "LogLevel is not a recognized level: "+c.LogLevel)
}
}
if c.GRPCServerDefaultTimeoutInSeconds > 0 && c.ShutdownDurationInSeconds > 0 &&
c.GRPCServerDefaultTimeoutInSeconds > c.ShutdownDurationInSeconds {
warnings = append(warnings, "GRPCServerDefaultTimeoutInSeconds exceeds ShutdownDurationInSeconds; in-flight RPCs may be killed before timeout")
}

return warnings
}
98 changes: 98 additions & 0 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,101 @@ func TestValidateShutdownTiming(t *testing.T) {
t.Error("healthcheck duration >= shutdown duration should produce a warning")
}
}

func TestValidateTLSFileNotFound(t *testing.T) {
c := Config{
GRPCPort: 9090,
HTTPPort: 9091,
GRPCTLSCertFile: "/nonexistent/cert.pem",
GRPCTLSKeyFile: "/nonexistent/key.pem",
}
warnings := c.Validate()
foundCert := false
foundKey := false
for _, w := range warnings {
if strings.Contains(w, "GRPCTLSCertFile not found") {
foundCert = true
}
if strings.Contains(w, "GRPCTLSKeyFile not found") {
foundKey = true
}
}
if !foundCert || !foundKey {
t.Errorf("non-existent TLS files should produce warnings, got: %v", warnings)
}
}

func TestValidateOTLPEndpointFormat(t *testing.T) {
// Invalid endpoint
c := Config{
GRPCPort: 9090,
HTTPPort: 9091,
OTLPEndpoint: "not-a-host-port",
}
warnings := c.Validate()
found := false
for _, w := range warnings {
if strings.Contains(w, "host:port") {
found = true
}
}
if !found {
t.Error("invalid OTLP endpoint should produce a warning")
}

// Valid endpoint should not warn
c.OTLPEndpoint = "localhost:4317"
warnings = c.Validate()
for _, w := range warnings {
if strings.Contains(w, "host:port") {
t.Errorf("valid OTLP endpoint should not produce a warning, got: %s", w)
}
}
}

func TestValidateLogLevel(t *testing.T) {
// Invalid level
c := Config{
GRPCPort: 9090,
HTTPPort: 9091,
LogLevel: "trace",
}
warnings := c.Validate()
found := false
for _, w := range warnings {
if strings.Contains(w, "not a recognized level") {
found = true
}
}
if !found {
t.Error("invalid log level should produce a warning")
}

// Valid level should not warn
c.LogLevel = "debug"
warnings = c.Validate()
for _, w := range warnings {
if strings.Contains(w, "not a recognized level") {
t.Errorf("valid log level should not produce a warning, got: %s", w)
}
}
}

func TestValidateTimeoutExceedsShutdown(t *testing.T) {
c := Config{
GRPCPort: 9090,
HTTPPort: 9091,
GRPCServerDefaultTimeoutInSeconds: 120,
ShutdownDurationInSeconds: 15,
}
warnings := c.Validate()
found := false
for _, w := range warnings {
if strings.Contains(w, "exceeds ShutdownDurationInSeconds") {
found = true
}
}
if !found {
t.Error("timeout exceeding shutdown duration should produce a warning")
}
}
35 changes: 24 additions & 11 deletions core.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,12 @@ func (c *cb) processConfig() {
if c.config.DisableProtoValidate {
interceptors.SetDisableProtoValidate(true)
}
if c.config.DisableDebugLogInterceptor {
interceptors.SetDisableDebugLogInterceptor(true)
}
if c.config.DebugLogHeaderName != "" {
interceptors.SetDebugLogHeaderName(c.config.DebugLogHeaderName)
}
Comment on lines +145 to +150
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR title/description says this is a docs-only clarification, but this hunk introduces new runtime behavior (new config flags affecting interceptor behavior) and is coupled with other non-doc changes in the PR (goleak TestMain, config validation additions, dependency bumps). Please update the PR title/description to reflect the functional changes, or split the functional changes into a separate PR to keep review scope clear.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recreated PR as #76 with clean branch from main — only the doc/comment changes. The previous branch had stale commits from the prior PR.

if c.config.EnablePrometheusGRPCHistogram {
if len(c.config.PrometheusGRPCHistogramBuckets) > 0 {
interceptors.SetServerMetricsOptions(
Expand Down Expand Up @@ -390,19 +396,26 @@ func spanRouteMiddleware(next runtime.HandlerFunc) runtime.HandlerFunc {
}
}

// getCustomHeaderMatcher returns a matcher that matches the given header and prefix
func getCustomHeaderMatcher(prefixes []string, header string) func(string) (string, bool) {
header = strings.ToLower(header)
// getCustomHeaderMatcher returns a matcher that matches the given exact headers and prefixes.
// Exact-match headers (e.g., trace header, debug log header) are forwarded from HTTP to gRPC metadata.
func getCustomHeaderMatcher(prefixes []string, headers ...string) func(string) (string, bool) {
lowerHeaders := make([]string, 0, len(headers))
for _, h := range headers {
if h != "" {
lowerHeaders = append(lowerHeaders, strings.ToLower(h))
}
}
return func(key string) (string, bool) {
key = strings.ToLower(key)

if key == header {
return key, true
} else if len(prefixes) > 0 {
for _, prefix := range prefixes {
if len(prefix) > 0 && strings.HasPrefix(key, strings.ToLower(prefix)) {
return key, true
}
for _, h := range lowerHeaders {
if key == h {
return key, true
}
}
for _, prefix := range prefixes {
if len(prefix) > 0 && strings.HasPrefix(key, strings.ToLower(prefix)) {
return key, true
Comment on lines +416 to +418
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getCustomHeaderMatcher lowercases each configured prefix on every header match attempt (strings.ToLower(prefix) inside the per-request matcher). Since this function runs for every inbound HTTP header, consider normalizing prefixes once (similar to lowerHeaders) to avoid repeated work on hot paths.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — fixed in #76. Prefixes are now normalized once at init alongside headers.

}
}

Expand All @@ -425,7 +438,7 @@ func (c *cb) initHTTP(ctx context.Context) (*http.Server, error) {

muxOpts := []runtime.ServeMuxOption{
runtime.WithIncomingHeaderMatcher(
getCustomHeaderMatcher(allowedHttpHeaderPrefixes, c.config.TraceHeaderName),
getCustomHeaderMatcher(allowedHttpHeaderPrefixes, c.config.TraceHeaderName, c.config.DebugLogHeaderName),
),
runtime.WithMarshalerOption("application/proto", pMar),
runtime.WithMarshalerOption("application/protobuf", pMar),
Expand Down
18 changes: 18 additions & 0 deletions core_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,3 +164,21 @@ func TestGetCustomHeaderMatcher(t *testing.T) {
}
})
}

func TestGetCustomHeaderMatcher_MultipleHeaders(t *testing.T) {
// removed t.Parallel() — core tests mutate package-level globals
matcher := getCustomHeaderMatcher(nil, "X-Trace-Id", "X-Debug-Log-Level")

_, traceMatched := matcher("X-Trace-Id")
if !traceMatched {
t.Fatal("expected trace header to match")
}
_, debugMatched := matcher("X-Debug-Log-Level")
if !debugMatched {
t.Fatal("expected debug log header to match")
}
_, unknownMatched := matcher("X-Random")
if unknownMatched {
t.Fatal("expected unknown header to not match")
}
}
9 changes: 5 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ go 1.25.9

require (
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5
github.com/go-coldbrew/errors v0.2.13
github.com/go-coldbrew/errors v0.2.14
github.com/go-coldbrew/hystrixprometheus v0.1.2
github.com/go-coldbrew/interceptors v0.1.22
github.com/go-coldbrew/log v0.3.1
github.com/go-coldbrew/interceptors v0.1.23
github.com/go-coldbrew/log v0.3.2
github.com/go-coldbrew/options v0.3.0
github.com/go-coldbrew/tracing v0.2.0
github.com/go-coldbrew/tracing v0.2.2
github.com/golang/protobuf v1.5.4
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0
Expand All @@ -26,6 +26,7 @@ require (
go.opentelemetry.io/otel/sdk/metric v1.43.0
go.opentelemetry.io/otel/trace v1.43.0
go.uber.org/automaxprocs v1.6.0
go.uber.org/goleak v1.3.0
golang.org/x/sync v0.20.0
google.golang.org/grpc v1.79.3
google.golang.org/protobuf v1.36.11
Expand Down
16 changes: 8 additions & 8 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -193,18 +193,18 @@ github.com/ghostiam/protogetter v0.3.20 h1:oW7OPFit2FxZOpmMRPP9FffU4uUpfeE/rEdE1
github.com/ghostiam/protogetter v0.3.20/go.mod h1:FjIu5Yfs6FT391m+Fjp3fbAYJ6rkL/J6ySpZBfnODuI=
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
github.com/go-coldbrew/errors v0.2.13 h1:OUOEWLml6Mstt0Sskc94VVPhZ/rMfONeNOlHqycaq1g=
github.com/go-coldbrew/errors v0.2.13/go.mod h1:eFLqeTPhgGyvsVVRXcdKGUxEnm+chrpddhL8lfogonk=
github.com/go-coldbrew/errors v0.2.14 h1:SQcV9Kw+hNfNGXjvu4fWl5uXw5NRD6lW+rkHgtzAYZE=
github.com/go-coldbrew/errors v0.2.14/go.mod h1:f9eGGKKF9KmyCpSWZRSqqV4HRWqbzmh1E9lyL8jyL+Y=
github.com/go-coldbrew/hystrixprometheus v0.1.2 h1:WSt4FtYr8xNDKgdGWYpMfXGFIK7zdDSBwDSbpuPhBHI=
github.com/go-coldbrew/hystrixprometheus v0.1.2/go.mod h1:OrNRHHxZagpmQXNp//oHKOemGSU0ScOqEcJgeKbJ+wg=
github.com/go-coldbrew/interceptors v0.1.22 h1:gglnX6iFl1unC4CoJRauwEmw+ldr5ZsLdxmqhE38Kk8=
github.com/go-coldbrew/interceptors v0.1.22/go.mod h1:qR1CzRxSemxlo+5UhSBYmNbfycRRIWwTO7zLgf3E6GE=
github.com/go-coldbrew/log v0.3.1 h1:Cyx6KWBW3wZE8dSru6mIDFtUnJ1R2h6C44ZDo5bOqAo=
github.com/go-coldbrew/log v0.3.1/go.mod h1:xxZGHBfni5eXc6Azg+g8UPTmqTJLAf9sX46gAT8o39Y=
github.com/go-coldbrew/interceptors v0.1.23 h1:mbrAx4ztOMei2DeLyxjXKttUvGAFeU5heDVMPhTOctw=
github.com/go-coldbrew/interceptors v0.1.23/go.mod h1:CjH6gc6GrdTNJnw+d7JigYtreWW5PcaGc5QH7zpsJPk=
github.com/go-coldbrew/log v0.3.2 h1:CoHa0PGX7a7o/Cv/ke7PdQfq4LKtbPVypUf3uXcRLMs=
github.com/go-coldbrew/log v0.3.2/go.mod h1:tumRNCmLWRep5wnhS/vzDQ7UMinF6OZ7WW8K/qlXAzc=
github.com/go-coldbrew/options v0.3.0 h1:JwyVntb9bzBeFdaHFK6yGVVz30G3aVlqJJ6uVyYQfCc=
github.com/go-coldbrew/options v0.3.0/go.mod h1:8JlmgVJXFoY1KiDLsyMmR//q1U1aBItCexvTrVT2Y60=
github.com/go-coldbrew/tracing v0.2.0 h1:WGfdp5PNunOGfjTZGXPFaip3G5qOOMP622JFYA90ML4=
github.com/go-coldbrew/tracing v0.2.0/go.mod h1:phF8WDsadDKK20lgB0Zv2/ocVIrCbVziMd3MMxqr+aU=
github.com/go-coldbrew/tracing v0.2.2 h1:pvRMSwla5txZgtQOi18OqsuJhtsqPbfeC1arH9tJMys=
github.com/go-coldbrew/tracing v0.2.2/go.mod h1:mMYoCOqxFN28fEPMFbufwK0w/6rCiSjGftSr0KrZ0T0=
github.com/go-critic/go-critic v0.14.3 h1:5R1qH2iFeo4I/RJU8vTezdqs08Egi4u5p6vOESA0pog=
github.com/go-critic/go-critic v0.14.3/go.mod h1:xwntfW6SYAd7h1OqDzmN6hBX/JxsEKl5up/Y2bsxgVQ=
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
Expand Down
Loading
Loading