From 9ad2bb07cb735731a6fb1f8f0c9a577de8a179a0 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Mon, 29 Dec 2025 15:19:39 +0100 Subject: [PATCH] Apply TLS settings to backend connections This is limited to the TLS version, but this is probably the most common use case for this setting. --- backend.go | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++ xqd_request.go | 8 ++++--- 2 files changed, 66 insertions(+), 3 deletions(-) diff --git a/backend.go b/backend.go index 1c05fc1..b32ae4f 100644 --- a/backend.go +++ b/backend.go @@ -1,9 +1,12 @@ package fastlike import ( + "crypto/tls" "fmt" + "net" "net/http" "net/url" + "time" ) // Backend represents a complete backend configuration with all introspectable properties @@ -85,6 +88,64 @@ func defaultBackend(name string) http.Handler { }) } +// fastlyTLSVersionToGo converts Fastly TLS version constants to Go's tls package constants. +func fastlyTLSVersionToGo(v uint32) uint16 { + switch v { + case TLSv10: + return tls.VersionTLS10 + case TLSv11: + return tls.VersionTLS11 + case TLSv12: + return tls.VersionTLS12 + case TLSv13: + return tls.VersionTLS13 + default: + return 0 + } +} + +// CreateTransport creates an http.Transport configured according to the backend's settings. +func (b *Backend) CreateTransport() *http.Transport { + transport := &http.Transport{ + Proxy: http.ProxyFromEnvironment, + DialContext: (&net.Dialer{ + Timeout: 30 * time.Second, + KeepAlive: 30 * time.Second, + }).DialContext, + ForceAttemptHTTP2: true, + MaxIdleConns: 100, + IdleConnTimeout: 90 * time.Second, + TLSHandshakeTimeout: 10 * time.Second, + ExpectContinueTimeout: 1 * time.Second, + } + + if b.ConnectTimeoutMs > 0 { + transport.DialContext = (&net.Dialer{ + Timeout: time.Duration(b.ConnectTimeoutMs) * time.Millisecond, + KeepAlive: 30 * time.Second, + }).DialContext + } + + if b.UseSSL || b.SSLMinVersion > 0 || b.SSLMaxVersion > 0 { + tlsConfig := &tls.Config{} + + if b.SSLMinVersion > 0 { + tlsConfig.MinVersion = fastlyTLSVersionToGo(b.SSLMinVersion) + } + if b.SSLMaxVersion > 0 { + tlsConfig.MaxVersion = fastlyTLSVersionToGo(b.SSLMaxVersion) + } + + transport.TLSClientConfig = tlsConfig + } + + if b.FirstByteTimeoutMs > 0 { + transport.ResponseHeaderTimeout = time.Duration(b.FirstByteTimeoutMs) * time.Millisecond + } + + return transport +} + // DynamicBackendConfig represents the configuration structure passed from guest code // when registering a dynamic backend at runtime. // This struct maps directly to the dynamic_backend_config struct in the XQD ABI, diff --git a/xqd_request.go b/xqd_request.go index 2582813..fd3c254 100644 --- a/xqd_request.go +++ b/xqd_request.go @@ -1692,8 +1692,10 @@ func (i *Instance) xqd_req_register_dynamic_backend(name_prefix_addr int32, name backend.TCPKeepaliveProbes = config.TCPKeepaliveProbes } + // Create a transport with TLS and timeout settings applied + transport := backend.CreateTransport() + // Create a simple http.Handler for the backend - // For local testing, we use http.DefaultTransport to make actual HTTP requests backend.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Update the request URL to point to the backend r.URL.Scheme = backend.URL.Scheme @@ -1705,8 +1707,8 @@ func (i *Instance) xqd_req_register_dynamic_backend(name_prefix_addr int32, name r.Header.Set("Host", backend.OverrideHost) } - // Use http.DefaultTransport to make the actual request - resp, err := http.DefaultTransport.RoundTrip(r) + // Use the configured transport to make the actual request + resp, err := transport.RoundTrip(r) if err != nil { w.WriteHeader(http.StatusBadGateway) _, _ = fmt.Fprintf(w, "Backend request failed: %v", err)