pkg/gateway/proxy/reverse_proxy.go
Source
- Package:
proxy - File:
pkg/gateway/proxy/reverse_proxy.go - GitHub: https://github.com/theroutercompany/api_router/blob/main/pkg/gateway/proxy/reverse_proxy.go
Overview
What:
Builds reverse proxy handlers with gateway-specific defaults:
- optional TLS/mTLS to upstreams
- gRPC-aware HTTP/2 and h2c transport selection
- product header injection
- Problem+JSON error responses on upstream failures
- streaming-friendly flushing behavior
Why:
Reverse proxying is subtle and easy to get wrong (TLS, HTTP/2, streaming, error formatting). Centralizing proxy construction ensures consistent behavior across trade/task upstreams and keeps the server wiring simple.
How:
New() constructs an httputil.ReverseProxy, then:
- configures a base
http.Transportwith HTTP/2 support - optionally applies TLS settings (CA bundle, client cert/key, insecure skip verify)
- adds an h2c transport for plaintext gRPC upstreams
- selects between base and h2c at request time using
grpcAwareTransport - injects
X-Router-Product(when configured) and sets host/scheme - writes Problem+JSON on upstream errors
Notes: InsecureSkipVerify disables certificate verification and should be avoided in production.
Contents
Imports
import block 1
import (
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"net"
"net/http"
"net/http/httputil"
"net/url"
"os"
"strings"
"time"
"github.com/theroutercompany/api_router/pkg/gateway/problem"
pkglog "github.com/theroutercompany/api_router/pkg/log"
"golang.org/x/net/http2"
)
Variables
var block 1
var errInvalidPEM = errors.New("invalid PEM block")
errInvalidPEM
What: Sentinel error used when a provided CA bundle cannot be parsed as PEM.
Why: Distinguishes "file read failed" from "file contents invalid" and allows wrapping with context.
How: Used by buildTLSConfig when AppendCertsFromPEM fails.
Types
type block 1
type TLSConfig struct {
Enabled bool
InsecureSkipVerify bool
CAFile string
ClientCertFile string
ClientKeyFile string
}
TLSConfig
What: TLS/mTLS settings applied to upstream requests made by the proxy.
Why: Many upstreams use internal PKI or require client certificates; this keeps those settings configurable.
How: Consumed by buildTLSConfig and attached to the base http.Transport when enabled.
type block 2
type Options struct {
Target string
Product string
TLS TLSConfig
}
Options
What: Parameters for building a reverse proxy (target URL, product label, TLS settings).
Why: Keeps proxy construction explicit and makes server wiring readable.
How: Passed into New() and used to configure the proxy director, error handler, and transports.
type block 3
type grpcAwareTransport struct {
base *http.Transport
h2c *http2.Transport
}
grpcAwareTransport
What: A transport wrapper that can switch between a normal HTTP transport and an h2c transport.
Why: Enables gRPC upstream support over cleartext HTTP/2 without affecting normal HTTP traffic.
How: Implements RoundTrip and routes gRPC requests (by Content-Type + scheme) to the h2c transport.
Functions and Methods
New
What: Constructs an http.Handler reverse proxy for a configured upstream target.
Why: Provides a single entry point for creating upstream proxies with consistent gateway defaults.
How: Parses the target URL, builds a reverse proxy, sets transports (TLS + optional h2c), injects product/host headers in the director, and sets a Problem+JSON error handler.
func New(opts Options) (http.Handler, error) {
target, err := url.Parse(opts.Target)
if err != nil {
return nil, err
}
proxy := httputil.NewSingleHostReverseProxy(target)
proxy.FlushInterval = 200 * time.Millisecond
transport := &http.Transport{
Proxy: http.ProxyFromEnvironment,
ForceAttemptHTTP2: true,
MaxIdleConnsPerHost: 10,
}
if opts.TLS.Enabled {
tlsCfg, err := buildTLSConfig(opts.TLS)
if err != nil {
return nil, err
}
transport.TLSClientConfig = tlsCfg
}
h2cTransport := buildH2CTransport(target)
proxy.Transport = &grpcAwareTransport{base: transport, h2c: h2cTransport}
originalDirector := proxy.Director
proxy.Director = func(r *http.Request) {
originalDirector(r)
r.URL.Scheme = target.Scheme
r.URL.Host = target.Host
r.Host = target.Host
if opts.Product != "" {
r.Header.Set("X-Router-Product", opts.Product)
}
}
proxy.ErrorHandler = func(w http.ResponseWriter, r *http.Request, err error) {
pkglog.Shared().Errorw("proxy upstream failure", "error", err, "url", r.URL.String())
detail := fmt.Sprintf("Failed to reach %s service", opts.Product)
problem.Write(w, http.StatusBadGateway, "Upstream Service Unavailable", detail, r.Header.Get("X-Trace-Id"), r.URL.Path)
}
return proxy, nil
}
Walkthrough
Expand walkthrough (28 steps)
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L42:
target, err := url.Parse(opts.Target)- What: Defines target, 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.
- L43:
if err != nil { return 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:
- L44:
return 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).
- L44:
- L47:
proxy := httputil.NewSingleHostReverseProxy(target)- What: Defines proxy.
- 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.
- L48:
proxy.FlushInterval = 200 * time.Millisecond- What: Assigns proxy.FlushInterval.
- 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.
- L50:
transport := &http.Transport{ Proxy: http.ProxyFromEnvironment, ForceAttemptHTTP2: true, MaxIdleConnsPerHost: 10, }- What: Defines transport.
- 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.
- L56:
if opts.TLS.Enabled { tlsCfg, err := buildTLSConfig(opts.TLS) if err != nil { return nil, err } transport.TLSClientConfig = tlsCfg }- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L57:
tlsCfg, err := buildTLSConfig(opts.TLS)- What: Defines tlsCfg, 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.
- L58:
if err != nil { return 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:
- L59:
return 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).
- L59:
- L61:
transport.TLSClientConfig = tlsCfg- What: Assigns transport.TLSClientConfig.
- 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.
- L57:
- L64:
h2cTransport := buildH2CTransport(target)- What: Defines h2cTransport.
- 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:
proxy.Transport = &grpcAwareTransport{base: transport, h2c: h2cTransport}- What: Assigns proxy.Transport.
- 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:
originalDirector := proxy.Director- What: Defines originalDirector.
- 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.
- L68:
proxy.Director = func(r *http.Request) { originalDirector(r) r.URL.Scheme = target.Scheme r.URL.Host = target.Host r.Host = target.Host if …- What: Assigns proxy.Director.
- 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:
- L68:
func(r *http.Request) { originalDirector(r) r.URL.Scheme = target.Scheme r.URL.Host = target.Host r.Host = target.Host if opts.Product != "…- 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:
- L69:
originalDirector(r)- What: Calls originalDirector.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L70:
r.URL.Scheme = target.Scheme- What: Assigns r.URL.Scheme.
- 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.
- L71:
r.URL.Host = target.Host- What: Assigns r.URL.Host.
- 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:
r.Host = target.Host- What: Assigns r.Host.
- 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.
- L74:
if opts.Product != "" { r.Header.Set("X-Router-Product", opts.Product) }- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L75:
r.Header.Set("X-Router-Product", opts.Product)- What: Calls r.Header.Set.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L75:
- L69:
- L68:
- L79:
proxy.ErrorHandler = func(w http.ResponseWriter, r *http.Request, err error) { pkglog.Shared().Errorw("proxy upstream failure", "error", er…- What: Assigns proxy.ErrorHandler.
- 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:
- L79:
func(w http.ResponseWriter, r *http.Request, err error) { pkglog.Shared().Errorw("proxy upstream failure", "error", err, "url", r.URL.Strin…- 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:
- L80:
pkglog.Shared().Errorw("proxy upstream failure", "error", err, "url", r.URL.String())- What: Calls pkglog.Shared().Errorw.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L81:
detail := fmt.Sprintf("Failed to reach %s service", opts.Product)- What: Defines detail.
- 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:
problem.Write(w, http.StatusBadGateway, "Upstream Service Unavailable", detail, r.Header.Get("X-Trace-Id"), r.URL.Path)- What: Calls problem.Write.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L80:
- L79:
- L85:
return proxy, 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).
buildH2CTransport
What: Builds an http2.Transport configured for h2c (HTTP/2 over cleartext TCP).
Why: Some upstreams (notably gRPC services) can speak HTTP/2 without TLS when running behind internal networks or sidecars.
How: Returns nil unless the target scheme is http, otherwise configures AllowHTTP and a DialTLS function that performs a plain dial.
func buildH2CTransport(target *url.URL) *http2.Transport {
if target == nil || target.Scheme != "http" {
return nil
}
return &http2.Transport{
AllowHTTP: true,
DialTLS: func(network, addr string, _ *tls.Config) (net.Conn, error) {
var d net.Dialer
return d.Dial(network, addr)
},
}
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L89:
if target == nil || target.Scheme != "http" { 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:
- L90:
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).
- L90:
- L93:
return &http2.Transport{ AllowHTTP: true, DialTLS: func(network, addr string, _ *tls.Config) (net.Conn, error) { var d net.Dialer return d.…- 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(network, addr string, _ *tls.Config) (net.Conn, error) { var d net.Dialer return d.Dial(network, addr) }- 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:
var d net.Dialer- What: Declares local names.
- Why: Introduces variables or types used later in the function.
- How: Executes a Go declaration statement inside the function body.
- L97:
return d.Dial(network, addr)- 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).
- L96:
- L95:
(*grpcAwareTransport).RoundTrip
What: Performs the request using either the base transport or the h2c transport.
Why: gRPC-over-h2c requires HTTP/2 transport semantics even though the upstream scheme is http.
How: If shouldUseH2C is true, clones and sanitizes the request then calls h2c.RoundTrip; otherwise calls base.RoundTrip.
func (t *grpcAwareTransport) RoundTrip(req *http.Request) (*http.Response, error) {
if t.shouldUseH2C(req) {
h2cReq := cloneRequest(req)
sanitizeH2CRequest(h2cReq)
return t.h2c.RoundTrip(h2cReq)
}
return t.base.RoundTrip(req)
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L108:
if t.shouldUseH2C(req) { h2cReq := cloneRequest(req) sanitizeH2CRequest(h2cReq) return t.h2c.RoundTrip(h2cReq) }- 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:
- L109:
h2cReq := cloneRequest(req)- What: Defines h2cReq.
- 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.
- L110:
sanitizeH2CRequest(h2cReq)- What: Calls sanitizeH2CRequest.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L111:
return t.h2c.RoundTrip(h2cReq)- 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).
- L109:
- L113:
return t.base.RoundTrip(req)- 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).
(*grpcAwareTransport).shouldUseH2C
What: Determines whether a request should be sent over the h2c transport.
Why: Only plaintext gRPC requests need the special h2c path; normal HTTP should use the base transport.
How: Requires a non-nil h2c transport, an http URL scheme, and a Content-Type that contains application/grpc.
func (t *grpcAwareTransport) shouldUseH2C(req *http.Request) bool {
if t.h2c == nil || req == nil || req.URL == nil {
return false
}
if req.URL.Scheme != "http" {
return false
}
ct := strings.ToLower(req.Header.Get("Content-Type"))
return strings.Contains(ct, "application/grpc")
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L117:
if t.h2c == nil || req == nil || req.URL == nil { return false }- 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:
- L118:
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).
- L118:
- L120:
if req.URL.Scheme != "http" { return false }- 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:
- L121:
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).
- L121:
- L123:
ct := strings.ToLower(req.Header.Get("Content-Type"))- What: Defines ct.
- 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.
- L124:
return strings.Contains(ct, "application/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).
(*grpcAwareTransport).CloseIdleConnections
What: Closes idle connections on both the base transport and the h2c transport.
Why: Prevents leaked keep-alives in long-running processes and during reloads.
How: Delegates to CloseIdleConnections on each transport when non-nil.
func (t *grpcAwareTransport) CloseIdleConnections() {
if t.base != nil {
t.base.CloseIdleConnections()
}
if t.h2c != nil {
t.h2c.CloseIdleConnections()
}
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L128:
if t.base != nil { t.base.CloseIdleConnections() }- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L129:
t.base.CloseIdleConnections()- What: Calls t.base.CloseIdleConnections.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L129:
- L131:
if t.h2c != nil { t.h2c.CloseIdleConnections() }- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L132:
t.h2c.CloseIdleConnections()- What: Calls t.h2c.CloseIdleConnections.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L132:
cloneRequest
What: Clones an incoming request into a form suitable for client-side RoundTrip.
Why: The h2c transport requires a request shaped for http.Client semantics; RequestURI must be empty and headers/body must be preserved.
How: Uses req.Clone(ctx) then reattaches body-related fields and clears RequestURI.
func cloneRequest(req *http.Request) *http.Request {
if req == nil {
return nil
}
clone := req.Clone(req.Context())
clone.Body = req.Body
clone.GetBody = req.GetBody
clone.ContentLength = req.ContentLength
clone.Host = req.Host
clone.RequestURI = ""
return clone
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L137:
if req == 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:
- L138:
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).
- L138:
- L140:
clone := req.Clone(req.Context())- What: Defines clone.
- 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.
- L141:
clone.Body = req.Body- What: Assigns clone.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.
- L142:
clone.GetBody = req.GetBody- What: Assigns clone.GetBody.
- 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.
- L143:
clone.ContentLength = req.ContentLength- What: Assigns clone.ContentLength.
- 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:
clone.Host = req.Host- What: Assigns clone.Host.
- 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.
- L145:
clone.RequestURI = ""- What: Assigns clone.RequestURI.
- 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.
- L146:
return clone- 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).
sanitizeH2CRequest
What: Removes hop-by-hop headers and forces the request protocol fields to reflect HTTP/2.
Why: Hop-by-hop headers are not valid for HTTP/2 and can cause protocol errors when forwarding gRPC over h2c.
How: Deletes a fixed list of hop-by-hop headers and sets Proto, ProtoMajor, and ProtoMinor to HTTP/2 values.
func sanitizeH2CRequest(req *http.Request) {
if req == nil {
return
}
for _, key := range []string{"Connection", "Proxy-Connection", "Upgrade", "Keep-Alive", "Transfer-Encoding"} {
req.Header.Del(key)
}
req.Proto = "HTTP/2.0"
req.ProtoMajor = 2
req.ProtoMinor = 0
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L150:
if req == nil { 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:
- L151:
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).
- L151:
- L153:
for _, key := range []string{"Connection", "Proxy-Connection", "Upgrade", "Keep-Alive", "Transfer-Encoding"} { req.Header.Del(key) }- What: Iterates over a collection.
- Why: Processes multiple elements with the same logic.
- How: Executes a
for ... rangeloop. - Nested steps:
- L154:
req.Header.Del(key)- What: Calls req.Header.Del.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L154:
- L156:
req.Proto = "HTTP/2.0"- What: Assigns req.Proto.
- 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:
req.ProtoMajor = 2- What: Assigns req.ProtoMajor.
- 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.
- L158:
req.ProtoMinor = 0- What: Assigns req.ProtoMinor.
- 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.
buildTLSConfig
What: Builds a tls.Config for upstream requests based on the proxy TLSConfig.
Why: Upstreams may require custom CA roots and/or mTLS client certificates.
How: Sets TLS 1.2+ defaults, optionally loads a CA bundle into RootCAs, optionally loads a client key pair, and errors for partial client cert config.
func buildTLSConfig(cfg TLSConfig) (*tls.Config, error) {
tlsCfg := &tls.Config{
MinVersion: tls.VersionTLS12,
InsecureSkipVerify: cfg.InsecureSkipVerify,
}
if cfg.CAFile != "" {
data, err := os.ReadFile(cfg.CAFile)
if err != nil {
return nil, fmt.Errorf("read CA file %q: %w", cfg.CAFile, err)
}
pool := x509.NewCertPool()
if !pool.AppendCertsFromPEM(data) {
return nil, fmt.Errorf("parse CA bundle %q: %w", cfg.CAFile, errInvalidPEM)
}
tlsCfg.RootCAs = pool
}
if cfg.ClientCertFile != "" || cfg.ClientKeyFile != "" {
if cfg.ClientCertFile == "" || cfg.ClientKeyFile == "" {
return nil, errors.New("client certificate and key must both be provided")
}
cert, err := tls.LoadX509KeyPair(cfg.ClientCertFile, cfg.ClientKeyFile)
if err != nil {
return nil, fmt.Errorf("load client key pair: %w", err)
}
tlsCfg.Certificates = []tls.Certificate{cert}
}
return tlsCfg, nil
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L162:
tlsCfg := &tls.Config{ MinVersion: tls.VersionTLS12, InsecureSkipVerify: cfg.InsecureSkipVerify, }- What: Defines tlsCfg.
- 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.
- L167:
if cfg.CAFile != "" { data, err := os.ReadFile(cfg.CAFile) if err != nil { return nil, fmt.Errorf("read CA file %q: %w", cfg.CAFile, err) }…- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L168:
data, err := os.ReadFile(cfg.CAFile)- What: Defines data, 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.
- L169:
if err != nil { return nil, fmt.Errorf("read CA file %q: %w", cfg.CAFile, 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:
- L170:
return nil, fmt.Errorf("read CA file %q: %w", cfg.CAFile, 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).
- L170:
- L173:
pool := x509.NewCertPool()- What: Defines pool.
- 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:
if !pool.AppendCertsFromPEM(data) { return nil, fmt.Errorf("parse CA bundle %q: %w", cfg.CAFile, errInvalidPEM) }- 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:
- L175:
return nil, fmt.Errorf("parse CA bundle %q: %w", cfg.CAFile, errInvalidPEM)- 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).
- L175:
- L177:
tlsCfg.RootCAs = pool- What: Assigns tlsCfg.RootCAs.
- 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.
- L168:
- L180:
if cfg.ClientCertFile != "" || cfg.ClientKeyFile != "" { if cfg.ClientCertFile == "" || cfg.ClientKeyFile == "" { return nil, errors.New("c…- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L181:
if cfg.ClientCertFile == "" || cfg.ClientKeyFile == "" { return nil, errors.New("client certificate and key must both be provided") }- 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 nil, errors.New("client certificate and key must both be provided")- 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:
cert, err := tls.LoadX509KeyPair(cfg.ClientCertFile, cfg.ClientKeyFile)- What: Defines cert, 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.
- L186:
if err != nil { return nil, fmt.Errorf("load client key pair: %w", 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:
- L187:
return nil, fmt.Errorf("load client key pair: %w", 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).
- L187:
- L189:
tlsCfg.Certificates = []tls.Certificate{cert}- What: Assigns tlsCfg.Certificates.
- 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.
- L181:
- L192:
return tlsCfg, 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).