pkg/gateway/server/middleware/middleware.go
Source
- Package:
middleware - File:
pkg/gateway/server/middleware/middleware.go - GitHub: https://github.com/theroutercompany/api_router/blob/main/pkg/gateway/server/middleware/middleware.go
Overview
What:
A collection of HTTP middleware functions used by the gateway server.
The middleware in this file covers common cross-cutting concerns:
- request/trace ID propagation to responses
- baseline security headers
- request body size limits
- per-client rate limiting
- CORS enforcement
- structured request logging and protocol metrics integration (including upgraded connections)
Why:
Middleware is the "seam" where the gateway enforces platform policy consistently for every request.
This package keeps the middleware logic decoupled from specific implementations by expressing its dependencies as small interfaces and function types (logger, problem writer, ID generators, rate-limit allow/key functions, protocol metric callbacks).
That design makes it easier to:
- unit test the middleware in isolation
- swap implementations (e.g., different logging backend)
- compose a server pipeline without creating import cycles
How:
Each exported middleware constructor returns a standard wrapper:
func(http.Handler) http.Handler.
Callers (typically pkg/gateway/server) build a chain by composing these wrappers.
Many middleware functions are intentionally nil-safe: if a required dependency is not provided,
they return an identity wrapper (or the next handler) to keep server wiring simple.
The Logging middleware uses a custom ResponseWriter wrapper to:
- capture status code and bytes written
- support
Flush/Pushpassthrough - intercept
Hijack()so upgraded connections can be wrapped and tracked on close
Notes:
Be careful about logging sensitive data. This middleware logs request path and metadata, not bodies.
Body size limits use both Content-Length checks and http.MaxBytesReader for enforcement.
Contents
Imports
import block 1
import (
"bufio"
"context"
"errors"
"fmt"
"net"
"net/http"
"strings"
"sync"
"time"
"github.com/rs/cors"
)
Types
type block 1
type Logger interface {
Infow(msg string, keysAndValues ...any)
Warnw(msg string, keysAndValues ...any)
Errorw(msg string, keysAndValues ...any)
}
Logger
What: Minimal logger interface required by middleware.
Why: Avoids importing the full logging package and prevents dependency cycles.
How: Implemented by pkg/log.Logger adapters and passed into Logging.
Notes: Uses Zap-style *w methods (message plus key/value fields).
type block 2
type ProblemWriter func(w http.ResponseWriter, status int, title, detail, traceID, instance string)
ProblemWriter
What: Function that writes a problem+json response.
Why: Centralizes error response formatting so middleware can emit consistent error bodies.
How: Called by BodyLimit, RateLimit, and CORS when provided; otherwise those middleware fall back to http.Error.
type block 3
type EnsureIDs func(*http.Request) (*http.Request, string, string)
EnsureIDs
What: Function type that ensures a request has request/trace identifiers.
Why: ID generation/extraction is owned by higher-level middleware or tracing systems, not by this package.
How: Returns an updated request (if needed) plus request ID and trace ID strings.
type block 4
type TraceIDFromContext func(context.Context) string
TraceIDFromContext
What: Extractor that reads a trace ID from a context.
Why: Logging and problem responses often want trace IDs for correlation, but the storage mechanism is owned elsewhere.
How: Called by Logging and by error middleware to include trace IDs when available.
type block 5
type RequestIDFromContext func(context.Context) string
RequestIDFromContext
What: Extractor that reads a request ID from a context.
Why: Logging should not know how IDs are stored in context.
How: Called by Logging to add requestId field when non-empty.
type block 6
type ClientAddress func(*http.Request) string
ClientAddress
What: Extractor that resolves a client address string from an HTTP request.
Why: Logging should record a stable client identifier without hardcoding a specific proxy/header strategy.
How: Used by Logging to optionally append a remoteAddr field.
Notes: Implementations typically consult X-Forwarded-For and X-Real-IP in addition to RemoteAddr.
type block 7
type TrackFunc func(*http.Request) func(status int, elapsed time.Duration)
TrackFunc
What: Callback that returns a per-request metric recording function.
Why: Allows middleware to time requests and record metrics without importing Prometheus packages.
How: Called at request start; the returned function is called at request end with status and elapsed duration.
type block 8
type HijackedFunc func(*http.Request) (func(), func(net.Conn) net.Conn)
HijackedFunc
What: Callback used to integrate upgraded-connection tracking and wrapping into the logging middleware.
Why: Upgraded connections need lifetime hooks (close callbacks) and sometimes need connection wrapping (timeouts, tracking, limits).
How: Called from loggingResponseWriter.Hijack() and returns an on-close callback plus an optional net.Conn wrapper.
Notes: The wrapper is applied before the on-close tracking wrapper.
type block 9
type AllowFunc func(key string, now time.Time) bool
AllowFunc
What: Function type that decides whether a rate-limit key is allowed at a given time.
Why: Decouples middleware from any specific rate limiter implementation.
How: Called by RateLimit with a derived key and a timestamp.
type block 10
type ClientKey func(*http.Request) string
ClientKey
What: Function type that derives a rate-limit key from a request.
Why: Allows the rate limiting policy to choose between IP-based keys, API keys, JWT subjects, etc.
How: Used by RateLimit before calling AllowFunc.
type block 11
type loggingResponseWriter struct {
http.ResponseWriter
status int
bytes int
hijackTracker func() (func(), func(net.Conn) net.Conn)
hijackOnce sync.Once
}
loggingResponseWriter
What: An http.ResponseWriter wrapper that captures status/bytes and supports hijack tracking.
Why: The standard interface does not expose status codes or byte counts, and upgraded connections need lifecycle hooks.
How: Embeds the underlying writer and intercepts WriteHeader, Write, and Hijack.
type block 12
type trackingConn struct {
net.Conn
onClose func()
once sync.Once
timeout time.Duration
}
trackingConn
What: Connection wrapper used to invoke an on-close callback once.
Why: Connection lifetime metrics and accounting require a reliable "closed" signal.
How: Wraps a net.Conn, delegates most operations, and calls onClose via sync.Once in Close().
Notes: Also includes optional read/write deadline enforcement when timeout is set.
Functions and Methods
RequestMetadata
What: Ensures every request has stable IDs and echoes them back in response headers.
Why: Request/trace IDs are foundational for debugging, log correlation, and distributed tracing.
How: Calls the injected EnsureIDs function to obtain a possibly-updated request and IDs, then sets X-Request-Id and X-Trace-Id headers before calling the next handler.
Notes: If ensure is nil, this middleware becomes a no-op wrapper.
func RequestMetadata(ensure EnsureIDs) func(http.Handler) http.Handler {
if ensure == nil {
return func(next http.Handler) http.Handler { return next }
}
return func(next http.Handler) http.Handler {
if next == nil {
return http.HandlerFunc(func(http.ResponseWriter, *http.Request) {})
}
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
req, requestID, traceID := ensure(r)
w.Header().Set("X-Request-Id", requestID)
if traceID != "" {
w.Header().Set("X-Trace-Id", traceID)
}
next.ServeHTTP(w, req)
})
}
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L53:
if ensure == nil { return func(next http.Handler) http.Handler { return next } }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L54:
return func(next http.Handler) http.Handler { return next }- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values). - Nested steps:
- L54:
func(next http.Handler) http.Handler { return next }- What: Defines an inline function (closure).
- Why: Encapsulates callback logic and may capture variables from the surrounding scope.
- How: Declares a
funcliteral and uses it as a value (for example, as an HTTP handler or callback). - Nested steps:
- L54:
return next- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L54:
- L54:
- L54:
- L57:
return func(next http.Handler) http.Handler { if next == nil { return http.HandlerFunc(func(http.ResponseWriter, *http.Request) {}) } retur…- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values). - Nested steps:
- L57:
func(next http.Handler) http.Handler { if next == nil { return http.HandlerFunc(func(http.ResponseWriter, *http.Request) {}) } return http.…- What: Defines an inline function (closure).
- Why: Encapsulates callback logic and may capture variables from the surrounding scope.
- How: Declares a
funcliteral and uses it as a value (for example, as an HTTP handler or callback). - Nested steps:
- L58:
if next == nil { return http.HandlerFunc(func(http.ResponseWriter, *http.Request) {}) }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L59:
return http.HandlerFunc(func(http.ResponseWriter, *http.Request) {})- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values). - Nested steps:
- L59:
func(http.ResponseWriter, *http.Request) {}- What: Defines an inline function (closure).
- Why: Encapsulates callback logic and may capture variables from the surrounding scope.
- How: Declares a
funcliteral and uses it as a value (for example, as an HTTP handler or callback).
- L59:
- L59:
- L61:
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { req, requestID, traceID := ensure(r) w.Header().Set("X-Request-Id", …- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values). - Nested steps:
- L61:
func(w http.ResponseWriter, r *http.Request) { req, requestID, traceID := ensure(r) w.Header().Set("X-Request-Id", requestID) if traceID !=…- What: Defines an inline function (closure).
- Why: Encapsulates callback logic and may capture variables from the surrounding scope.
- How: Declares a
funcliteral and uses it as a value (for example, as an HTTP handler or callback). - Nested steps:
- L62:
req, requestID, traceID := ensure(r)- What: Defines req, requestID, traceID.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L63:
w.Header().Set("X-Request-Id", requestID)- What: Calls w.Header().Set.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L64:
if traceID != "" { w.Header().Set("X-Trace-Id", traceID) }- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L65:
w.Header().Set("X-Trace-Id", traceID)- What: Calls w.Header().Set.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L65:
- L67:
next.ServeHTTP(w, req)- What: Calls next.ServeHTTP.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L62:
- L61:
- L58:
- L57:
SecurityHeaders
What: Adds baseline security hardening headers to all responses.
Why: Many responses are proxied, but the gateway still acts as the edge and should provide consistent browser hardening defaults.
How: Sets headers like X-Content-Type-Options, X-Frame-Options, Referrer-Policy, and Permissions-Policy before calling the next handler.
Notes: These headers are conservative defaults. If you need CSP/HSTS, add them at a higher layer with environment-specific configuration.
func SecurityHeaders() func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
if next == nil {
return http.HandlerFunc(func(http.ResponseWriter, *http.Request) {})
}
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
headers := w.Header()
headers.Set("X-Content-Type-Options", "nosniff")
headers.Set("X-Frame-Options", "DENY")
headers.Set("Referrer-Policy", "no-referrer")
headers.Set("Permissions-Policy", "geolocation=(), microphone=(), camera=()")
next.ServeHTTP(w, r)
})
}
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L74:
return func(next http.Handler) http.Handler { if next == nil { return http.HandlerFunc(func(http.ResponseWriter, *http.Request) {}) } retur…- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values). - Nested steps:
- L74:
func(next http.Handler) http.Handler { if next == nil { return http.HandlerFunc(func(http.ResponseWriter, *http.Request) {}) } return http.…- What: Defines an inline function (closure).
- Why: Encapsulates callback logic and may capture variables from the surrounding scope.
- How: Declares a
funcliteral and uses it as a value (for example, as an HTTP handler or callback). - Nested steps:
- L75:
if next == nil { return http.HandlerFunc(func(http.ResponseWriter, *http.Request) {}) }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L76:
return http.HandlerFunc(func(http.ResponseWriter, *http.Request) {})- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values). - Nested steps:
- L76:
func(http.ResponseWriter, *http.Request) {}- What: Defines an inline function (closure).
- Why: Encapsulates callback logic and may capture variables from the surrounding scope.
- How: Declares a
funcliteral and uses it as a value (for example, as an HTTP handler or callback).
- L76:
- L76:
- L78:
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { headers := w.Header() headers.Set("X-Content-Type-Options", "nosniff…- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values). - Nested steps:
- L78:
func(w http.ResponseWriter, r *http.Request) { headers := w.Header() headers.Set("X-Content-Type-Options", "nosniff") headers.Set("X-Frame-…- What: Defines an inline function (closure).
- Why: Encapsulates callback logic and may capture variables from the surrounding scope.
- How: Declares a
funcliteral and uses it as a value (for example, as an HTTP handler or callback). - Nested steps:
- L79:
headers := w.Header()- What: Defines headers.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L80:
headers.Set("X-Content-Type-Options", "nosniff")- What: Calls headers.Set.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L81:
headers.Set("X-Frame-Options", "DENY")- What: Calls headers.Set.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L82:
headers.Set("Referrer-Policy", "no-referrer")- What: Calls headers.Set.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L83:
headers.Set("Permissions-Policy", "geolocation=(), microphone=(), camera=()")- What: Calls headers.Set.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L84:
next.ServeHTTP(w, r)- What: Calls next.ServeHTTP.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L79:
- L78:
- L75:
- L74:
BodyLimit
What: Enforces a maximum request body size.
Why: Protects the gateway from accidental or malicious large payloads that can consume memory and upstream bandwidth.
How: Rejects requests with Content-Length above the limit with 413; otherwise wraps r.Body with http.MaxBytesReader so reads cannot exceed the limit.
Notes: If Content-Length is unknown (-1), the MaxBytesReader path is the enforcement mechanism.
func BodyLimit(limit int64, trace TraceIDFromContext, write ProblemWriter) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
if next == nil {
return http.HandlerFunc(func(http.ResponseWriter, *http.Request) {})
}
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.ContentLength > limit {
tid := ""
if trace != nil {
tid = trace(r.Context())
}
if write != nil {
write(w, http.StatusRequestEntityTooLarge, "Payload Too Large", fmt.Sprintf("Request body exceeds %d bytes", limit), tid, r.URL.Path)
return
}
http.Error(w, http.StatusText(http.StatusRequestEntityTooLarge), http.StatusRequestEntityTooLarge)
return
}
if r.Body != nil {
r.Body = http.MaxBytesReader(w, r.Body, limit)
}
next.ServeHTTP(w, r)
})
}
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L91:
return func(next http.Handler) http.Handler { if next == nil { return http.HandlerFunc(func(http.ResponseWriter, *http.Request) {}) } retur…- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values). - Nested steps:
- L91:
func(next http.Handler) http.Handler { if next == nil { return http.HandlerFunc(func(http.ResponseWriter, *http.Request) {}) } return http.…- What: Defines an inline function (closure).
- Why: Encapsulates callback logic and may capture variables from the surrounding scope.
- How: Declares a
funcliteral and uses it as a value (for example, as an HTTP handler or callback). - Nested steps:
- L92:
if next == nil { return http.HandlerFunc(func(http.ResponseWriter, *http.Request) {}) }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L93:
return http.HandlerFunc(func(http.ResponseWriter, *http.Request) {})- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values). - Nested steps:
- L93:
func(http.ResponseWriter, *http.Request) {}- What: Defines an inline function (closure).
- Why: Encapsulates callback logic and may capture variables from the surrounding scope.
- How: Declares a
funcliteral and uses it as a value (for example, as an HTTP handler or callback).
- L93:
- L93:
- L95:
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.ContentLength > limit { tid := "" if trace != nil { tid = trace…- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values). - Nested steps:
- L95:
func(w http.ResponseWriter, r *http.Request) { if r.ContentLength > limit { tid := "" if trace != nil { tid = trace(r.Context()) } if write…- What: Defines an inline function (closure).
- Why: Encapsulates callback logic and may capture variables from the surrounding scope.
- How: Declares a
funcliteral and uses it as a value (for example, as an HTTP handler or callback). - Nested steps:
- L96:
if r.ContentLength > limit { tid := "" if trace != nil { tid = trace(r.Context()) } if write != nil { write(w, http.StatusRequestEntityTooL…- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L97:
tid := ""- What: Defines tid.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L98:
if trace != nil { tid = trace(r.Context()) }- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L99:
tid = trace(r.Context())- What: Assigns tid.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L99:
- L101:
if write != nil { write(w, http.StatusRequestEntityTooLarge, "Payload Too Large", fmt.Sprintf("Request body exceeds %d bytes", limit), tid,…- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L102:
write(w, http.StatusRequestEntityTooLarge, "Payload Too Large", fmt.Sprintf("Request body exceeds %d bytes", limit), tid, r.URL.Path)- What: Calls write.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L103:
return- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L102:
- L105:
http.Error(w, http.StatusText(http.StatusRequestEntityTooLarge), http.StatusRequestEntityTooLarge)- What: Calls http.Error.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L106:
return- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L97:
- L108:
if r.Body != nil { r.Body = http.MaxBytesReader(w, r.Body, limit) }- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L109:
r.Body = http.MaxBytesReader(w, r.Body, limit)- What: Assigns r.Body.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L109:
- L111:
next.ServeHTTP(w, r)- What: Calls next.ServeHTTP.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L96:
- L95:
- L92:
- L91:
RateLimit
What: Enforces per-client rate limiting.
Why: Provides a safety valve against abusive clients and protects upstream services from burst load.
How: Uses a provided AllowFunc (rate-limit decision) and ClientKey (key derivation). OPTIONS requests bypass the limiter. Exceeded limits return 429 with a problem response when available.
Notes: The middleware is deliberately generic; the actual limiter implementation lives elsewhere.
func RateLimit(allow AllowFunc, key ClientKey, now func() time.Time, trace TraceIDFromContext, write ProblemWriter) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
if next == nil || allow == nil || key == nil || now == nil {
return next
}
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodOptions {
next.ServeHTTP(w, r)
return
}
client := key(r)
if allow(client, now()) {
next.ServeHTTP(w, r)
return
}
if write != nil {
tid := ""
if trace != nil {
tid = trace(r.Context())
}
write(w, http.StatusTooManyRequests, "Too Many Requests", "Rate limit exceeded", tid, r.URL.Path)
} else {
http.Error(w, http.StatusText(http.StatusTooManyRequests), http.StatusTooManyRequests)
}
})
}
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L118:
return func(next http.Handler) http.Handler { if next == nil || allow == nil || key == nil || now == nil { return next } return http.Handle…- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values). - Nested steps:
- L118:
func(next http.Handler) http.Handler { if next == nil || allow == nil || key == nil || now == nil { return next } return http.HandlerFunc(f…- What: Defines an inline function (closure).
- Why: Encapsulates callback logic and may capture variables from the surrounding scope.
- How: Declares a
funcliteral and uses it as a value (for example, as an HTTP handler or callback). - Nested steps:
- L119:
if next == nil || allow == nil || key == nil || now == nil { return next }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L120:
return next- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L120:
- L122:
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method == http.MethodOptions { next.ServeHTTP(w, r) return } cl…- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values). - Nested steps:
- L122:
func(w http.ResponseWriter, r *http.Request) { if r.Method == http.MethodOptions { next.ServeHTTP(w, r) return } client := key(r) if allow(…- What: Defines an inline function (closure).
- Why: Encapsulates callback logic and may capture variables from the surrounding scope.
- How: Declares a
funcliteral and uses it as a value (for example, as an HTTP handler or callback). - Nested steps:
- L123:
if r.Method == http.MethodOptions { next.ServeHTTP(w, r) return }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L124:
next.ServeHTTP(w, r)- What: Calls next.ServeHTTP.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L125:
return- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L124:
- L127:
client := key(r)- What: Defines client.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L128:
if allow(client, now()) { next.ServeHTTP(w, r) return }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L129:
next.ServeHTTP(w, r)- What: Calls next.ServeHTTP.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L130:
return- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L129:
- L132:
if write != nil { tid := "" if trace != nil { tid = trace(r.Context()) } write(w, http.StatusTooManyRequests, "Too Many Requests", "Rate li…- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L133:
tid := ""- What: Defines tid.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L134:
if trace != nil { tid = trace(r.Context()) }- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L135:
tid = trace(r.Context())- What: Assigns tid.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L135:
- L137:
write(w, http.StatusTooManyRequests, "Too Many Requests", "Rate limit exceeded", tid, r.URL.Path)- What: Calls write.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L139:
http.Error(w, http.StatusText(http.StatusTooManyRequests), http.StatusTooManyRequests)- What: Calls http.Error.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L133:
- L123:
- L122:
- L119:
- L118:
CORS
What: Applies CORS policy using the provided *cors.Cors configuration.
Why: Browsers enforce CORS, and the gateway needs to provide a centralized origin policy across all proxied endpoints.
How: Wraps the next handler with handler.Handler(next) and pre-checks OriginAllowed so disallowed origins can return a consistent 403 problem response.
Notes: The explicit origin check exists so operators get clear feedback instead of relying on default cors library behaviour.
func CORS(handler *cors.Cors, trace TraceIDFromContext, write ProblemWriter) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
if handler == nil || next == nil {
return next
}
corsHandler := handler.Handler(next)
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
origin := strings.TrimSpace(r.Header.Get("Origin"))
if origin != "" && !handler.OriginAllowed(r) {
if write != nil {
tid := ""
if trace != nil {
tid = trace(r.Context())
}
write(w, http.StatusForbidden, "Not allowed by CORS", fmt.Sprintf("Origin %s is not allowed", origin), tid, r.URL.Path)
} else {
http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
}
return
}
corsHandler.ServeHTTP(w, r)
})
}
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L147:
return func(next http.Handler) http.Handler { if handler == nil || next == nil { return next } corsHandler := handler.Handler(next) return …- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values). - Nested steps:
- L147:
func(next http.Handler) http.Handler { if handler == nil || next == nil { return next } corsHandler := handler.Handler(next) return http.Ha…- What: Defines an inline function (closure).
- Why: Encapsulates callback logic and may capture variables from the surrounding scope.
- How: Declares a
funcliteral and uses it as a value (for example, as an HTTP handler or callback). - Nested steps:
- L148:
if handler == nil || next == nil { return next }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L149:
return next- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L149:
- L151:
corsHandler := handler.Handler(next)- What: Defines corsHandler.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L152:
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { origin := strings.TrimSpace(r.Header.Get("Origin")) if origin != "" …- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values). - Nested steps:
- L152:
func(w http.ResponseWriter, r *http.Request) { origin := strings.TrimSpace(r.Header.Get("Origin")) if origin != "" && !handler.OriginAllowe…- What: Defines an inline function (closure).
- Why: Encapsulates callback logic and may capture variables from the surrounding scope.
- How: Declares a
funcliteral and uses it as a value (for example, as an HTTP handler or callback). - Nested steps:
- L153:
origin := strings.TrimSpace(r.Header.Get("Origin"))- What: Defines origin.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L154:
if origin != "" && !handler.OriginAllowed(r) { if write != nil { tid := "" if trace != nil { tid = trace(r.Context()) } write(w, http.Statu…- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L155:
if write != nil { tid := "" if trace != nil { tid = trace(r.Context()) } write(w, http.StatusForbidden, "Not allowed by CORS", fmt.Sprintf(…- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L156:
tid := ""- What: Defines tid.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L157:
if trace != nil { tid = trace(r.Context()) }- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- L160:
write(w, http.StatusForbidden, "Not allowed by CORS", fmt.Sprintf("Origin %s is not allowed", origin), tid, r.URL.Path)- What: Calls write.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L162:
http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)- What: Calls http.Error.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L156:
- L164:
return- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L155:
- L166:
corsHandler.ServeHTTP(w, r)- What: Calls corsHandler.ServeHTTP.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L153:
- L152:
- L148:
- L147:
Logging
What: Emits structured logs for each HTTP request and optionally integrates protocol metrics tracking.
Why:
Request logs are the primary operational visibility mechanism for the gateway. Integrating protocol metrics here keeps metrics emission aligned with request lifecycle and avoids duplicating timing logic.
How:
- Records start time and builds a
trackFnby callingtrack(r)(if provided). - Wraps the
ResponseWriterwithloggingResponseWriterto capture status and bytes. - Calls
next.ServeHTTP. - Computes elapsed duration and calls
trackFn(status, duration). - Logs a message with standard fields (method, path, status, durationMs, bytesWritten) and optional requestId/traceId/remoteAddr.
When hijacked is provided, the response writer's Hijack() path calls it to obtain:
- an on-close callback (for metrics)
- an optional
net.Connwrapper (for deadline enforcement or additional tracking)
Notes:
Logging level is chosen by status code (5xx error, 4xx warn, otherwise info). Avoid adding headers or bodies to logs unless they are explicitly scrubbed.
func Logging(
logger Logger,
track TrackFunc,
hijacked HijackedFunc,
requestID RequestIDFromContext,
traceID TraceIDFromContext,
clientAddr ClientAddress,
) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
if next == nil || logger == nil {
return next
}
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
trackFn := func(int, time.Duration) {}
if track != nil {
if fn := track(r); fn != nil {
trackFn = fn
}
}
var hijackTracker func() (func(), func(net.Conn) net.Conn)
if hijacked != nil {
hijackTracker = func() (func(), func(net.Conn) net.Conn) {
return hijacked(r)
}
}
writer := newLoggingResponseWriter(w, hijackTracker)
next.ServeHTTP(writer, r)
duration := time.Since(start)
status := writer.status
if status == 0 {
status = http.StatusOK
}
trackFn(status, duration)
fields := []any{
"method", r.Method,
"path", r.URL.Path,
"status", status,
"durationMs", float64(duration.Microseconds()) / 1000.0,
"bytesWritten", writer.bytes,
}
if requestID != nil {
if rid := requestID(r.Context()); rid != "" {
fields = append(fields, "requestId", rid)
}
}
if traceID != nil {
if tid := traceID(r.Context()); tid != "" {
fields = append(fields, "traceId", tid)
}
}
if clientAddr != nil {
if remote := clientAddr(r); remote != "" {
fields = append(fields, "remoteAddr", remote)
}
}
switch {
case status >= 500:
logger.Errorw("http request completed", fields...)
case status >= 400:
logger.Warnw("http request completed", fields...)
default:
logger.Infow("http request completed", fields...)
}
})
}
}
Walkthrough
Expand walkthrough (44 steps)
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L180:
return func(next http.Handler) http.Handler { if next == nil || logger == nil { return next } return http.HandlerFunc(func(w http.ResponseW…- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values). - Nested steps:
- L180:
func(next http.Handler) http.Handler { if next == nil || logger == nil { return next } return http.HandlerFunc(func(w http.ResponseWriter, …- What: Defines an inline function (closure).
- Why: Encapsulates callback logic and may capture variables from the surrounding scope.
- How: Declares a
funcliteral and uses it as a value (for example, as an HTTP handler or callback). - Nested steps:
- L181:
if next == nil || logger == nil { return next }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L182:
return next- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L182:
- L185:
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { start := time.Now() trackFn := func(int, time.Duration) {} if track …- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values). - Nested steps:
- L185:
func(w http.ResponseWriter, r *http.Request) { start := time.Now() trackFn := func(int, time.Duration) {} if track != nil { if fn := track(…- What: Defines an inline function (closure).
- Why: Encapsulates callback logic and may capture variables from the surrounding scope.
- How: Declares a
funcliteral and uses it as a value (for example, as an HTTP handler or callback). - Nested steps:
- L186:
start := time.Now()- What: Defines start.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L188:
trackFn := func(int, time.Duration) {}- What: Defines trackFn.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- Nested steps:
- L188:
func(int, time.Duration) {}- What: Defines an inline function (closure).
- Why: Encapsulates callback logic and may capture variables from the surrounding scope.
- How: Declares a
funcliteral and uses it as a value (for example, as an HTTP handler or callback).
- L188:
- L189:
if track != nil { if fn := track(r); fn != nil { trackFn = fn } }- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L190:
if fn := track(r); fn != nil { trackFn = fn }- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L190:
fn := track(r)- What: Defines fn.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L191:
trackFn = fn- What: Assigns trackFn.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L190:
- L190:
- L195:
var hijackTracker func() (func(), func(net.Conn) net.Conn)- What: Declares local names.
- Why: Introduces variables or types used later in the function.
- How: Executes a Go declaration statement inside the function body.
- L196:
if hijacked != nil { hijackTracker = func() (func(), func(net.Conn) net.Conn) { return hijacked(r) } }- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L197:
hijackTracker = func() (func(), func(net.Conn) net.Conn) { return hijacked(r) }- What: Assigns hijackTracker.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- Nested steps:
- L197:
func() (func(), func(net.Conn) net.Conn) { return hijacked(r) }- What: Defines an inline function (closure).
- Why: Encapsulates callback logic and may capture variables from the surrounding scope.
- How: Declares a
funcliteral and uses it as a value (for example, as an HTTP handler or callback).
- L197:
- L197:
- L202:
writer := newLoggingResponseWriter(w, hijackTracker)- What: Defines writer.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L203:
next.ServeHTTP(writer, r)- What: Calls next.ServeHTTP.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L205:
duration := time.Since(start)- What: Defines duration.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L206:
status := writer.status- What: Defines status.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L207:
if status == 0 { status = http.StatusOK }- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L208:
status = http.StatusOK- What: Assigns status.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L208:
- L211:
trackFn(status, duration)- What: Calls trackFn.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L213:
fields := []any{ "method", r.Method, "path", r.URL.Path, "status", status, "durationMs", float64(duration.Microseconds()) / 1000.0, "bytesW…- What: Defines fields.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L221:
if requestID != nil { if rid := requestID(r.Context()); rid != "" { fields = append(fields, "requestId", rid) } }- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L222:
if rid := requestID(r.Context()); rid != "" { fields = append(fields, "requestId", rid) }- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L222:
rid := requestID(r.Context())- What: Defines rid.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L223:
fields = append(fields, "requestId", rid)- What: Assigns fields.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L222:
- L222:
- L226:
if traceID != nil { if tid := traceID(r.Context()); tid != "" { fields = append(fields, "traceId", tid) } }- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L227:
if tid := traceID(r.Context()); tid != "" { fields = append(fields, "traceId", tid) }- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L227:
tid := traceID(r.Context())- What: Defines tid.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L228:
fields = append(fields, "traceId", tid)- What: Assigns fields.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L227:
- L227:
- L231:
if clientAddr != nil { if remote := clientAddr(r); remote != "" { fields = append(fields, "remoteAddr", remote) } }- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L232:
if remote := clientAddr(r); remote != "" { fields = append(fields, "remoteAddr", remote) }- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L232:
remote := clientAddr(r)- What: Defines remote.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L233:
fields = append(fields, "remoteAddr", remote)- What: Assigns fields.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L232:
- L232:
- L237:
switch { case status >= 500: logger.Errorw("http request completed", fields...) case status >= 400: logger.Warnw("http request completed", …- What: Selects a branch from multiple cases.
- Why: Keeps multi-case branching readable and explicit.
- How: Evaluates the switch expression and executes the first matching case.
- Nested steps:
- L238:
case status >= 500:- What: Selects a switch case.
- Why: Makes multi-branch control flow explicit and readable.
- How: Runs this case body when the switch value matches (or when default is selected).
- Nested steps:
- L239:
logger.Errorw("http request completed", fields...)- What: Calls logger.Errorw.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L239:
- L240:
case status >= 400:- What: Selects a switch case.
- Why: Makes multi-branch control flow explicit and readable.
- How: Runs this case body when the switch value matches (or when default is selected).
- Nested steps:
- L241:
logger.Warnw("http request completed", fields...)- What: Calls logger.Warnw.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L241:
- L242:
default:- What: Selects a switch case.
- Why: Makes multi-branch control flow explicit and readable.
- How: Runs this case body when the switch value matches (or when default is selected).
- Nested steps:
- L243:
logger.Infow("http request completed", fields...)- What: Calls logger.Infow.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L243:
- L238:
- L186:
- L185:
- L181:
- L180:
newLoggingResponseWriter
What: Constructs a loggingResponseWriter wrapper.
Why: Centralizes the initialization defaults (status = 200) and dependency injection for hijack tracking.
How: Stores the underlying response writer and the optional hijack tracker function.
func newLoggingResponseWriter(w http.ResponseWriter, tracker func() (func(), func(net.Conn) net.Conn)) *loggingResponseWriter {
return &loggingResponseWriter{
ResponseWriter: w,
status: http.StatusOK,
hijackTracker: tracker,
}
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L258:
return &loggingResponseWriter{ ResponseWriter: w, status: http.StatusOK, hijackTracker: tracker, }- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
(*loggingResponseWriter).WriteHeader
What: Captures the status code while delegating header writes.
Why: Status code is needed for both logs and metrics.
How: Stores the status code then calls the underlying WriteHeader.
func (w *loggingResponseWriter) WriteHeader(status int) {
w.status = status
w.ResponseWriter.WriteHeader(status)
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L266:
w.status = status- What: Assigns w.status.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L267:
w.ResponseWriter.WriteHeader(status)- What: Calls w.ResponseWriter.WriteHeader.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
(*loggingResponseWriter).Write
What: Captures bytes written while delegating response writes.
Why: Byte counts are useful for operational logs and for debugging unexpectedly large responses.
How: Ensures a default status exists, calls the underlying Write, and accumulates the byte count.
func (w *loggingResponseWriter) Write(b []byte) (int, error) {
if w.status == 0 {
w.status = http.StatusOK
}
n, err := w.ResponseWriter.Write(b)
w.bytes += n
return n, err
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L271:
if w.status == 0 { w.status = http.StatusOK }- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L272:
w.status = http.StatusOK- What: Assigns w.status.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L272:
- L274:
n, err := w.ResponseWriter.Write(b)- What: Defines n, err.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L275:
w.bytes += n- What: Assigns w.bytes.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L276:
return n, err- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
(*loggingResponseWriter).Flush
What: Implements http.Flusher when the underlying writer supports it.
Why: Some responses (streaming/SSE) require flush support; the wrapper should not break that behaviour.
How: Type-asserts to http.Flusher and delegates.
func (w *loggingResponseWriter) Flush() {
if flusher, ok := w.ResponseWriter.(http.Flusher); ok {
flusher.Flush()
}
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L280:
if flusher, ok := w.ResponseWriter.(http.Flusher); ok { flusher.Flush() }- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L280:
flusher, ok := w.ResponseWriter.(http.Flusher)- What: Defines flusher, ok.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L281:
flusher.Flush()- What: Calls flusher.Flush.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L280:
(*loggingResponseWriter).Hijack
What: Implements http.Hijacker and integrates upgraded-connection tracking.
Why: Websockets and other upgrades require hijacking; the gateway also wants to track upgraded connection lifetime for metrics and limits.
How: Delegates to the underlying http.Hijacker, then (optionally) calls the configured hijack tracker once to obtain an on-close callback and a connection wrapper; wraps the returned net.Conn accordingly.
Notes: If the underlying writer does not support hijacking, returns an explicit error.
func (w *loggingResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
hijacker, ok := w.ResponseWriter.(http.Hijacker)
if !ok {
return nil, nil, errors.New("hijacker not supported")
}
conn, rw, err := hijacker.Hijack()
if err != nil {
return nil, nil, err
}
if w.hijackTracker != nil {
var closer func()
var wrapper func(net.Conn) net.Conn
w.hijackOnce.Do(func() {
closer, wrapper = w.hijackTracker()
})
if wrapper != nil {
conn = wrapper(conn)
}
if closer != nil {
conn = &trackingConn{Conn: conn, onClose: closer}
}
}
return conn, rw, nil
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L286:
hijacker, ok := w.ResponseWriter.(http.Hijacker)- What: Defines hijacker, ok.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L287:
if !ok { return nil, nil, errors.New("hijacker not supported") }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L288:
return nil, nil, errors.New("hijacker not supported")- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L288:
- L290:
conn, rw, err := hijacker.Hijack()- What: Defines conn, rw, err.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L291:
if err != nil { return nil, nil, err }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L292:
return nil, nil, err- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L292:
- L295:
if w.hijackTracker != nil { var closer func() var wrapper func(net.Conn) net.Conn w.hijackOnce.Do(func() { closer, wrapper = w.hijackTracke…- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L296:
var closer func()- What: Declares local names.
- Why: Introduces variables or types used later in the function.
- How: Executes a Go declaration statement inside the function body.
- L297:
var wrapper func(net.Conn) net.Conn- What: Declares local names.
- Why: Introduces variables or types used later in the function.
- How: Executes a Go declaration statement inside the function body.
- L298:
w.hijackOnce.Do(func() { closer, wrapper = w.hijackTracker() })- What: Calls w.hijackOnce.Do.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- Nested steps:
- L298:
func() { closer, wrapper = w.hijackTracker() }- What: Defines an inline function (closure).
- Why: Encapsulates callback logic and may capture variables from the surrounding scope.
- How: Declares a
funcliteral and uses it as a value (for example, as an HTTP handler or callback). - Nested steps:
- L299:
closer, wrapper = w.hijackTracker()- What: Assigns closer, wrapper.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L299:
- L298:
- L301:
if wrapper != nil { conn = wrapper(conn) }- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L302:
conn = wrapper(conn)- What: Assigns conn.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L302:
- L304:
if closer != nil { conn = &trackingConn{Conn: conn, onClose: closer} }- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L305:
conn = &trackingConn{Conn: conn, onClose: closer}- What: Assigns conn.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L305:
- L296:
- L309:
return conn, rw, nil- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
(*loggingResponseWriter).Push
What: Implements http.Pusher when the underlying writer supports it.
Why: Preserves HTTP/2 server push support when enabled by downstream handlers (rare for the gateway, but safe to support).
How: Delegates to http.Pusher.Push or returns http.ErrNotSupported.
func (w *loggingResponseWriter) Push(target string, opts *http.PushOptions) error {
if pusher, ok := w.ResponseWriter.(http.Pusher); ok {
return pusher.Push(target, opts)
}
return http.ErrNotSupported
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L313:
if pusher, ok := w.ResponseWriter.(http.Pusher); ok { return pusher.Push(target, opts) }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L313:
pusher, ok := w.ResponseWriter.(http.Pusher)- What: Defines pusher, ok.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L314:
return pusher.Push(target, opts)- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L313:
- L316:
return http.ErrNotSupported- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
(*trackingConn).Close
What: Closes the underlying connection and runs the on-close callback once.
Why: Upgraded connections can outlive the HTTP request; metrics/accounting must decrement when the connection actually closes.
How: Delegates to Conn.Close, then uses sync.Once to invoke onClose a single time.
func (c *trackingConn) Close() error {
err := c.Conn.Close()
c.once.Do(func() {
if c.onClose != nil {
c.onClose()
}
})
return err
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L327:
err := c.Conn.Close()- What: Defines err.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L328:
c.once.Do(func() { if c.onClose != nil { c.onClose() } })- What: Calls c.once.Do.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- Nested steps:
- L328:
func() { if c.onClose != nil { c.onClose() } }- What: Defines an inline function (closure).
- Why: Encapsulates callback logic and may capture variables from the surrounding scope.
- How: Declares a
funcliteral and uses it as a value (for example, as an HTTP handler or callback). - Nested steps:
- L329:
if c.onClose != nil { c.onClose() }- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L330:
c.onClose()- What: Calls c.onClose.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L330:
- L329:
- L328:
- L333:
return err- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
(*trackingConn).Read
What: Reads from the underlying connection with optional deadline enforcement.
Why: Deadline enforcement can protect the gateway from idle upgraded connections when configured.
How: If timeout is non-zero, sets a read deadline before calling Conn.Read.
Notes: This wrapper is currently constructed with a zero timeout in this package; provide a custom wrapper via HijackedFunc for idle enforcement.
func (c *trackingConn) Read(b []byte) (int, error) {
if c.timeout > 0 {
_ = c.Conn.SetReadDeadline(time.Now().Add(c.timeout))
}
return c.Conn.Read(b)
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L337:
if c.timeout > 0 { _ = c.Conn.SetReadDeadline(time.Now().Add(c.timeout)) }- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L338:
_ = c.Conn.SetReadDeadline(time.Now().Add(c.timeout))- What: Assigns _.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L338:
- L340:
return c.Conn.Read(b)- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
(*trackingConn).Write
What: Writes to the underlying connection with optional deadline enforcement.
Why: Mirrors the read path for symmetry and supports enforcing write-side timeouts when configured.
How: If timeout is non-zero, sets a write deadline before calling Conn.Write.
Notes: This wrapper is currently constructed with a zero timeout in this package; provide a custom wrapper via HijackedFunc for idle enforcement.
func (c *trackingConn) Write(b []byte) (int, error) {
if c.timeout > 0 {
_ = c.Conn.SetWriteDeadline(time.Now().Add(c.timeout))
}
return c.Conn.Write(b)
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L344:
if c.timeout > 0 { _ = c.Conn.SetWriteDeadline(time.Now().Add(c.timeout)) }- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L345:
_ = c.Conn.SetWriteDeadline(time.Now().Add(c.timeout))- What: Assigns _.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L345:
- L347:
return c.Conn.Write(b)- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).