Skip to main content

pkg/gateway/proxy/reverse_proxy.go

Source

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.Transport with 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

pkg/gateway/proxy/reverse_proxy.go#L6
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

pkg/gateway/proxy/reverse_proxy.go#L195
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

pkg/gateway/proxy/reverse_proxy.go#L25
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

pkg/gateway/proxy/reverse_proxy.go#L34
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

pkg/gateway/proxy/reverse_proxy.go#L102
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.

pkg/gateway/proxy/reverse_proxy.go#L41
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 return statement (possibly returning values).
  • 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 return statement (possibly returning values).
      • 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.
  • 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 func literal 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.
  • 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 func literal 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.
  • 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 return statement (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.

pkg/gateway/proxy/reverse_proxy.go#L88
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 return statement (possibly returning values).
  • 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 return statement (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 func literal 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 return statement (possibly returning values).

(*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.

pkg/gateway/proxy/reverse_proxy.go#L107
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 return statement (possibly returning values).
  • 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 return statement (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.

pkg/gateway/proxy/reverse_proxy.go#L116
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 return statement (possibly returning values).
  • 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 return statement (possibly returning values).
  • 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 return statement (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.

pkg/gateway/proxy/reverse_proxy.go#L127
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.
  • 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.

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.

pkg/gateway/proxy/reverse_proxy.go#L136
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 return statement (possibly returning values).
  • 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 return statement (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.

pkg/gateway/proxy/reverse_proxy.go#L149
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 return statement (possibly returning values).
  • 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 ... range loop.
    • 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.
  • 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.

pkg/gateway/proxy/reverse_proxy.go#L161
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 return statement (possibly returning values).
      • 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 return statement (possibly returning values).
      • 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.
  • 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 return statement (possibly returning values).
      • 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 return statement (possibly returning values).
      • 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.
  • 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 return statement (possibly returning values).

Architecture

Guides

Neighboring source