pkg/gateway/server/protocol_metrics.go
Source
- Package:
server - File:
pkg/gateway/server/protocol_metrics.go - GitHub: https://github.com/theroutercompany/api_router/blob/main/pkg/gateway/server/protocol_metrics.go
Overview
What:
Defines Prometheus metrics that describe gateway traffic by "protocol" and "product".
In this context:
- protocol is a best-effort classification of the inbound request (http, websocket, sse, grpc)
- product is a high-level routing bucket (e.g. trade/task) derived from headers or URL prefixes
Why:
The gateway proxies multiple kinds of traffic (regular HTTP, streaming, upgrades). For operations and capacity planning, it is useful to answer questions like:
- Are we seeing more websocket connections than expected?
- Is one product experiencing higher error rates or latency?
- Are we accumulating long-lived SSE/grpc requests?
These metrics are designed to be emitted from middleware without coupling the middleware to Prometheus directly.
How:
newProtocolMetrics() constructs four collectors and registers them with the gateway registry:
gateway_protocol_requests_totalcounter labelled by protocol, product, outcomegateway_protocol_inflightgauge labelled by protocol, productgateway_protocol_request_duration_secondshistogram labelled by protocol, productgateway_protocol_active_connectionsgauge labelled by protocol, product (for long-lived connections)
The middleware calls:
track(r)at request start to increment inflight (and sometimes active connections)- the returned closure at request end to record outcome, duration, and decrement gauges
hijacked(r)to handle websocket lifetime tracking for hijacked connections
Notes:
Protocol classification is heuristic by design. It uses standard headers and request metadata, but it is not a perfect protocol detector. Prefer stable "product" labels for long-term dashboards.
Contents
Imports
import block 1
import (
"net/http"
"strings"
"time"
"github.com/prometheus/client_golang/prometheus"
gatewaymetrics "github.com/theroutercompany/api_router/pkg/gateway/metrics"
)
Types
type block 1
type protocolMetrics struct {
requests *prometheus.CounterVec
inflight *prometheus.GaugeVec
duration *prometheus.HistogramVec
connections *prometheus.GaugeVec
}
protocolMetrics
What: Holds the Prometheus collectors used for protocol metrics.
Why: Groups related collectors so middleware can be wired with a single value rather than many globals.
How: Constructed by newProtocolMetrics and used by server middleware to emit metrics.
Notes: All fields are pointers to allow partial construction and nil-safety in the tracking functions.
Functions and Methods
newProtocolMetrics
What: Constructs and registers the protocol/product collectors.
Why: Centralizes metric names, help strings, label sets, and registration in one place.
How: Creates counters/gauges/histograms, registers them via reg.Register, and returns a protocolMetrics wrapper (or nil if the registry is nil).
Notes: Metric names are currently hard-coded (not namespaced) to keep dashboards stable.
func newProtocolMetrics(reg *gatewaymetrics.Registry) *protocolMetrics {
if reg == nil {
return nil
}
requests := prometheus.NewCounterVec(prometheus.CounterOpts{
Name: "gateway_protocol_requests_total",
Help: "Count of proxied requests labelled by protocol, product, and outcome.",
}, []string{"protocol", "product", "outcome"})
inflight := prometheus.NewGaugeVec(prometheus.GaugeOpts{
Name: "gateway_protocol_inflight",
Help: "Current number of in-flight proxied requests by protocol and product.",
}, []string{"protocol", "product"})
duration := prometheus.NewHistogramVec(prometheus.HistogramOpts{
Name: "gateway_protocol_request_duration_seconds",
Help: "Upstream duration for proxied requests segmented by protocol and product.",
Buckets: prometheus.DefBuckets,
}, []string{"protocol", "product"})
connections := prometheus.NewGaugeVec(prometheus.GaugeOpts{
Name: "gateway_protocol_active_connections",
Help: "Current number of active upgraded connections (e.g., websockets) by protocol and product.",
}, []string{"protocol", "product"})
reg.Register(requests)
reg.Register(inflight)
reg.Register(duration)
reg.Register(connections)
return &protocolMetrics{
requests: requests,
inflight: inflight,
duration: duration,
connections: connections,
}
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L21:
if reg == nil { return nil }- 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:
- L22:
return 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).
- L22:
- L25:
requests := prometheus.NewCounterVec(prometheus.CounterOpts{ Name: "gateway_protocol_requests_total", Help: "Count of proxied requests labe…- What: Defines requests.
- 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.
- L30:
inflight := prometheus.NewGaugeVec(prometheus.GaugeOpts{ Name: "gateway_protocol_inflight", Help: "Current number of in-flight proxied requ…- What: Defines inflight.
- 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.
- L35:
duration := prometheus.NewHistogramVec(prometheus.HistogramOpts{ Name: "gateway_protocol_request_duration_seconds", Help: "Upstream duratio…- 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.
- L41:
connections := prometheus.NewGaugeVec(prometheus.GaugeOpts{ Name: "gateway_protocol_active_connections", Help: "Current number of active up…- What: Defines connections.
- 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.
- L46:
reg.Register(requests)- What: Calls reg.Register.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L47:
reg.Register(inflight)- What: Calls reg.Register.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L48:
reg.Register(duration)- What: Calls reg.Register.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L49:
reg.Register(connections)- What: Calls reg.Register.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L51:
return &protocolMetrics{ requests: requests, inflight: inflight, duration: duration, connections: connections, }- 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).
(*protocolMetrics).track
What: Produces a per-request tracking function for status/outcome/duration and inflight gauges.
Why: Keeps request lifecycle accounting together and lets middleware defer metric updates to the end of request handling.
How: Classifies protocol/product, increments inflight gauge (and optionally active connection gauge for long-lived protocols), then returns a closure that records outcome/duration and decrements gauges.
Notes: Non-positive status values are treated as 200 OK for metric labeling.
func (m *protocolMetrics) track(r *http.Request) func(status int, elapsed time.Duration) {
if m == nil || r == nil {
return func(int, time.Duration) {}
}
protocol := classifyProtocol(r)
product := productFromRequest(r)
if m.inflight != nil {
m.inflight.WithLabelValues(protocol, product).Inc()
}
trackConnection := m.connections != nil && trackConnectionForProtocol(protocol)
if trackConnection {
m.connections.WithLabelValues(protocol, product).Inc()
}
return func(status int, elapsed time.Duration) {
if status <= 0 {
status = http.StatusOK
}
outcome := "success"
if status >= 400 {
outcome = "error"
}
if m.requests != nil {
m.requests.WithLabelValues(protocol, product, outcome).Inc()
}
if m.duration != nil {
m.duration.WithLabelValues(protocol, product).Observe(elapsed.Seconds())
}
if m.inflight != nil {
m.inflight.WithLabelValues(protocol, product).Dec()
}
if trackConnection {
m.connections.WithLabelValues(protocol, product).Dec()
}
}
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L60:
if m == nil || r == nil { return func(int, time.Duration) {} }- 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:
- L61:
return func(int, time.Duration) {}- 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(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).
- L61:
- L61:
- L64:
protocol := classifyProtocol(r)- What: Defines protocol.
- 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.
- L65:
product := productFromRequest(r)- What: Defines product.
- 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.
- L67:
if m.inflight != nil { m.inflight.WithLabelValues(protocol, product).Inc() }- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L68:
m.inflight.WithLabelValues(protocol, product).Inc()- What: Calls m.inflight.WithLabelValues(protocol, product).Inc.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L68:
- L71:
trackConnection := m.connections != nil && trackConnectionForProtocol(protocol)- What: Defines trackConnection.
- 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.
- L72:
if trackConnection { m.connections.WithLabelValues(protocol, product).Inc() }- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L73:
m.connections.WithLabelValues(protocol, product).Inc()- What: Calls m.connections.WithLabelValues(protocol, product).Inc.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L73:
- L76:
return func(status int, elapsed time.Duration) { if status <= 0 { status = http.StatusOK } outcome := "success" if status >= 400 { outcome …- 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(status int, elapsed time.Duration) { if status <= 0 { status = http.StatusOK } outcome := "success" if status >= 400 { outcome = "erro…- 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:
- L77:
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:
- L78:
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.
- L78:
- L81:
outcome := "success"- What: Defines outcome.
- 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.
- L82:
if status >= 400 { outcome = "error" }- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L83:
outcome = "error"- What: Assigns outcome.
- 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.
- L83:
- L86:
if m.requests != nil { m.requests.WithLabelValues(protocol, product, outcome).Inc() }- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L87:
m.requests.WithLabelValues(protocol, product, outcome).Inc()- What: Calls m.requests.WithLabelValues(protocol, product, outcome).Inc.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L87:
- L89:
if m.duration != nil { m.duration.WithLabelValues(protocol, product).Observe(elapsed.Seconds()) }- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L90:
m.duration.WithLabelValues(protocol, product).Observe(elapsed.Seconds())- What: Calls m.duration.WithLabelValues(protocol, product).Observe.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L90:
- L92:
if m.inflight != nil { m.inflight.WithLabelValues(protocol, product).Dec() }- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L93:
m.inflight.WithLabelValues(protocol, product).Dec()- What: Calls m.inflight.WithLabelValues(protocol, product).Dec.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L93:
- L95:
if trackConnection { m.connections.WithLabelValues(protocol, product).Dec() }- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L96:
m.connections.WithLabelValues(protocol, product).Dec()- What: Calls m.connections.WithLabelValues(protocol, product).Dec.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L96:
- L77:
- L76:
(*protocolMetrics).hijacked
What: Returns a closure that decrements the websocket connection gauge when an upgraded connection closes.
Why: Websocket lifetimes outlive the HTTP handler call, so connection tracking must hook into the net.Conn close path.
How: Increments gateway_protocol_active_connections{protocol=\"websocket\"} and returns a function that decrements it; returns nil when the request is not websocket-classified.
Notes: The middleware is responsible for calling the returned closure exactly once on connection close.
func (m *protocolMetrics) hijacked(r *http.Request) func() {
if m == nil || m.connections == nil || r == nil {
return nil
}
protocol := classifyProtocol(r)
if protocol != "websocket" {
return nil
}
product := productFromRequest(r)
m.connections.WithLabelValues(protocol, product).Inc()
return func() {
m.connections.WithLabelValues(protocol, product).Dec()
}
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L102:
if m == nil || m.connections == nil || r == nil { return nil }- 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:
- L103:
return 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).
- L103:
- L106:
protocol := classifyProtocol(r)- What: Defines protocol.
- 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.
- L107:
if protocol != "websocket" { return nil }- 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:
- L108:
return 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).
- L108:
- L111:
product := productFromRequest(r)- What: Defines product.
- 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.
- L112:
m.connections.WithLabelValues(protocol, product).Inc()- What: Calls m.connections.WithLabelValues(protocol, product).Inc.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L113:
return func() { m.connections.WithLabelValues(protocol, product).Dec() }- 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:
- L113:
func() { m.connections.WithLabelValues(protocol, product).Dec() }- 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:
- L114:
m.connections.WithLabelValues(protocol, product).Dec()- What: Calls m.connections.WithLabelValues(protocol, product).Dec.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L114:
- L113:
trackConnectionForProtocol
What: Indicates whether a protocol should be tracked as an "active connection".
Why: Some protocols represent long-lived sessions where concurrency matters more than request count.
How: Returns true for sse and grpc; false for other protocols.
Notes: Websockets are tracked via (*protocolMetrics).hijacked because their lifetime extends beyond ServeHTTP.
func trackConnectionForProtocol(protocol string) bool {
switch protocol {
case "sse", "grpc":
return true
default:
return false
}
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L119:
switch protocol { case "sse", "grpc": return true default: return false }- 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:
- L120:
case "sse", "grpc":- 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:
- L121:
return true- 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).
- L121:
- L122:
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:
- L123:
return false- 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).
- L123:
- L120:
classifyProtocol
What: Best-effort classification of a request into a protocol label string.
Why: Enables segmenting metrics by traffic shape without requiring every caller to pass explicit protocol metadata.
How: Checks for SSE (Accept/Content-Type), websocket upgrades (Upgrade/Connection), and gRPC (Content-Type, Grpc-Timeout, HTTP/2+TE trailers), otherwise returns http.
Notes: Returns unknown for a nil request.
func classifyProtocol(r *http.Request) string {
if r == nil {
return "unknown"
}
accept := strings.ToLower(r.Header.Get("Accept"))
contentType := strings.ToLower(r.Header.Get("Content-Type"))
if strings.Contains(accept, "text/event-stream") || strings.HasPrefix(contentType, "text/event-stream") {
return "sse"
}
if upgrade := r.Header.Get("Upgrade"); upgrade != "" && strings.EqualFold(upgrade, "websocket") {
return "websocket"
}
if connection := r.Header.Get("Connection"); connection != "" && strings.Contains(strings.ToLower(connection), "upgrade") {
if strings.EqualFold(r.Header.Get("Upgrade"), "websocket") {
return "websocket"
}
}
if strings.HasPrefix(contentType, "application/grpc") {
return "grpc"
}
if r.Header.Get("Grpc-Timeout") != "" {
return "grpc"
}
if r.ProtoMajor == 2 && strings.Contains(strings.ToLower(r.Header.Get("TE")), "trailers") && strings.HasPrefix(contentType, "application/") {
return "grpc"
}
return "http"
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L128:
if r == nil { return "unknown" }- 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:
return "unknown"- 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:
accept := strings.ToLower(r.Header.Get("Accept"))- What: Defines accept.
- 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.
- L133:
contentType := strings.ToLower(r.Header.Get("Content-Type"))- What: Defines contentType.
- 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:
if strings.Contains(accept, "text/event-stream") || strings.HasPrefix(contentType, "text/event-stream") { return "sse" }- 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:
- L136:
return "sse"- 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).
- L136:
- L139:
if upgrade := r.Header.Get("Upgrade"); upgrade != "" && strings.EqualFold(upgrade, "websocket") { return "websocket" }- 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:
- L139:
upgrade := r.Header.Get("Upgrade")- What: Defines upgrade.
- 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.
- L140:
return "websocket"- 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).
- L139:
- L143:
if connection := r.Header.Get("Connection"); connection != "" && strings.Contains(strings.ToLower(connection), "upgrade") { if strings.Equa…- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L143:
connection := r.Header.Get("Connection")- What: Defines connection.
- 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.
- L144:
if strings.EqualFold(r.Header.Get("Upgrade"), "websocket") { return "websocket" }- 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:
- L145:
return "websocket"- 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).
- L145:
- L143:
- L149:
if strings.HasPrefix(contentType, "application/grpc") { return "grpc" }- 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:
- L150:
return "grpc"- 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).
- L150:
- L153:
if r.Header.Get("Grpc-Timeout") != "" { return "grpc" }- 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:
- L154:
return "grpc"- 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).
- L154:
- L157:
if r.ProtoMajor == 2 && strings.Contains(strings.ToLower(r.Header.Get("TE")), "trailers") && strings.HasPrefix(contentType, "application/")…- 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:
- L158:
return "grpc"- 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).
- L158:
- L161:
return "http"- 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).
productFromRequest
What: Derives the "product" label for a request.
Why: Product-level segmentation is useful for SLOs and on-call triage when multiple upstreams share the same gateway.
How: Prefers X-Router-Product header; otherwise falls back to URL path prefix heuristics (/v1/trade, /v1/task); returns unknown if nothing matches.
Notes: Header overrides allow callers (or upstream routers) to provide a more authoritative label than URL parsing.
func productFromRequest(r *http.Request) string {
if r == nil {
return "unknown"
}
if product := r.Header.Get("X-Router-Product"); product != "" {
return product
}
path := r.URL.Path
switch {
case strings.HasPrefix(path, "/v1/trade"):
return "trade"
case strings.HasPrefix(path, "/v1/task"):
return "task"
default:
return "unknown"
}
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L165:
if r == nil { return "unknown" }- 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:
- L166:
return "unknown"- 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).
- L166:
- L169:
if product := r.Header.Get("X-Router-Product"); product != "" { return product }- 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:
- L169:
product := r.Header.Get("X-Router-Product")- What: Defines product.
- 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.
- L170:
return product- 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).
- L169:
- L173:
path := r.URL.Path- What: Defines path.
- 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.
- L174:
switch { case strings.HasPrefix(path, "/v1/trade"): return "trade" case strings.HasPrefix(path, "/v1/task"): return "task" default: return …- 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:
- L175:
case strings.HasPrefix(path, "/v1/trade"):- 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:
- L176:
return "trade"- 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).
- L176:
- L177:
case strings.HasPrefix(path, "/v1/task"):- 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:
- L178:
return "task"- 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).
- L178:
- L179:
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:
- L180:
return "unknown"- 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).
- L180:
- L175: